commit bafe3872a51c4491f56f52efb71917bf69f1b8ad Author: Ibnu hamdani Date: Tue Jul 5 15:35:16 2022 +0700 first commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8001ec4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.env b/.env new file mode 100644 index 0000000..495d264 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +PORT=3000 +CHOKIDAR_USEPOLLING=true +GENERATE_SOURCEMAP=false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d4a143f --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +/node_modules +/src/views/Report/ControlMonitoringv1.rar +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.idea +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json +yarn.lock + +src/components/MapToolbar/MapToolbar_backup2.js \ No newline at end of file diff --git a/API_GeoHR_docs.md b/API_GeoHR_docs.md new file mode 100644 index 0000000..6ea455b --- /dev/null +++ b/API_GeoHR_docs.md @@ -0,0 +1,410 @@ + +## API LOGIN + +Payload Login Sales +``` +[POST] https://oslog.id/geohr-api/sales/login +{ + "username": null, //string + "password": null //string +} +``` + + +## API UPLOAD IMAGE + +Payload Upload Image +``` +[POST] https://oslog.id/geohr-api/image/{category}/upload +//notes: gunakan form-data +Payload di form-data: +ref_id: "" //diambil dari id berdasarkan categorynya +files: tipe datanya file (ini file image1) +files: tipe datanya file (ini file image2) +files: tipe datanya file (ini file image3) +files: tipe datanya file (ini file image4, dst) +``` + + +## API OFFICE + +Search +``` +[POST] https://oslog.id/geohr-api/office/monitoring +Payload +{ + "paging": {"start": 0, "length": 10}, + "columns": [ + {"name":"name", "logic_operator": "like", "value": "", "operator": "and"} + ], + "orders": {"columns": ["name"], "ascending": true} +} + +Response +{ + "code": 200, + "data": { + "type": "FeatureCollection", + "features": [ + { + "id": "m_office.37", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "m_office.37", + "company": 5, + "name": "goro", + "employes": "goro", + "address": "goro", + "description": "goro", + "buffer_radius": 100, + "lat": -6.178889275035602, + "lon": 106.83972036828338, + "geom": "POLYGON((93.8211107249645 106.839720368283,64.5317888436193 36.1290422496288,-6.17888927503533 6.83972036828345,-76.8895673936901 36.1290422496285,-106.178889275036 106.839720368283,-76.8895673936905 177.550398486938,-6.17888927503596 206.839720368283,64.5317888436189 177.550398486939,93.8211107249645 106.839720368283))", + "created_by": "@system", + "created_date": "2021-06-15T13:54:24.157516Z", + "modified_by": "@system", + "modified_date": "2021-06-15T13:54:24.157516Z", + "join": { + "": "" + } + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.83972036828338, + -6.178889275035602 + ] + } + }, + { + "id": "m_office.36", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "m_office.36", + "company": 1, + "name": "tori", + "employes": "tori", + "address": "toritori", + "description": "toriaja", + "buffer_radius": 100, + "lat": -5.992300230787033, + "lon": 106.0353342769051, + "geom": "POLYGON((94.0076997692131 106.035334276905,64.7183778878679 35.3246561582504,-5.99230023078677 6.03533427690506,-76.7029783494415 35.3246561582501,-105.992300230787 106.035334276905,-76.702978349442 176.74601239556,-5.99230023078739 206.035334276905,64.7183778878674 176.74601239556,94.0076997692131 106.035334276905))", + "created_by": "@system", + "created_date": "2021-06-15T13:20:33.590436Z", + "modified_by": "@system", + "modified_date": "2021-06-15T13:52:05.91823Z", + "join": { + "": "" + } + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.0353342769051, + -5.992300230787033 + ] + } + } + ] + }, + "executionTime": "2.122619ms", + "message": "OK", + "totalRecord": 2 +} +``` + + +Payload Add/Edit Office +``` +[POST] https://oslog.id/geohr-api/office/add +[PUT] https://oslog.id/geohr-api/office/{id:[0-9]+}/edit +{ + "company": null, //int + "name": null, //string + "employes": null, //string + "address": null, //string + "description": null, //string + "geom": null //string +} +``` + + +## API CUSTOMER + +Monitoring +``` +[POST] https://oslog.id/geohr-api/customer/monitoring +{ + "paging": {"start": 0, "length": 10}, + "columns": [ + {"name":"name", "logic_operator": "like", "value": "", "operator": "and"} + ], + "orders": {"columns": ["name"], "ascending": true} +} +``` + + + +## API SALES + +Search +``` +[POST] https://oslog.id/geohr-api/sales/monitoring +Payload +{ + "paging": {"start": 0, "length": 10}, + "columns": [ + {"name":"name", "logic_operator": "like", "value": "", "operator": "and", "table_name": "m_group_sales"} + ], + "joins": [ + {"name": "group_sales", "column_results": ["name", "description"]} + ], + "orders": {"columns": ["name"], "ascending": true} +} + +Response +{ + "code": 200, + "data": { + "type": "FeatureCollection", + "features": [ + { + "id": "m_sales.1", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "m_sales.1", + "group_sales": 7, + "username": "ibnuh", + "password": "7f2ababa423061c509f4923dd04b6cf1", + "session_login": "5459aff0-82c9-40bb-a34e-e6278fd9c173", + "name": "ibnu hamdani", + "phone_number": "083823134569", + "email": "ibnuhamdani234@gmail.com", + "address": "jl tenjo bogor", + "achieve": null, + "type_sales": null, + "lat": -6.2622531, + "lon": 106.7881649, + "created_by": "admin", + "created_date": "2021-06-15T03:35:32.852925Z", + "modified_by": "admin", + "modified_date": "2021-06-15T03:35:32.852925Z", + "join": { + "group_sales_description": "Group Sales Wilayah Kebayoran baru", + "group_sales_name": "Kebayoran Baru" + } + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.7881649, + -6.2622531 + ] + } + } + ] + }, + "executionTime": "1.916764ms", + "message": "OK", + "totalRecord": 1 +} +``` + +Payload Add/Edit Sales +``` +[POST] https://oslog.id/geohr-api/sales/add +[PUT] https://oslog.id/geohr-api/sales/{id:[0-9]+}/edit +{ + "group_sales": null, //int + "username": null, //string + "password": null, //string + "name": null, //string + "phone_number": null, //string + "email": null, //string + "address": null, //string + "image": null //string +} +``` + + +## API GROUP SALES + +Search +``` +[POST] https://oslog.id/geohr-api/group-sales/search +{ + "paging": {"start": 0, "length": 10}, + "columns": [ + {"name":"name", "logic_operator": "like", "value": "", "operator": "and"} + ], + "orders": {"columns": ["name"], "ascending": true} +} +``` + +Payload Add/Edit Group Sales +``` +[POST] https://oslog.id/geohr-api/group-sales/add +[PUT] https://oslog.id/geohr-api/group-sales/{id:[0-9]+}/edit +{ + "name": null, //string + "description": null //string +} +``` + +## API WAYPOINT SALES + +Search +``` +[POST] https://oslog.id/geohr-api/waypoint-sales/monitoring?salesId=1 +Payload +{ + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"wptime", "logic_operator": "range", "value": "2021-06-15 00:00:00", "value1": "2021-06-15 23:59:59", "operator": "and"} + ], + "orders": {"columns": ["wptime"], "ascending": true} +} +``` + +Example Response (if waypoint is available) +``` +{ + "code": 200, + "data": { + "type": "FeatureCollection", + "features": [ + { + "id": "m_sales.1", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "m_sales.1", + "group_sales": 7, + "username": "ibnuh", + "password": "7f2ababa423061c509f4923dd04b6cf1", + "session_login": "a5b5dff4-a7af-475c-9eb8-13f234d05472", + "name": "ibnu hamdani", + "phone_number": "083823134569", + "email": "ibnuhamdani234@gmail.com", + "address": "jl tenjo bogor", + "achieve": null, + "type_sales": "BTB", + "lat": -6.2623094, + "lon": 106.7880976, + "created_by": "admin", + "created_date": "2021-06-15T03:35:32.852925Z", + "modified_by": "@system", + "modified_date": "2021-06-16T07:24:31.227968Z", + "join": { + "group_sales_description": "Group Sales Wilayah Kebayoran baru", + "group_sales_name": "Kebayoran Baru" + } + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 106.7881649, + -6.2622531 + ], + ..., + [ + 106.7881603, + -6.2622923 + ] + ] + } + } + ] + }, + "executionTime": "6.957314ms", + "message": "OK", + "totalRecord": 1 +} +``` + +Example Response (if waypoint is unavailable) +``` +{ + "code": 200, + "data": { + "type": "FeatureCollection", + "features": [ + { + "id": "m_sales.2", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "m_sales.2", + "group_sales": 25, + "username": "jono", + "password": "jono123", + "session_login": null, + "name": "jono", + "phone_number": "0983838484", + "email": "jono@gmail.com", + "address": "jonoooo", + "achieve": null, + "type_sales": "BTB", + "lat": null, + "lon": null, + "created_by": "@system", + "created_date": "2021-06-16T04:06:23.743462Z", + "modified_by": "@system", + "modified_date": "2021-06-16T07:24:21.725833Z", + "join": { + "group_sales_description": "Group Sales Wilayah Cilandak", + "group_sales_name": "Cilandak" + } + }, + "geometry": { + "type": "LineString", + "coordinates": [] + } + } + ] + }, + "executionTime": "2.73392ms", + "message": "OK", + "totalRecord": 0 +} +``` + + +Payload Add Waypoint Sales +``` +[POST] https://oslog.id/geohr-api/waypoint-sales/add +{ + "sales": null, //int + "lat": null, //float + "lon": null, //float + "wptime": null, //time now + "speed": null, //int + "angle": null, //float + "satelite": null, //int +} +``` + + +## API DAILY INFO + +Response (still dummy) +``` +{ + "code":200, + "data": { + "at_trip":{"total": 50, "id": [1, 2, 25]}, + "at_customer":"20", + "at_office":"30", + "present": "100", + "absent":"10", + "at_time":"2021-06-16T07:16:03.509928Z" + }, + "executionTime":"1.494754ms", + "message":"OK" +} +``` \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c6abef8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,476 @@ +## [CoreUI](https://coreui.io/) for [react](./REACT.md) changelog + +##### `v2.6.0` +- move to `reactstrap v8`. Breaking changes and deprecations, see: https://github.com/reactstrap/reactstrap/blob/master/CHANGELOG.md#800-2019-04-03 +- fix(DefaultHeader): replace `AppHeaderDropdown` with `UncontrolledDropdown` +- refactor: add ie polyfills + +###### dependencies update +- update: `@coreui/coreui` to `^2.1.12` +- update: `@coreui/coreui-plugin-chartjs-custom-tooltips` to `^1.3.1` +- update: `@coreui/react` to `^2.5.1` +- update: `core-js` to `^3.1.4` +- update: `enzyme` to `^3.10.0` +- update: `enzyme-adapter-react-16` to `^1.14.0` +- update: `react-router-config` to `^5.0.1` +- update: `react-router-dom` to `^5.0.1` +- update: `reactstrap` to `^8.0.0` + +##### `v2.5.0` +- release for use with: + - react-router-dom `~5.0.0` + - @coreui/react `~2.5.0` + +###### dependencies update +- update: `@coreui/react` to `~2.5.0` +- update: `react-router-config` to `^5.0.0` +- update: `react-router-dom` to `^5.0.0` + +It turns out this is not such a breaking change, as it seemed at a glance. +Just update dependencies and you're good. + +#### _migration guide v2.1 -> v2.5_ :boom: +- update `dependencies` in `package.json` + - [ ] `@coreui/react` to `~2.5.0` + - [ ] `react-router-dom` to `^5.0.0` + - [ ] `react-router-config` to `^5.0.0` + + +__BREAKING CHANGES__ :boom: +- use React Router `v5` +- drop 'Breadcrumb' in favour of `Breadcrumb2` +- drop 'SidebarNav' in favour of `SidebarNav2` +- __Breadcrumb2__: **mandatory** prop `router` 💥 see > [Breadcrumb](./src/Breadcrumb.md) +- __SidebarNav2__: **mandatory** prop `router` 💥 see > [SidebarNav](./src/SidebarNav.md) + +React Router v5 uses the new React Context API, which is incompatible with version used in 4.3. +That's a breaking change. With a raw upgrade to v5, you can encounter an error message: `You should not render a outside a ` or `You should not use outside a ` etc... It means that Route, Link etc, can't find the correct context object because `Breadcrumb` and `SidebarNav` components have their own context object. + +It's important to use the same instance of the `react-router-dom v5` library with template and coreui components. `@coreui/react` version `2.5.0` moves react-router-dom form dependencies to peerDependecies and takes the same library/module from the template/app instead. We have to pass `router` module object as a prop to `` and `` + +#### _migration guide v2.1 -> v2.5_ :boom: +1. update `dependencies` in `package.json` + - [ ] `@coreui/react` to `~2.5.0` + - [ ] `react-router-dom` to `^5.0.0` + - [ ] `react-router-config` to `^5.0.0` + +2. modify `DefaultLayout.js` + - [ ] import react-router-dom module as an object + ``` + import * as router from 'react-router-dom'; + ``` + - [ ] import new versions of components `AppBreadcrumb2` and `AppSidebarNav2` (alias is optional, just keep consistency with markup) + ```jsx + import { + ... + AppBreadcrumb2 as AppBreadcrumb, + AppSidebarNav2 as AppSidebarNav + ... + } from '@coreui/react'; + ``` + - [ ] inject `router` object as a prop to `` and `` + ```html + + ``` + + ```html + + ``` + +--- + +##### `v2.1.7` +- maintenance release for use with: + - react-router `v4.3.x` + - reactstrap `v7.x` + - @coreui/react `~2.1.7` +- chore: add `package-lock.json` with updated `tar` dependency +- chore: fix `test:cov` script +- fix(Popovers): add `trigger="legacy" delay={0}` (breaking change in reactstrap) +###### dependencies update +- update: `@coreui/react` to `~2.1.7` +- update: `@coreui/coreui-plugin-chartjs-custom-tooltips` to `^1.3.0` +- update: `enzyme-adapter-react-16` to `^1.13.0` +- update: `node-sass` to `^4.12.0` +- update: `react` to `^16.8.6` +- update: `react-app-polyfill` to `^1.0.1` +- update: `react-chartjs-2` to `^2.7.6` +- update: `react-dom` to `^16.8.6` +- update: `react-test-renderer` to `^16.8.6` +- update: `react-scripts` to `^3.0.1` + +##### `v2.1.6` +- fix(App): remove redundant react-loadable - thanks @sergeyt +- fix(routes) remove circular dependency - thanks @sergeyt +- refactor(App): change to render in Route +- fix(routes): add Home to routes - breadcrumb issue +- refactor(DefaultHeader): move to ReactRouter `NavLink` +- refactor(Forms): move to `InputGroupButtonDropdown` where applicable + +###### dependencies update +- update: `@coreui/coreui` to `^2.1.9` +- update: `@coreui/react` to `~2.1.5` +- update: `chart.js` to `^2.8.0` +- update: `enzyme-adapter-react-16` to `^1.11.2` +- update: `react` to `^16.8.5` +- update: `react-app-polyfill` to `^0.2.2` +- update: `react-dom` to `^16.8.5` +- update: `react-router-config` to `^4.4.0-beta.8` +- update: `react-router-dom` to `~4.3.1` +- update: `react-test-renderer` to `^16.8.5` +- update: `react-scripts` to `^2.1.8` + +##### `v2.1.5` +- fix: iOS 9 Safari sidebar toggle force issue `@coreui/react@2.1.5` + +###### dependencies update +- update: `@coreui/react` to `^2.1.5` +- update: `enzyme-adapter-react-16` to `^1.10.0` +- update: `flag-icon-css` to `^3.3.0` +- update: `react` to `^16.8.4` +- update: `react-dom` to `^16.8.4` +- update: `react-test-renderer` to `^16.8.4` + +##### `v2.1.4` +- maintenance release: fixes #151 #145 +###### dependencies update +- update: `@coreui/coreui` to `^2.1.7` +- update: `@coreui/react` to `^2.1.4` +- update: `bootstrap` to `^4.3.1` +- update: `core-js` to `^2.6.5` +- update: `enzyme` to `^3.9.0` +- update: `enzyme-adapter-react-16` to `^1.9.1` +- update: `prop-types` to `^15.7.2` +- update: `react` to `^16.8.2` +- update: `react-app-polyfill` to `^0.2.1` +- update: `react-dom` to `^16.8.2` +- update: `react-test-renderer` to `^16.8.2` +- update: `reactstrap` to `^7.1.0` +- update: `react-scripts` to `2.1.5` + +##### `v2.1.3` +- fix(Collapse): add `mb-0` to accordion cards +- fix(ButtonGroups): misplaced dropdownOpen +- chore: update `@coreui/coreui` to `^2.1.5` +- chore: update `@coreui/react` to `^2.1.3` +- chore: update `bootstrap` to `^4.2.1` +- chore: update `core-js` to `^2.6.1` +- chore: update `enzyme` to `^3.8.0` +- chore: update `enzyme-adapter-react-16` to `^1.7.1` +- chore: update `node-sass` to `^4.11.0` +- chore: update `react` to `^16.7.0` +- chore: update `react-app-polyfill` to `^0.2.0` +- chore: update `react-chartjs-2` to `^2.7.4` +- chore: update `react-dom` to `^16.7.0` +- chore: update `react-test-renderer` to `^16.7.0` +- chore: update `reactstrap` to `^7.0.2` +- chore: update `react-scripts` to `2.1.3` + +##### `v2.1.2` +- fix(scss): floating footer ie11 issue +- chore: update `@coreui/react` to `^2.1.1` + +##### `v2.1.1` +- refactor(App.js): code splitting with `react-loadable` (waiting for release of `react-router-dom`) +- refactor(routes.js): code splitting with `React.lazy`, remove `react-loadable` +- refactor(DefaultLayout): code splitting with `React.lazy` Aside, Footer, Header, routes +- refactor(Dashboard): tweak lazy and Suspense for Widget03 +- refactor(Login): add router link to `Register` button +- refactor(Register): add margins to social-media buttons +- chore: disable eslint warning for href="#" attribute +- chore: update `@coreui/coreui` to `^2.1.1` +- chore: update `enzyme-adapter-react-16` to `1.7.0` +- chore: update `react` to `16.6.3` +- chore: update `react-dom` to `16.6.3` +- chore: update `react-test-renderer` to `16.6.3` + +##### `v2.1.0` +- feat(SidebarNav): navLink `attributes` - optional JS object with valid JS API naming: + - valid attributes: `rel`, `target`, `hidden`, `disabled`, etc... + - starting with `@coreui/coreui`, `@coreui/react` version `2.1.0` and up + - closes #106 + - item example(`./src/_nav.js`): + ```js + [ + { + name: 'Disabled', + url: '/disabled', + icon: 'icon-ban', + attributes: { disabled: true }, + }, + { + name: 'Try CoreUI PRO', + url: 'https://coreui.io/pro/react/', + icon: 'cui-layers icons', + variant: 'danger', + attributes: { target: '_blank', rel: "noopener" }, + } + ] + ``` +- fix(Cards): `card-header-actions` added to `CardHeader` for `rtl` support +- feat(Dashboard): new `Suspense` example with Widget03 +- chore: update `@coreui/coreui` to `2.1.0` +- chore: update `@coreui/react` to `2.1.0` +- chore: update `node-sass` to `4.10.0` +- chore: update `react` to `16.6.1` +- chore: update `react-dom` to `16.6.1` +- chore: update `react-test-renderer` to `16.6.1` + +##### `v2.0.14` +- chore: update `@coreui/coreui` to `2.0.25` +- chore: update `chart.js` to `2.7.3` +- chore: update `flag-icon-css` to `3.2.1` +- chore: update `node-sass` to `4.9.4` +- chore: update `react` to `16.6.0` +- chore: update `react-dom` to `16.6.0` +- chore: update `react-router-config` to `4.4.0-beta.6` +- chore: update `react-test-renderer` to `16.6.0` +- chore: update `react-scripts` to `2.1.1` + +##### `v2.0.13` +- refactor: migration to [Create React App 2.0](https://reactjs.org/blog/2018/10/01/create-react-app-v2.html) cleanup + - cleanup `package.json` scripts + - remove `babel-jest` dependency + - remove `node-sass-chokidar` dependency + - remove `npm-run-all` dependency + - move `App.js` import styles to `App.scss` + - replace imports from `node_modules/` with `~` prefix +- chore: remove unused `src/scss/vendors/charts.js/` directory +- chore: update `@coreui/coreui` to `^2.0.15` +- chore: update `@coreui/react` to `^2.0.9` + +##### `v2.0.12` +fixes some issues with `rtl`, `ie11`, `sidebar-minimized` behaviour and `aside` responsiveness +- fix(DefaultAside): `ListGroup` with `tag="div"` works better with `rtl` +- fix(DefaultLayout): `AppAside` remove deprecated `hidden` prop +- chore: update `@coreui/react` to `^2.0.8` +- chore: update `reactsrtrap` to `^6.5.0` +- chore: update `react-scripts` to `^2.0.4` +- chore: `enzyme` to `3.7.0` +- chore: `enzyme-adapter-react-16` to `1.6.0` + +##### `v2.0.11` +- chore: update `@coreui/react` to `^2.0.7` +- chore: migration to [Create React App 2.0](https://reactjs.org/blog/2018/10/01/create-react-app-v2.html) + - chore: update `react-scripts` to `^2.0.3` + - chore: update `node-sass-chokidar` to `^1.3.3` + - chore: add `node-sass v4.9.3` + - chore: add `react-app-polyfill v0.1.3` + - chore: add `eslintConfig` in `package.json` + - chore: add `browserslist` in `package.json` + - chore: update `manifest.json` + - refactor(index.js): add `react-app-polyfill` for `ie9-11` support + - refactor(index.js): migration to `serviceWorker.js` + +###### Migrating from CRA 1.x to 2.x: +affected files: +- `package.json` -> dependencies update +- `src/index.js` -> move to `serviceWorker`, add `react-app-polyfill` for `ie9-11` support when needed + +In most cases bumping the `react-scripts` version in `package.json` and running `npm install` in this folder should be enough, but it’s good to consult the [changelog](https://github.com/facebook/create-react-app/blob/master/CHANGELOG.md#migrating-from-1x-to-203) for potential breaking changes. + +--- + +##### `v2.0.10` +- chore: update `@coreui/coreui` to `^2.0.14` +- chore: update `@coreui/react` to `^2.0.6` +- chore: update `enzyme` to `^3.6.0` +- chore: update `enzyme-adapter-react-16` to `^1.5.0` +- chore: update `flag-icon-css` to `^3.2.0` +- chore: update `react` to `^16.5.2` +- chore: update `react-dom` to `^16.5.2` +- chore: update `react-router-config` to `^4.4.0-beta.1` +- chore: update `react-test-renderer` to `^16.5.2` +- chore: update `babel-jest` to `^23.6.0` + +##### `v2.0.9` +- chore: update `@coreui/icons` to `0.3.0` +- refactor(CoreUIIcons): move to `@coreui/icons v0.3.0` +- chore: update `enzyme` to `3.5.0` +- chore: update `enzyme-adapter-react-16` to `1.3.1` +- chore: update `react-loadable` to `5.5.0` +- chore: update `reactstrap` to `6.4.0` +- chore: update `react-scripts` to `1.1.5` + +##### `v2.0.8` +- fix(User): add missing unique key prop +- fix(Login): add missing form and autoComplete +- fix(Register): add missing form and autoComplete +- chore: update `@coreui/react` to `2.0.5` +- chore: update `bootstrap` to `4.1.3` +- chore: update `reactstrap` to `6.3.1` +- chore: update `babel-jest` to `23.4.2` + +##### `v2.0.5` +- feat(router): Users/User Breadcrumb example with `/users/:id` +- chore: update `@coreui/react` to `2.0.4`, +- chore: update `prop-types` to `15.6.2` +- chore: update `react` to `16.4.1` +- chore: update `react-dom` to `16.4.1` +- chore: update `react-test-renderer` to `16.4.1` +- chore: update `npm-run-all` to `4.1.3` +- chore: add `.env` file + +##### `v2.0.4` +- feat(Forms): FormFeedback valid, toggleFade +- refactor(Cards): toggleFade +- chore: update `@coreui/coreui` to `2.0.2`, +- chore: update `@coreui/react` to `2.0.1`, +- chore: update `classnames` to `2.2.6`, +- chore: update `core-js` to `2.5.7`, +- chore: update `react` to `16.4.0`, +- chore: update `react-dom` to `16.4.0`, +- chore: update `react-router-dom` to `4.3.1`, +- chore: update `react-test-renderer` to `16.4.0`, +- chore: update `reactstrap` to `6.1.0`, +- chore: update `babel-jest` to `23.0.1`, + +##### `v2.0.3` +- refactor: disable `ServiceWorker` by default +- fix(routes): mismatched `SimpleLineIcons` dynamic import +- refactor: CoreUI Icons `v0.2.0` +- chore: update`babel-jest` to `v22.4.4` + +##### `v2.0.2` +- chore: update `@coreui/react` to `v2.0.0`, + +##### `v2.0.1` +- refactor: code splitting via dynamic import +- refactor: switches view rearrange +- fix: update component names in package.json +- chore: update `node-sass-chokidar` to `v1.3.0` +- chore(release): dependencies update + +##### `v2.0.0-rc.1` +- feat: new CoreUI Icons set + +##### `v2.0.0-beta.2` +- feat: CoreUI custom tooltips plugin for chart.js + +##### `v2.0.0-beta.1` +- refactor(Switches): move to AppSwitch component +- fix: typo + +##### `v2.0.0-beta` +- update to `@coreui/react: ^2.0.0-beta` + +##### `v2.0.0-alpha.3` +- refactor(Colors): view layout, minor temp tweaks +- refactor(FullAside): - ListGroup (deprecate callout) +- refactor(Full*): containers minor fixes +- refactor(Dropdowns): minor fixes +- refactor(Forms): `card-header-actions` +- feat(Forms): `` +- feat(Forms): `FormFeedback` +- feat(Collapses): Accordion, Custom Accordion +- feat(ListGroup): with TabPanes +- refactor(PaginationItem): `tag="button"` +- refactor(BrandButtons): spacing +- refactor:(Buttons): view layout + +##### `v2.0.0-alpha.2` +- refactor: FullHeader `` (required prop `direction`) +- refactor: ButtonDropdowns `` (deprecate 'dropup') +- refactor: Dashboard legend badge pill +- refactor: SocialButtons to BrandButtons `btn-brand` +- refactor: Buttons spacing `mr-1` +- update: reactstrap to `5.0.0` +- update: react, react-dom to `16.3.1` +- update: node-sass-chokidar to `1.1.0` +- update: prop-types to `15.5.8` +- update: react-scripts to `1.1.4` + +##### `v2.0.0-alpha.1` +- refactor: separation of concerns - (CoreUI template vs CoreUI components) prepare to use CoreUI as dependency +- refactor: project structure change +- refactor: moved to [Create-React-App](CRA.md) +- chore: moved to [Semantic Versioning](https://semver.org/) + +##### `v1.0.10` +- refactor: `` +- refactor: `` +- refactor: `` +- refactor: remove `` +- update: reactstrap to `5.0.0-beta` +- update: dependencies + +###### `v1.0.9` +- refactor: Sidebar structure change + +###### `v1.0.8` +- refactor: Dashboard radio buttons, new `onRadioBtnClick()` method +- update: react to `16.2.0` +- update: Bootstrap `4.0.0-beta.3` +- update: dependencies +- feature: some Bootstrap4 components added +- fix: rollback to webpack-dev-server `2.9.7` +- temp tweaks(b4 beta3): `InputGroupAddon` and `InputGroupButton` +- refactor(checkboxes, radios): temp tweaks +- feat: mobile sidebar link click closes the sidebar +- fix: .nav-tabs .nav-link `cursor: pointer` + +###### `v1.0.6` + +- update: react to `^16.1.1` +- update: reactstrap to `^5.0.0-alpha.4` +- refactor: deprecated reactstrap `NavDropdown` change to `Dropdown` with `nav` prop +- refactor: use prop `bsSize` instead of the `size` to bootstrap's input sizing +- update: dependencies + +###### `v1.0.5` +- feature: Sidebar add divider.class +- refactor: Sidebar +- moved to react: `^16.1.0` +- chore: dependencies update + +###### `v1.0.4` +- refactor: scss + +###### `v1.0.3` +- update: bootstrap to `4.0.0-beta.2` + +###### `v1.0.2` +- `HeaderDropdown` component example extracted out of `Header` + +###### `v1.0.1` +- moved to react: `^16.0.0` +- moved to reactstrap: `^5.0.0-alpha.3` +- moved to react-text-mask-hoc: `^0.10.4` +- moved from deprecated CardBlock to `CardBody` reactstrap component +- moved to `NavDropdown` in `Header` component +- fix for app-header navbar-nav dropdown-menu-right +- fix typo in Tables component PaginationItem + +###### `v1.0.0` +- Sidebar component: + - item with optional class (_nav.js) + - nav link with optional variant (_nav.js) + - external urls allowed (_nav.js) + - optional SidebarFooter, SidebarHeader, SidebarForm components +- SidebarMinimizer component +- .brand-minimized +- .sidebar-minimized, +- Header component - sidebarMinimize +- react-transition-group downgrade to v1 : (modals and alerts reactstrap:v4.8 issue) + +###### `2017.08.24` +- webpack.config env.prod +- Dashboard .dropdown-menu-right temp.scss hotfix (full) +- callout.scss .chart-wrapper hotfix (full) + +###### `2017.08.11` +- Bootstrap 4 beta +- Dashboard component (full): + - line chart for social box + - sparkline chart for callout + +###### `2017.08.01` +- Sidebar component: + - title item with optional wrapper and class (_nav.js) + - nav link item with optional badge + - code refactoring + +###### `2017.07.31` +- moved to [reactstrap](https://reactstrap.github.io/) +- moved to [webpack](https://webpack.js.org/) (dropping gulp) +- data driven Sidebar component (_nav.js) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bd0b34b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,172 @@ +# Contributing to CoreUI Free React Admin Template + +Looking to contribute something to CoreUI Free React Admin Template? **Here's how you can help.** + +Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved. + +Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features. + +## Using the issue tracker + +The [issue tracker](https://github.com/coreui/coreui-free-react-admin-template/issues) is the preferred channel for [bug reports](#bug-reports), [features requests](#feature-requests) and [submitting pull requests](#pull-requests), but please respect the following restrictions: + +* Please **do not** use the issue tracker for personal support requests. + +* Please **do not** post comments consisting solely of "+1" or ":thumbsup:". + Use [GitHub's "reactions" feature](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) + instead. + +* Please **do not** open issues or pull requests regarding the code in [`@coreui/coreui`](https://github.com/coreui/coreui-react) (open them in their respective repositories). + +## Bug reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. +Good bug reports are extremely helpful, so thanks! + +Guidelines for bug reports: + +0. **Validate and lint your code** — to ensure your problem isn't caused by a simple error in your own code. + +1. **Use the GitHub issue search** — check if the issue has already been reported. + +2. **Check if the issue has been fixed** — try to reproduce it using the latest `master` or development branch in the repository. + +3. **Isolate the problem** — ideally create a [reduced test case](https://css-tricks.com/reduced-test-cases/) and a live example. [This JS Bin](http://jsbin.com/lefey/1/edit?html,output) is a helpful template. + + +A good bug report shouldn't leave others needing to chase you up for more +information. Please try to be as detailed as possible in your report. What is +your environment? What steps will reproduce the issue? What browser(s) and OS +experience the problem? Do other browsers show the bug differently? What +would you expect to be the outcome? All these details will help people to fix +any potential bugs. + +Example: + +> Short and descriptive example bug report title +> +> A summary of the issue and the browser/OS environment in which it occurs. If +> suitable, include the steps required to reproduce the bug. +> +> 1. This is the first step +> 2. This is the second step +> 3. Further steps, etc. +> +> `` - a link to the reduced test case +> +> Any other information you want to share that is relevant to the issue being +> reported. This might include the lines of code that you have identified as +> causing the bug, and potential solutions (and your opinions on their +> merits). + +## Feature requests + +Feature requests are welcome. Before opening a feature request, please take a moment to find out whether your idea +fits with the scope and aims of the project. It's up to *you* to make a strong +case to convince the project's developers of the merits of this feature. Please +provide as much detail and context as possible. + + +## Pull requests + +Good pull requests—patches, improvements, new features—are a fantastic +help. They should remain focused in scope and avoid containing unrelated +commits. + +**Please ask first** before embarking on any significant pull request (e.g. +implementing features, refactoring code, porting to a different language), +otherwise you risk spending a lot of time working on something that the +project's developers might not want to merge into the project. + +Adhering to the following process is the best way to get your work +included in the project: + +1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com//free-react-admin-template.git + # Navigate to the newly cloned directory + cd free-react-admin-template + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/coreui/coreui-free-react-admin-template.git + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout master + git pull upstream master + ``` + +3. Create a new topic branch (off the main project development branch) to + contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +4. Commit your changes in logical chunks. Please adhere to these [git commit + message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + or your code is unlikely to be merged into the main project. Use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + +5. Locally merge (or rebase) the upstream development branch into your topic branch: + + ```bash + git pull [--rebase] upstream master + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description against the `master` branch. + +**IMPORTANT**: By submitting a patch, you agree to allow the project owners to license your work under the terms of the [MIT License](LICENSE). + +### Semantic Git commit messages + +Inspired by Sparkbox's awesome article on [semantic commit messages](http://seesparkbox.com/foundry/semantic_commit_messages). Please use following commit message format. + +* chore (updating npm tasks etc; no production code change) -> ```git test -m 'chore: commit-message-here'``` +* docs (changes to documentation) -> ```git commit -m 'docs: commit-message-here'``` +* feat (new feature) -> ```git commit -m 'feat: commit-message-here'``` +* fix (bug fix) -> ```git commit -m 'fix: commit-message-here'``` +* refactor (refactoring production code) -> ```git commit -m 'refactor: commit-message-here'``` +* style (formatting, missing semi colons, etc; no code change) -> ```git commit -m 'style: commit-message-here'``` +* test (adding missing tests, refactoring tests; no production code change) -> ```git test -m 'refactor: commit-message-here'``` + + +## Code guidelines + +### HTML + +[Adhere to the Code Guide.](http://codeguide.co/#html) + +- Use tags and elements appropriate for an HTML5 doctype (e.g., self-closing tags). +- Use CDNs and HTTPS for third-party JS when possible. We don't use protocol-relative URLs in this case because they break when viewing the page locally via `file://`. +- Use [WAI-ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) attributes in documentation examples to promote accessibility. + +### CSS + +[Adhere to the Code Guide.](http://codeguide.co/#css) + +- When feasible, default color palettes should comply with [WCAG color contrast guidelines](http://www.w3.org/TR/WCAG20/#visual-audio-contrast). +- Except in rare cases, don't remove default `:focus` styles (via e.g. `outline: none;`) without providing alternative styles. See [this A11Y Project post](http://a11yproject.com/posts/never-remove-css-outlines) for more details. + +### JS + +- No semicolons (in client-side JS) +- 2 spaces (no tabs) +- strict mode +- "Attractive" +- Don't use [jQuery event alias convenience methods](https://github.com/jquery/jquery/blob/master/src/event/alias.js) (such as `$().focus()`). Instead, use [`$().trigger(eventType, ...)`](http://api.jquery.com/trigger/) or [`$().on(eventType, ...)`](http://api.jquery.com/on/), depending on whether you're firing an event or listening for an event. (For example, `$().trigger('focus')` or `$().on('focus', function (event) { /* handle focus event */ })`) We do this to be compatible with custom builds of jQuery where the event aliases module has been excluded. + +## License + +By contributing your code, you agree to license your contribution under the [MIT License](LICENSE). diff --git a/CRA.md b/CRA.md new file mode 100644 index 0000000..2fe2c04 --- /dev/null +++ b/CRA.md @@ -0,0 +1,2473 @@ +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +Below you will find some information on how to perform common tasks.
+You can find the most recent version of this guide [here](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md). + +## Table of Contents + +- [Updating to New Releases](#updating-to-new-releases) +- [Sending Feedback](#sending-feedback) +- [Folder Structure](#folder-structure) +- [Available Scripts](#available-scripts) + - [npm start](#npm-start) + - [npm test](#npm-test) + - [npm run build](#npm-run-build) + - [npm run eject](#npm-run-eject) +- [Supported Browsers](#supported-browsers) +- [Supported Language Features](#supported-language-features) +- [Syntax Highlighting in the Editor](#syntax-highlighting-in-the-editor) +- [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor) +- [Debugging in the Editor](#debugging-in-the-editor) +- [Formatting Code Automatically](#formatting-code-automatically) +- [Changing the Page ``](#changing-the-page-title) +- [Installing a Dependency](#installing-a-dependency) +- [Importing a Component](#importing-a-component) +- [Code Splitting](#code-splitting) +- [Adding a Stylesheet](#adding-a-stylesheet) +- [Adding a CSS Modules Stylesheet](#adding-a-css-modules-stylesheet) +- [Adding a Sass Stylesheet](#adding-a-sass-stylesheet) +- [Post-Processing CSS](#post-processing-css) +- [Adding Images, Fonts, and Files](#adding-images-fonts-and-files) +- [Adding SVGs](#adding-svgs) +- [Using the `public` Folder](#using-the-public-folder) + - [Changing the HTML](#changing-the-html) + - [Adding Assets Outside of the Module System](#adding-assets-outside-of-the-module-system) + - [When to Use the `public` Folder](#when-to-use-the-public-folder) +- [Using Global Variables](#using-global-variables) +- [Adding Bootstrap](#adding-bootstrap) + - [Using a Custom Theme](#using-a-custom-theme) +- [Adding Flow](#adding-flow) +- [Adding a Router](#adding-a-router) +- [Adding Custom Environment Variables](#adding-custom-environment-variables) + - [Referencing Environment Variables in the HTML](#referencing-environment-variables-in-the-html) + - [Adding Temporary Environment Variables In Your Shell](#adding-temporary-environment-variables-in-your-shell) + - [Adding Development Environment Variables In `.env`](#adding-development-environment-variables-in-env) +- [Can I Use Decorators?](#can-i-use-decorators) +- [Fetching Data with AJAX Requests](#fetching-data-with-ajax-requests) +- [Integrating with an API Backend](#integrating-with-an-api-backend) + - [Node](#node) + - [Ruby on Rails](#ruby-on-rails) +- [Proxying API Requests in Development](#proxying-api-requests-in-development) + - ["Invalid Host Header" Errors After Configuring Proxy](#invalid-host-header-errors-after-configuring-proxy) + - [Configuring the Proxy Manually](#configuring-the-proxy-manually) +- [Using HTTPS in Development](#using-https-in-development) +- [Generating Dynamic `<meta>` Tags on the Server](#generating-dynamic-meta-tags-on-the-server) +- [Pre-Rendering into Static HTML Files](#pre-rendering-into-static-html-files) +- [Injecting Data from the Server into the Page](#injecting-data-from-the-server-into-the-page) +- [Running Tests](#running-tests) + - [Filename Conventions](#filename-conventions) + - [Command Line Interface](#command-line-interface) + - [Version Control Integration](#version-control-integration) + - [Writing Tests](#writing-tests) + - [Testing Components](#testing-components) + - [Using Third Party Assertion Libraries](#using-third-party-assertion-libraries) + - [Initializing Test Environment](#initializing-test-environment) + - [Focusing and Excluding Tests](#focusing-and-excluding-tests) + - [Coverage Reporting](#coverage-reporting) + - [Continuous Integration](#continuous-integration) + - [Disabling jsdom](#disabling-jsdom) + - [Snapshot Testing](#snapshot-testing) + - [Editor Integration](#editor-integration) +- [Debugging Tests](#debugging-tests) + - [Debugging Tests in Chrome](#debugging-tests-in-chrome) + - [Debugging Tests in Visual Studio Code](#debugging-tests-in-visual-studio-code) +- [Developing Components in Isolation](#developing-components-in-isolation) + - [Getting Started with Storybook](#getting-started-with-storybook) + - [Getting Started with Styleguidist](#getting-started-with-styleguidist) +- [Publishing Components to npm](#publishing-components-to-npm) +- [Making a Progressive Web App](#making-a-progressive-web-app) + - [Why Opt-in?](#why-opt-in) + - [Offline-First Considerations](#offline-first-considerations) + - [Progressive Web App Metadata](#progressive-web-app-metadata) +- [Analyzing the Bundle Size](#analyzing-the-bundle-size) +- [Deployment](#deployment) + - [Static Server](#static-server) + - [Other Solutions](#other-solutions) + - [Serving Apps with Client-Side Routing](#serving-apps-with-client-side-routing) + - [Building for Relative Paths](#building-for-relative-paths) + - [Customizing Environment Variables for Arbitrary Build Environments](#customizing-environment-variables-for-arbitrary-build-environments) + - [Azure](#azure) + - [Firebase](#firebase) + - [GitHub Pages](#github-pages) + - [Heroku](#heroku) + - [Netlify](#netlify) + - [Now](#now) + - [S3 and CloudFront](#s3-and-cloudfront) + - [Surge](#surge) +- [Advanced Configuration](#advanced-configuration) +- [Troubleshooting](#troubleshooting) + - [`npm start` doesn’t detect changes](#npm-start-doesnt-detect-changes) + - [`npm test` hangs or crashes on macOS Sierra](#npm-test-hangs-or-crashes-on-macos-sierra) + - [`npm run build` exits too early](#npm-run-build-exits-too-early) + - [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku) + - [`npm run build` fails to minify](#npm-run-build-fails-to-minify) + - [Moment.js locales are missing](#momentjs-locales-are-missing) +- [Alternatives to Ejecting](#alternatives-to-ejecting) +- [Something Missing?](#something-missing) + +## Updating to New Releases + +Create React App is divided into two packages: + +- `create-react-app` is a global command-line utility that you use to create new projects. +- `react-scripts` is a development dependency in the generated projects (including this one). + +You almost never need to update `create-react-app` itself: it delegates all the setup to `react-scripts`. + +When you run `create-react-app`, it always creates the project with the latest version of `react-scripts` so you’ll get all the new features and improvements in newly created apps automatically. + +To update an existing project to a new version of `react-scripts`, [open the changelog](https://github.com/facebook/create-react-app/blob/master/CHANGELOG.md), find the version you’re currently on (check `package.json` in this folder if you’re not sure), and apply the migration instructions for the newer versions. + +In most cases bumping the `react-scripts` version in `package.json` and running `npm install` (or `yarn install`) in this folder should be enough, but it’s good to consult the [changelog](https://github.com/facebook/create-react-app/blob/master/CHANGELOG.md) for potential breaking changes. + +We commit to keeping the breaking changes minimal so you can upgrade `react-scripts` painlessly. + +## Sending Feedback + +We are always open to [your feedback](https://github.com/facebook/create-react-app/issues). + +## Folder Structure + +After creation, your project should look like this: + +``` +my-app/ + README.md + node_modules/ + package.json + public/ + index.html + favicon.ico + src/ + App.css + App.js + App.test.js + index.css + index.js + logo.svg +``` + +For the project to build, **these files must exist with exact filenames**: + +- `public/index.html` is the page template; +- `src/index.js` is the JavaScript entry point. + +You can delete or rename the other files. + +You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by Webpack.<br> +You need to **put any JS and CSS files inside `src`**, otherwise Webpack won’t see them. + +Only files inside `public` can be used from `public/index.html`.<br> +Read instructions below for using assets from JavaScript and HTML. + +You can, however, create more top-level directories.<br> +They will not be included in the production build so you can use them for things like documentation. + +## Available Scripts + +In the project directory, you can run: + +### `npm start` + +Runs the app in the development mode.<br> +Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.<br> +You will also see any lint errors in the console. + +### `npm test` + +Launches the test runner in the interactive watch mode.<br> +See the section about [running tests](#running-tests) for more information. + +### `npm run build` + +Builds the app for production to the `build` folder.<br> +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.<br> +Your app is ready to be deployed! + +See the section about [deployment](#deployment) for more information. + +### `npm run eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Supported Browsers + +By default, the generated project supports all modern browsers.<br> +Support for Internet Explorer 9, 10, and 11 requires [polyfills](https://github.com/facebook/create-react-app/blob/master/packages/react-app-polyfill/README.md). + +### Supported Language Features + +This project supports a superset of the latest JavaScript standard.<br> +In addition to [ES6](https://github.com/lukehoban/es6features) syntax features, it also supports: + +- [Exponentiation Operator](https://github.com/rwaldron/exponentiation-operator) (ES2016). +- [Async/await](https://github.com/tc39/ecmascript-asyncawait) (ES2017). +- [Object Rest/Spread Properties](https://github.com/tc39/proposal-object-rest-spread) (ES2018). +- [Dynamic import()](https://github.com/tc39/proposal-dynamic-import) (stage 3 proposal) +- [Class Fields and Static Properties](https://github.com/tc39/proposal-class-public-fields) (part of stage 3 proposal). +- [JSX](https://facebook.github.io/react/docs/introducing-jsx.html) and [Flow](https://flow.org/) syntax. + +Learn more about [different proposal stages](https://babeljs.io/docs/plugins/#presets-stage-x-experimental-presets-). + +While we recommend using experimental proposals with some caution, Facebook heavily uses these features in the product code, so we intend to provide [codemods](https://medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb) if any of these proposals change in the future. + +Note that **this project includes no [polyfills](https://github.com/facebook/create-react-app/blob/master/packages/react-app-polyfill/README.md)** by default. + +If you use any other ES6+ features that need **runtime support** (such as `Array.from()` or `Symbol`), make sure you are [including the appropriate polyfills manually](https://github.com/facebook/create-react-app/blob/master/packages/react-app-polyfill/README.md), or that the browsers you are targeting already support them. + +## Syntax Highlighting in the Editor + +To configure the syntax highlighting in your favorite text editor, head to the [relevant Babel documentation page](https://babeljs.io/docs/editors) and follow the instructions. Some of the most popular editors are covered. + +## Displaying Lint Output in the Editor + +> Note: this feature is available with `react-scripts@0.2.0` and higher.<br> +> It also only works with npm 3 or higher. + +Some editors, including Sublime Text, Atom, and Visual Studio Code, provide plugins for ESLint. + +They are not required for linting. You should see the linter output right in your terminal as well as the browser console. However, if you prefer the lint results to appear right in your editor, there are some extra steps you can do. + +You would need to install an ESLint plugin for your editor first. Then, add a file called `.eslintrc` to the project root: + +```js +{ + "extends": "react-app" +} +``` + +Now your editor should report the linting warnings. + +Note that even if you edit your `.eslintrc` file further, these changes will **only affect the editor integration**. They won’t affect the terminal and in-browser lint output. This is because Create React App intentionally provides a minimal set of rules that find common mistakes. + +If you want to enforce a coding style for your project, consider using [Prettier](https://github.com/jlongster/prettier) instead of ESLint style rules. + +## Debugging in the Editor + +**This feature is currently only supported by [Visual Studio Code](https://code.visualstudio.com) and [WebStorm](https://www.jetbrains.com/webstorm/).** + +Visual Studio Code and WebStorm support debugging out of the box with Create React App. This enables you as a developer to write and debug your React code without leaving the editor, and most importantly it enables you to have a continuous development workflow, where context switching is minimal, as you don’t have to switch between tools. + +### Visual Studio Code + +You would need to have the latest version of [VS Code](https://code.visualstudio.com) and VS Code [Chrome Debugger Extension](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) installed. + +Then add the block below to your `launch.json` file and put it inside the `.vscode` folder in your app’s root directory. + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Chrome", + "type": "chrome", + "request": "launch", + "url": "http://localhost:3000", + "webRoot": "${workspaceRoot}/src", + "sourceMapPathOverrides": { + "webpack:///src/*": "${webRoot}/*" + } + } + ] +} +``` + +> Note: the URL may be different if you've made adjustments via the [HOST or PORT environment variables](#advanced-configuration). + +Start your app by running `npm start`, and start debugging in VS Code by pressing `F5` or by clicking the green debug icon. You can now write code, set breakpoints, make changes to the code, and debug your newly modified code—all from your editor. + +Having problems with VS Code Debugging? Please see their [troubleshooting guide](https://github.com/Microsoft/vscode-chrome-debug/blob/master/README.md#troubleshooting). + +### WebStorm + +You would need to have [WebStorm](https://www.jetbrains.com/webstorm/) and [JetBrains IDE Support](https://chrome.google.com/webstore/detail/jetbrains-ide-support/hmhgeddbohgjknpmjagkdomcpobmllji) Chrome extension installed. + +In the WebStorm menu `Run` select `Edit Configurations...`. Then click `+` and select `JavaScript Debug`. Paste `http://localhost:3000` into the URL field and save the configuration. + +> Note: the URL may be different if you've made adjustments via the [HOST or PORT environment variables](#advanced-configuration). + +Start your app by running `npm start`, then press `^D` on macOS or `F9` on Windows and Linux or click the green debug icon to start debugging in WebStorm. + +The same way you can debug your application in IntelliJ IDEA Ultimate, PhpStorm, PyCharm Pro, and RubyMine. + +## Formatting Code Automatically + +Prettier is an opinionated code formatter with support for JavaScript, CSS and JSON. With Prettier you can format the code you write automatically to ensure a code style within your project. See the [Prettier's GitHub page](https://github.com/prettier/prettier) for more information, and look at this [page to see it in action](https://prettier.github.io/prettier/). + +To format our code whenever we make a commit in git, we need to install the following dependencies: + +```sh +npm install --save husky lint-staged prettier +``` + +Alternatively you may use `yarn`: + +```sh +yarn add husky lint-staged prettier +``` + +- `husky` makes it easy to use githooks as if they are npm scripts. +- `lint-staged` allows us to run scripts on staged files in git. See this [blog post about lint-staged to learn more about it](https://medium.com/@okonetchnikov/make-linting-great-again-f3890e1ad6b8). +- `prettier` is the JavaScript formatter we will run before commits. + +Now we can make sure every file is formatted correctly by adding a few lines to the `package.json` in the project root. + +Add the following field to the `package.json` section: + +```diff ++ "husky": { ++ "hooks": { ++ "pre-commit": "lint-staged" ++ } ++ } +``` + +Next we add a 'lint-staged' field to the `package.json`, for example: + +```diff + "dependencies": { + // ... + }, ++ "lint-staged": { ++ "src/**/*.{js,jsx,json,css}": [ ++ "prettier --single-quote --write", ++ "git add" ++ ] ++ }, + "scripts": { +``` + +Now, whenever you make a commit, Prettier will format the changed files automatically. You can also run `./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx}"` to format your entire project for the first time. + +Next you might want to integrate Prettier in your favorite editor. Read the section on [Editor Integration](https://prettier.io/docs/en/editors.html) on the Prettier GitHub page. + +## Changing the Page `<title>` + +You can find the source HTML file in the `public` folder of the generated project. You may edit the `<title>` tag in it to change the title from “React App” to anything else. + +Note that normally you wouldn’t edit files in the `public` folder very often. For example, [adding a stylesheet](#adding-a-stylesheet) is done without touching the HTML. + +If you need to dynamically update the page title based on the content, you can use the browser [`document.title`](https://developer.mozilla.org/en-US/docs/Web/API/Document/title) API. For more complex scenarios when you want to change the title from React components, you can use [React Helmet](https://github.com/nfl/react-helmet), a third party library. + +If you use a custom server for your app in production and want to modify the title before it gets sent to the browser, you can follow advice in [this section](#generating-dynamic-meta-tags-on-the-server). Alternatively, you can pre-build each page as a static HTML file which then loads the JavaScript bundle, which is covered [here](#pre-rendering-into-static-html-files). + +## Installing a Dependency + +The generated project includes React and ReactDOM as dependencies. It also includes a set of scripts used by Create React App as a development dependency. You may install other dependencies (for example, React Router) with `npm`: + +```sh +npm install --save react-router-dom +``` + +Alternatively you may use `yarn`: + +```sh +yarn add react-router-dom +``` + +This works for any library, not just `react-router-dom`. + +## Importing a Component + +This project setup supports ES6 modules thanks to Webpack.<br> +While you can still use `require()` and `module.exports`, we encourage you to use [`import` and `export`](http://exploringjs.com/es6/ch_modules.html) instead. + +For example: + +### `Button.js` + +```js +import React, { Component } from 'react'; + +class Button extends Component { + render() { + // ... + } +} + +export default Button; // Don’t forget to use export default! +``` + +### `DangerButton.js` + +```js +import React, { Component } from 'react'; +import Button from './Button'; // Import a component from another file + +class DangerButton extends Component { + render() { + return <Button color="red" />; + } +} + +export default DangerButton; +``` + +Be aware of the [difference between default and named exports](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281). It is a common source of mistakes. + +We suggest that you stick to using default imports and exports when a module only exports a single thing (for example, a component). That’s what you get when you use `export default Button` and `import Button from './Button'`. + +Named exports are useful for utility modules that export several functions. A module may have at most one default export and as many named exports as you like. + +Learn more about ES6 modules: + +- [When to use the curly braces?](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281) +- [Exploring ES6: Modules](http://exploringjs.com/es6/ch_modules.html) +- [Understanding ES6: Modules](https://leanpub.com/understandinges6/read#leanpub-auto-encapsulating-code-with-modules) + +## Code Splitting + +Instead of downloading the entire app before users can use it, code splitting allows you to split your code into small chunks which you can then load on demand. + +This project setup supports code splitting via [dynamic `import()`](http://2ality.com/2017/01/import-operator.html#loading-code-on-demand). Its [proposal](https://github.com/tc39/proposal-dynamic-import) is in stage 3. The `import()` function-like form takes the module name as an argument and returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) which always resolves to the namespace object of the module. + +Here is an example: + +### `moduleA.js` + +```js +const moduleA = 'Hello'; + +export { moduleA }; +``` + +### `App.js` + +```js +import React, { Component } from 'react'; + +class App extends Component { + handleClick = () => { + import('./moduleA') + .then(({ moduleA }) => { + // Use moduleA + }) + .catch(err => { + // Handle failure + }); + }; + + render() { + return ( + <div> + <button onClick={this.handleClick}>Load</button> + </div> + ); + } +} + +export default App; +``` + +This will make `moduleA.js` and all its unique dependencies as a separate chunk that only loads after the user clicks the 'Load' button. + +You can also use it with `async` / `await` syntax if you prefer it. + +### With React Router + +If you are using React Router check out [this tutorial](http://serverless-stack.com/chapters/code-splitting-in-create-react-app.html) on how to use code splitting with it. You can find the companion GitHub repository [here](https://github.com/AnomalyInnovations/serverless-stack-demo-client/tree/code-splitting-in-create-react-app). + +Also check out the [Code Splitting](https://reactjs.org/docs/code-splitting.html) section in React documentation. + +## Adding a Stylesheet + +This project setup uses [Webpack](https://webpack.js.org/) for handling all assets. Webpack offers a custom way of “extending” the concept of `import` beyond JavaScript. To express that a JavaScript file depends on a CSS file, you need to **import the CSS from the JavaScript file**: + +### `Button.css` + +```css +.Button { + padding: 20px; +} +``` + +### `Button.js` + +```js +import React, { Component } from 'react'; +import './Button.css'; // Tell Webpack that Button.js uses these styles + +class Button extends Component { + render() { + // You can use them as regular CSS styles + return <div className="Button" />; + } +} +``` + +**This is not required for React** but many people find this feature convenient. You can read about the benefits of this approach [here](https://medium.com/seek-blog/block-element-modifying-your-javascript-components-d7f99fcab52b). However you should be aware that this makes your code less portable to other build tools and environments than Webpack. + +In development, expressing dependencies this way allows your styles to be reloaded on the fly as you edit them. In production, all CSS files will be concatenated into a single minified `.css` file in the build output. + +If you are concerned about using Webpack-specific semantics, you can put all your CSS right into `src/index.css`. It would still be imported from `src/index.js`, but you could always remove that import if you later migrate to a different build tool. + +## Adding a CSS Modules Stylesheet + +> Note: this feature is available with `react-scripts@2.0.0` and higher. + +This project supports [CSS Modules](https://github.com/css-modules/css-modules) alongside regular stylesheets using the `[name].module.css` file naming convention. CSS Modules allows the scoping of CSS by automatically creating a unique classname of the format `[filename]\_[classname]\_\_[hash]`. + +> **Tip:** Should you want to preprocess a stylesheet with Sass then make sure to [follow the installation instructions](#adding-a-sass-stylesheet) and then change the stylesheet file extension as follows: `[name].module.scss` or `[name].module.sass`. + +CSS Modules let you use the same CSS class name in different files without worrying about naming clashes. Learn more about CSS Modules [here](https://css-tricks.com/css-modules-part-1-need/). + +### `Button.module.css` + +```css +.error { + background-color: red; +} +``` + +### `another-stylesheet.css` + +```css +.error { + color: red; +} +``` + +### `Button.js` + +```js +import React, { Component } from 'react'; +import styles from './Button.module.css'; // Import css modules stylesheet as styles +import './another-stylesheet.css'; // Import regular stylesheet + +class Button extends Component { + render() { + // reference as a js object + return <button className={styles.error}>Error Button</button>; + } +} +``` + +### Result + +No clashes from other `.error` class names + +```html +<!-- This button has red background but not red text --> +<button class="Button_error_ax7yz"></div> +``` + +**This is an optional feature.** Regular `<link>` stylesheets and CSS files are fully supported. CSS Modules are turned on for files ending with the `.module.css` extension. + +## Adding a Sass Stylesheet + +> Note: this feature is available with `react-scripts@2.0.0` and higher. + +Generally, we recommend that you don’t reuse the same CSS classes across different components. For example, instead of using a `.Button` CSS class in `<AcceptButton>` and `<RejectButton>` components, we recommend creating a `<Button>` component with its own `.Button` styles, that both `<AcceptButton>` and `<RejectButton>` can render (but [not inherit](https://facebook.github.io/react/docs/composition-vs-inheritance.html)). + +Following this rule often makes CSS preprocessors less useful, as features like mixins and nesting are replaced by component composition. You can, however, integrate a CSS preprocessor if you find it valuable. + +To use Sass, first install `node-sass`: + +```bash +$ npm install node-sass --save +$ # or +$ yarn add node-sass +``` + +Now you can rename `src/App.css` to `src/App.scss` and update `src/App.js` to import `src/App.scss`. +This file and any other file will be automatically compiled if imported with the extension `.scss` or `.sass`. + +To share variables between Sass files, you can use Sass imports. For example, `src/App.scss` and other component style files could include `@import "./shared.scss";` with variable definitions. + +This will allow you to do imports like + +```scss +@import 'styles/_colors.scss'; // assuming a styles directory under src/ +@import '~nprogress/nprogress'; // importing a css file from the nprogress node module +``` + +> **Tip:** You can opt into using this feature with [CSS modules](#adding-a-css-modules-stylesheet) too! + +> **Note:** You must prefix imports from `node_modules` with `~` as displayed above. + +## Post-Processing CSS + +This project setup minifies your CSS and adds vendor prefixes to it automatically through [Autoprefixer](https://github.com/postcss/autoprefixer) so you don’t need to worry about it. + +Support for new CSS features like the [`all` property](https://developer.mozilla.org/en-US/docs/Web/CSS/all), [`break` properties](https://www.w3.org/TR/css-break-3/#breaking-controls), [custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables), and [media query ranges](https://www.w3.org/TR/mediaqueries-4/#range-context) are automatically polyfilled to add support for older browsers. + +You can customize your target support browsers by adjusting the `browserslist` key in `package.json` accoring to the [Browserslist specification](https://github.com/browserslist/browserslist#readme). + +For example, this: + +```css +.App { + display: flex; + flex-direction: row; + align-items: center; +} +``` + +becomes this: + +```css +.App { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} +``` + +If you need to disable autoprefixing for some reason, [follow this section](https://github.com/postcss/autoprefixer#disabling). + +[CSS Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout) prefixing is disabled by default, but it will **not** strip manual prefixing. +If you'd like to opt-in to CSS Grid prefixing, [first familiarize yourself about its limitations](https://github.com/postcss/autoprefixer#does-autoprefixer-polyfill-grid-layout-for-ie).<br> +To enable CSS Grid prefixing, add `/* autoprefixer grid: on */` to the top of your CSS file. + +## Adding Images, Fonts, and Files + +With Webpack, using static assets like images and fonts works similarly to CSS. + +You can **`import` a file right in a JavaScript module**. This tells Webpack to include that file in the bundle. Unlike CSS imports, importing a file gives you a string value. This value is the final path you can reference in your code, e.g. as the `src` attribute of an image or the `href` of a link to a PDF. + +To reduce the number of requests to the server, importing images that are less than 10,000 bytes returns a [data URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) instead of a path. This applies to the following file extensions: bmp, gif, jpg, jpeg, and png. SVG files are excluded due to [#1153](https://github.com/facebook/create-react-app/issues/1153). + +Here is an example: + +```js +import React from 'react'; +import logo from './logo.png'; // Tell Webpack this JS file uses this image + +console.log(logo); // /logo.84287d09.png + +function Header() { + // Import result is the URL of your image + return <img src={logo} alt="Logo" />; +} + +export default Header; +``` + +This ensures that when the project is built, Webpack will correctly move the images into the build folder, and provide us with correct paths. + +This works in CSS too: + +```css +.Logo { + background-image: url(./logo.png); +} +``` + +Webpack finds all relative module references in CSS (they start with `./`) and replaces them with the final paths from the compiled bundle. If you make a typo or accidentally delete an important file, you will see a compilation error, just like when you import a non-existent JavaScript module. The final filenames in the compiled bundle are generated by Webpack from content hashes. If the file content changes in the future, Webpack will give it a different name in production so you don’t need to worry about long-term caching of assets. + +Please be advised that this is also a custom feature of Webpack. + +**It is not required for React** but many people enjoy it (and React Native uses a similar mechanism for images).<br> +An alternative way of handling static assets is described in the next section. + +### Adding SVGs + +> Note: this feature is available with `react-scripts@2.0.0` and higher. + +One way to add SVG files was described in the section above. You can also import SVGs directly as React components. You can use either of the two approaches. In your code it would look like this: + +```js +import { ReactComponent as Logo } from './logo.svg'; +const App = () => ( + <div> + {/* Logo is an actual React component */} + <Logo /> + </div> +); +``` + +This is handy if you don't want to load SVG as a separate file. Don't forget the curly braces in the import! The `ReactComponent` import name is special and tells Create React App that you want a React component that renders an SVG, rather than its filename. + +## Using the `public` Folder + +> Note: this feature is available with `react-scripts@0.5.0` and higher. + +### Changing the HTML + +The `public` folder contains the HTML file so you can tweak it, for example, to [set the page title](#changing-the-page-title). +The `<script>` tag with the compiled code will be added to it automatically during the build process. + +### Adding Assets Outside of the Module System + +You can also add other assets to the `public` folder. + +Note that we normally encourage you to `import` assets in JavaScript files instead. +For example, see the sections on [adding a stylesheet](#adding-a-stylesheet) and [adding images and fonts](#adding-images-fonts-and-files). +This mechanism provides a number of benefits: + +- Scripts and stylesheets get minified and bundled together to avoid extra network requests. +- Missing files cause compilation errors instead of 404 errors for your users. +- Result filenames include content hashes so you don’t need to worry about browsers caching their old versions. + +However there is an **escape hatch** that you can use to add an asset outside of the module system. + +If you put a file into the `public` folder, it will **not** be processed by Webpack. Instead it will be copied into the build folder untouched. To reference assets in the `public` folder, you need to use a special variable called `PUBLIC_URL`. + +Inside `index.html`, you can use it like this: + +```html +<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> +``` + +Only files inside the `public` folder will be accessible by `%PUBLIC_URL%` prefix. If you need to use a file from `src` or `node_modules`, you’ll have to copy it there to explicitly specify your intention to make this file a part of the build. + +When you run `npm run build`, Create React App will substitute `%PUBLIC_URL%` with a correct absolute path so your project works even if you use client-side routing or host it at a non-root URL. + +In JavaScript code, you can use `process.env.PUBLIC_URL` for similar purposes: + +```js +render() { + // Note: this is an escape hatch and should be used sparingly! + // Normally we recommend using `import` for getting asset URLs + // as described in “Adding Images and Fonts” above this section. + return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />; +} +``` + +Keep in mind the downsides of this approach: + +- None of the files in `public` folder get post-processed or minified. +- Missing files will not be called at compilation time, and will cause 404 errors for your users. +- Result filenames won’t include content hashes so you’ll need to add query arguments or rename them every time they change. + +### When to Use the `public` Folder + +Normally we recommend importing [stylesheets](#adding-a-stylesheet), [images, and fonts](#adding-images-fonts-and-files) from JavaScript. +The `public` folder is useful as a workaround for a number of less common cases: + +- You need a file with a specific name in the build output, such as [`manifest.webmanifest`](https://developer.mozilla.org/en-US/docs/Web/Manifest). +- You have thousands of images and need to dynamically reference their paths. +- You want to include a small script like [`pace.js`](http://github.hubspot.com/pace/docs/welcome/) outside of the bundled code. +- Some library may be incompatible with Webpack and you have no other option but to include it as a `<script>` tag. + +Note that if you add a `<script>` that declares global variables, you also need to read the next section on using them. + +## Using Global Variables + +When you include a script in the HTML file that defines global variables and try to use one of these variables in the code, the linter will complain because it cannot see the definition of the variable. + +You can avoid this by reading the global variable explicitly from the `window` object, for example: + +```js +const $ = window.$; +``` + +This makes it obvious you are using a global variable intentionally rather than because of a typo. + +Alternatively, you can force the linter to ignore any line by adding `// eslint-disable-line` after it. + +## Adding Bootstrap + +You don’t have to use [reactstrap](https://reactstrap.github.io/) together with React but it is a popular library for integrating Bootstrap with React apps. If you need it, you can integrate it with Create React App by following these steps: + +Install reactstrap and Bootstrap from npm. reactstrap does not include Bootstrap CSS so this needs to be installed as well: + +```sh +npm install --save reactstrap bootstrap@4 +``` + +Alternatively you may use `yarn`: + +```sh +yarn add bootstrap@4 reactstrap +``` + +Import Bootstrap CSS and optionally Bootstrap theme CSS in the beginning of your `src/index.js` file: + +```js +import 'bootstrap/dist/css/bootstrap.css'; +// Put any other imports below so that CSS from your +// components takes precedence over default styles. +``` + +Import required reactstrap components within `src/App.js` file or your custom component files: + +```js +import { Button } from 'reactstrap'; +``` + +Now you are ready to use the imported reactstrap components within your component hierarchy defined in the render method. Here is an example [`App.js`](https://gist.githubusercontent.com/zx6658/d9f128cd57ca69e583ea2b5fea074238/raw/a56701c142d0c622eb6c20a457fbc01d708cb485/App.js) redone using reactstrap. + +### Using a Custom Theme + +> Note: this feature is available with `react-scripts@2.0.0` and higher. + +Sometimes you might need to tweak the visual styles of Bootstrap (or equivalent package).<br> +As of `react-scripts@2.0.0` you can import `.scss` files. This makes it possible to use a package's built-in Sass variables for global style preferences. + +To customize Bootstrap, create a file called `src/custom.scss` (or similar) and import the Bootstrap source stylesheet. Add any overrides _before_ the imported file(s). You can reference [Bootstrap's documentation](http://getbootstrap.com/docs/4.1/getting-started/theming/#css-variables) for the names of the available variables. + +```scss +// Override default variables before the import +$body-bg: #000; + +// Import Bootstrap and its default variables +@import '~bootstrap/scss/bootstrap.scss'; +``` + +> **Note:** You must prefix imports from `node_modules` with `~` as displayed above. + +Finally, import the newly created `.scss` file instead of the default Bootstrap `.css` in the beginning of your `src/index.js` file, for example: + +```javascript +import './custom.scss'; +``` + +## Adding Flow + +Flow is a static type checker that helps you write code with fewer bugs. Check out this [introduction to using static types in JavaScript](https://medium.com/@preethikasireddy/why-use-static-types-in-javascript-part-1-8382da1e0adb) if you are new to this concept. + +Recent versions of [Flow](https://flow.org/) work with Create React App projects out of the box. + +To add Flow to a Create React App project, follow these steps: + +1. Run `npm install --save flow-bin` (or `yarn add flow-bin`). +2. Add `"flow": "flow"` to the `scripts` section of your `package.json`. +3. Run `npm run flow init` (or `yarn flow init`) to create a [`.flowconfig` file](https://flow.org/en/docs/config/) in the root directory. +4. Add `// @flow` to any files you want to type check (for example, to `src/App.js`). + +Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors. +You can optionally use an IDE like [Nuclide](https://nuclide.io/docs/languages/flow/) for a better integrated experience. +In the future we plan to integrate it into Create React App even more closely. + +To learn more about Flow, check out [its documentation](https://flow.org/). + +## Adding a Router + +Create React App doesn't prescribe a specific routing solution, but [React Router](https://reacttraining.com/react-router/web/) is the most popular one. + +To add it, run: + +```sh +npm install --save react-router-dom +``` + +Alternatively you may use `yarn`: + +```sh +yarn add react-router-dom +``` + +To try it, delete all the code in `src/App.js` and replace it with any of the examples on its website. The [Basic Example](https://reacttraining.com/react-router/web/example/basic) is a good place to get started. + +Note that [you may need to configure your production server to support client-side routing](#serving-apps-with-client-side-routing) before deploying your app. + +## Adding Custom Environment Variables + +> Note: this feature is available with `react-scripts@0.2.3` and higher. + +Your project can consume variables declared in your environment as if they were declared locally in your JS files. By +default you will have `NODE_ENV` defined for you, and any other environment variables starting with +`REACT_APP_`. + +**The environment variables are embedded during the build time**. Since Create React App produces a static HTML/CSS/JS bundle, it can’t possibly read them at runtime. To read them at runtime, you would need to load HTML into memory on the server and replace placeholders in runtime, just like [described here](#injecting-data-from-the-server-into-the-page). Alternatively you can rebuild the app on the server anytime you change them. + +> Note: You must create custom environment variables beginning with `REACT_APP_`. Any other variables except `NODE_ENV` will be ignored to avoid accidentally [exposing a private key on the machine that could have the same name](https://github.com/facebook/create-react-app/issues/865#issuecomment-252199527). Changing any environment variables will require you to restart the development server if it is running. + +These environment variables will be defined for you on `process.env`. For example, having an environment +variable named `REACT_APP_SECRET_CODE` will be exposed in your JS as `process.env.REACT_APP_SECRET_CODE`. + +There is also a special built-in environment variable called `NODE_ENV`. You can read it from `process.env.NODE_ENV`. When you run `npm start`, it is always equal to `'development'`, when you run `npm test` it is always equal to `'test'`, and when you run `npm run build` to make a production bundle, it is always equal to `'production'`. **You cannot override `NODE_ENV` manually.** This prevents developers from accidentally deploying a slow development build to production. + +These environment variables can be useful for displaying information conditionally based on where the project is +deployed or consuming sensitive data that lives outside of version control. + +First, you need to have environment variables defined. For example, let’s say you wanted to consume a secret defined +in the environment inside a `<form>`: + +```jsx +render() { + return ( + <div> + <small>You are running this application in <b>{process.env.NODE_ENV}</b> mode.</small> + <form> + <input type="hidden" defaultValue={process.env.REACT_APP_SECRET_CODE} /> + </form> + </div> + ); +} +``` + +During the build, `process.env.REACT_APP_SECRET_CODE` will be replaced with the current value of the `REACT_APP_SECRET_CODE` environment variable. Remember that the `NODE_ENV` variable will be set for you automatically. + +When you load the app in the browser and inspect the `<input>`, you will see its value set to `abcdef`, and the bold text will show the environment provided when using `npm start`: + +```html +<div> + <small>You are running this application in <b>development</b> mode.</small> + <form> + <input type="hidden" value="abcdef" /> + </form> +</div> +``` + +The above form is looking for a variable called `REACT_APP_SECRET_CODE` from the environment. In order to consume this +value, we need to have it defined in the environment. This can be done using two ways: either in your shell or in +a `.env` file. Both of these ways are described in the next few sections. + +Having access to the `NODE_ENV` is also useful for performing actions conditionally: + +```js +if (process.env.NODE_ENV !== 'production') { + analytics.disable(); +} +``` + +When you compile the app with `npm run build`, the minification step will strip out this condition, and the resulting bundle will be smaller. + +### Referencing Environment Variables in the HTML + +> Note: this feature is available with `react-scripts@0.9.0` and higher. + +You can also access the environment variables starting with `REACT_APP_` in the `public/index.html`. For example: + +```html +<title>%REACT_APP_WEBSITE_NAME% +``` + +Note that the caveats from the above section apply: + +- Apart from a few built-in variables (`NODE_ENV` and `PUBLIC_URL`), variable names must start with `REACT_APP_` to work. +- The environment variables are injected at build time. If you need to inject them at runtime, [follow this approach instead](#generating-dynamic-meta-tags-on-the-server). + +### Adding Temporary Environment Variables In Your Shell + +Defining environment variables can vary between OSes. It’s also important to know that this manner is temporary for the +life of the shell session. + +#### Windows (cmd.exe) + +```cmd +set "REACT_APP_SECRET_CODE=abcdef" && npm start +``` + +(Note: Quotes around the variable assignment are required to avoid a trailing whitespace.) + +#### Windows (Powershell) + +```Powershell +($env:REACT_APP_SECRET_CODE = "abcdef") -and (npm start) +``` + +#### Linux, macOS (Bash) + +```bash +REACT_APP_SECRET_CODE=abcdef npm start +``` + +### Adding Development Environment Variables In `.env` + +> Note: this feature is available with `react-scripts@0.5.0` and higher. + +To define permanent environment variables, create a file called `.env` in the root of your project: + +``` +REACT_APP_SECRET_CODE=abcdef +``` + +> Note: You must create custom environment variables beginning with `REACT_APP_`. Any other variables except `NODE_ENV` will be ignored to avoid [accidentally exposing a private key on the machine that could have the same name](https://github.com/facebook/create-react-app/issues/865#issuecomment-252199527). Changing any environment variables will require you to restart the development server if it is running. + +`.env` files **should be** checked into source control (with the exclusion of `.env*.local`). + +#### What other `.env` files can be used? + +> Note: this feature is **available with `react-scripts@1.0.0` and higher**. + +- `.env`: Default. +- `.env.local`: Local overrides. **This file is loaded for all environments except test.** +- `.env.development`, `.env.test`, `.env.production`: Environment-specific settings. +- `.env.development.local`, `.env.test.local`, `.env.production.local`: Local overrides of environment-specific settings. + +Files on the left have more priority than files on the right: + +- `npm start`: `.env.development.local`, `.env.development`, `.env.local`, `.env` +- `npm run build`: `.env.production.local`, `.env.production`, `.env.local`, `.env` +- `npm test`: `.env.test.local`, `.env.test`, `.env` (note `.env.local` is missing) + +These variables will act as the defaults if the machine does not explicitly set them.
+Please refer to the [dotenv documentation](https://github.com/motdotla/dotenv) for more details. + +> Note: If you are defining environment variables for development, your CI and/or hosting platform will most likely need +> these defined as well. Consult their documentation how to do this. For example, see the documentation for [Travis CI](https://docs.travis-ci.com/user/environment-variables/) or [Heroku](https://devcenter.heroku.com/articles/config-vars). + +#### Expanding Environment Variables In `.env` + +> Note: this feature is available with `react-scripts@1.1.0` and higher. + +Expand variables already on your machine for use in your `.env` file (using [dotenv-expand](https://github.com/motdotla/dotenv-expand)). + +For example, to get the environment variable `npm_package_version`: + +``` +REACT_APP_VERSION=$npm_package_version +# also works: +# REACT_APP_VERSION=${npm_package_version} +``` + +Or expand variables local to the current `.env` file: + +``` +DOMAIN=www.example.com +REACT_APP_FOO=$DOMAIN/foo +REACT_APP_BAR=$DOMAIN/bar +``` + +## Can I Use Decorators? + +Some popular libraries use [decorators](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841) in their documentation.
+Create React App intentionally doesn’t support decorator syntax at the moment because: + +- It is an experimental proposal and is subject to change (in fact, it has already changed once, and will change again). +- Most libraries currently support only the old version of the proposal — which will never be a standard. + +However in many cases you can rewrite decorator-based code without decorators just as fine.
+Please refer to these two threads for reference: + +- [#214](https://github.com/facebook/create-react-app/issues/214) +- [#411](https://github.com/facebook/create-react-app/issues/411) + +Create React App will add decorator support when the specification advances to a stable stage. + +## Fetching Data with AJAX Requests + +React doesn't prescribe a specific approach to data fetching, but people commonly use either a library like [axios](https://github.com/axios/axios) or the [`fetch()` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) provided by the browser. + +The global `fetch` function allows you to easily make AJAX requests. It takes in a URL as an input and returns a `Promise` that resolves to a `Response` object. You can find more information about `fetch` [here](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch). + +A Promise represents the eventual result of an asynchronous operation, you can find more information about Promises [here](https://www.promisejs.org/) and [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). Both axios and `fetch()` use Promises under the hood. You can also use the [`async / await`](https://davidwalsh.name/async-await) syntax to reduce the callback nesting. + +Make sure the [`fetch()` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) are available in your target audience's browsers. +For example, support in Internet Explorer requires a [polyfill](https://github.com/facebook/create-react-app/blob/master/packages/react-app-polyfill/README.md). + +You can learn more about making AJAX requests from React components in [the FAQ entry on the React website](https://reactjs.org/docs/faq-ajax.html). + +## Integrating with an API Backend + +These tutorials will help you to integrate your app with an API backend running on another port, +using `fetch()` to access it. + +### Node + +Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/). +You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo). + +### Ruby on Rails + +Check out [this tutorial](https://www.fullstackreact.com/articles/how-to-get-create-react-app-to-work-with-your-rails-api/). +You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo-rails). + +### API Platform (PHP and Symfony) + +[API Platform](https://api-platform.com) is a framework designed to build API-driven projects. +It allows to create hypermedia and GraphQL APIs in minutes. +It is shipped with an official Progressive Web App generator as well as a dynamic administration interface, both built for Create React App. +Check out [this tutorial](https://api-platform.com/docs/distribution). + +## Proxying API Requests in Development + +> Note: this feature is available with `react-scripts@0.2.3` and higher. + +People often serve the front-end React app from the same host and port as their backend implementation.
+For example, a production setup might look like this after the app is deployed: + +``` +/ - static server returns index.html with React app +/todos - static server returns index.html with React app +/api/todos - server handles any /api/* requests using the backend implementation +``` + +Such setup is **not** required. However, if you **do** have a setup like this, it is convenient to write requests like `fetch('/api/todos')` without worrying about redirecting them to another host or port during development. + +To tell the development server to proxy any unknown requests to your API server in development, add a `proxy` field to your `package.json`, for example: + +```js + "proxy": "http://localhost:4000", +``` + +This way, when you `fetch('/api/todos')` in development, the development server will recognize that it’s not a static asset, and will proxy your request to `http://localhost:4000/api/todos` as a fallback. The development server will **only** attempt to send requests without `text/html` in its `Accept` header to the proxy. + +Conveniently, this avoids [CORS issues](http://stackoverflow.com/questions/21854516/understanding-ajax-cors-and-security-considerations) and error messages like this in development: + +``` +Fetch API cannot load http://localhost:4000/api/todos. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. +``` + +Keep in mind that `proxy` only has effect in development (with `npm start`), and it is up to you to ensure that URLs like `/api/todos` point to the right thing in production. You don’t have to use the `/api` prefix. Any unrecognized request without a `text/html` accept header will be redirected to the specified `proxy`. + +The `proxy` option supports HTTP, HTTPS and WebSocket connections.
+If the `proxy` option is **not** flexible enough for you, alternatively you can: + +- [Configure the proxy yourself](#configuring-the-proxy-manually) +- Enable CORS on your server ([here’s how to do it for Express](http://enable-cors.org/server_expressjs.html)). +- Use [environment variables](#adding-custom-environment-variables) to inject the right server host and port into your app. + +### "Invalid Host Header" Errors After Configuring Proxy + +When you enable the `proxy` option, you opt into a more strict set of host checks. This is necessary because leaving the backend open to remote hosts makes your computer vulnerable to DNS rebinding attacks. The issue is explained in [this article](https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a) and [this issue](https://github.com/webpack/webpack-dev-server/issues/887). + +This shouldn’t affect you when developing on `localhost`, but if you develop remotely like [described here](https://github.com/facebook/create-react-app/issues/2271), you will see this error in the browser after enabling the `proxy` option: + +> Invalid Host header + +To work around it, you can specify your public development host in a file called `.env.development` in the root of your project: + +``` +HOST=mypublicdevhost.com +``` + +If you restart the development server now and load the app from the specified host, it should work. + +If you are still having issues or if you’re using a more exotic environment like a cloud editor, you can bypass the host check completely by adding a line to `.env.development.local`. **Note that this is dangerous and exposes your machine to remote code execution from malicious websites:** + +``` +# NOTE: THIS IS DANGEROUS! +# It exposes your machine to attacks from the websites you visit. +DANGEROUSLY_DISABLE_HOST_CHECK=true +``` + +We don’t recommend this approach. + +### Configuring the Proxy Manually + +> Note: this feature is available with `react-scripts@2.0.0` and higher. + +If the `proxy` option is **not** flexible enough for you, you can get direct access to the Express app instance and hook up your own proxy middleware. + +You can use this feature in conjunction with the `proxy` property in `package.json`, but it is recommended you consolidate all of your logic into `src/setupProxy.js`. + +First, install `http-proxy-middleware` using npm or Yarn: + +```bash +$ npm install http-proxy-middleware --save +$ # or +$ yarn add http-proxy-middleware +``` + +Next, create `src/setupProxy.js` and place the following contents in it: + +```js +const proxy = require('http-proxy-middleware'); + +module.exports = function(app) { + // ... +}; +``` + +You can now register proxies as you wish! Here's an example using the above `http-proxy-middleware`: + +```js +const proxy = require('http-proxy-middleware'); + +module.exports = function(app) { + app.use(proxy('/api', { target: 'http://localhost:5000/' })); +}; +``` + +> **Note:** You do not need to import this file anywhere. It is automatically registered when you start the development server. + +> **Note:** This file only supports Node's JavaScript syntax. Be sure to only use supported language features (i.e. no support for Flow, ES Modules, etc). + +## Using HTTPS in Development + +> Note: this feature is available with `react-scripts@0.4.0` and higher. + +You may require the dev server to serve pages over HTTPS. One particular case where this could be useful is when using [the "proxy" feature](#proxying-api-requests-in-development) to proxy requests to an API server when that API server is itself serving HTTPS. + +To do this, set the `HTTPS` environment variable to `true`, then start the dev server as usual with `npm start`: + +#### Windows (cmd.exe) + +```cmd +set HTTPS=true&&npm start +``` + +(Note: the lack of whitespace is intentional.) + +#### Windows (Powershell) + +```Powershell +($env:HTTPS = $true) -and (npm start) +``` + +#### Linux, macOS (Bash) + +```bash +HTTPS=true npm start +``` + +Note that the server will use a self-signed certificate, so your web browser will almost definitely display a warning upon accessing the page. + +## Generating Dynamic `` Tags on the Server + +Since Create React App doesn’t support server rendering, you might be wondering how to make `` tags dynamic and reflect the current URL. To solve this, we recommend to add placeholders into the HTML, like this: + +```html + + + + + +``` + +Then, on the server, regardless of the backend you use, you can read `index.html` into memory and replace `__OG_TITLE__`, `__OG_DESCRIPTION__`, and any other placeholders with values depending on the current URL. Just make sure to sanitize and escape the interpolated values so that they are safe to embed into HTML! + +If you use a Node server, you can even share the route matching logic between the client and the server. However duplicating it also works fine in simple cases. + +## Pre-Rendering into Static HTML Files + +If you’re hosting your `build` with a static hosting provider you can use [react-snapshot](https://www.npmjs.com/package/react-snapshot) or [react-snap](https://github.com/stereobooster/react-snap) to generate HTML pages for each route, or relative link, in your application. These pages will then seamlessly become active, or “hydrated”, when the JavaScript bundle has loaded. + +There are also opportunities to use this outside of static hosting, to take the pressure off the server when generating and caching routes. + +The primary benefit of pre-rendering is that you get the core content of each page _with_ the HTML payload—regardless of whether or not your JavaScript bundle successfully downloads. It also increases the likelihood that each route of your application will be picked up by search engines. + +You can read more about [zero-configuration pre-rendering (also called snapshotting) here](https://medium.com/superhighfives/an-almost-static-stack-6df0a2791319). + +## Injecting Data from the Server into the Page + +Similarly to the previous section, you can leave some placeholders in the HTML that inject global variables, for example: + +```js + + + + +``` + +Then, on the server, you can replace `__SERVER_DATA__` with a JSON of real data right before sending the response. The client code can then read `window.SERVER_DATA` to use it. **Make sure to [sanitize the JSON before sending it to the client](https://medium.com/node-security/the-most-common-xss-vulnerability-in-react-js-applications-2bdffbcc1fa0) as it makes your app vulnerable to XSS attacks.** + +## Running Tests + +> Note: this feature is available with `react-scripts@0.3.0` and higher.
+ +> [Read the migration guide to learn how to enable it in older projects!](https://github.com/facebook/create-react-app/blob/master/CHANGELOG.md#migrating-from-023-to-030) + +Create React App uses [Jest](https://facebook.github.io/jest/) as its test runner. To prepare for this integration, we did a [major revamp](https://facebook.github.io/jest/blog/2016/09/01/jest-15.html) of Jest so if you heard bad things about it years ago, give it another try. + +Jest is a Node-based runner. This means that the tests always run in a Node environment and not in a real browser. This lets us enable fast iteration speed and prevent flakiness. + +While Jest provides browser globals such as `window` thanks to [jsdom](https://github.com/tmpvar/jsdom), they are only approximations of the real browser behavior. Jest is intended to be used for unit tests of your logic and your components rather than the DOM quirks. + +We recommend that you use a separate tool for browser end-to-end tests if you need them. They are beyond the scope of Create React App. + +### Filename Conventions + +Jest will look for test files with any of the following popular naming conventions: + +- Files with `.js` suffix in `__tests__` folders. +- Files with `.test.js` suffix. +- Files with `.spec.js` suffix. + +The `.test.js` / `.spec.js` files (or the `__tests__` folders) can be located at any depth under the `src` top level folder. + +We recommend to put the test files (or `__tests__` folders) next to the code they are testing so that relative imports appear shorter. For example, if `App.test.js` and `App.js` are in the same folder, the test just needs to `import App from './App'` instead of a long relative path. Colocation also helps find tests more quickly in larger projects. + +### Command Line Interface + +When you run `npm test`, Jest will launch in the watch mode. Every time you save a file, it will re-run the tests, just like `npm start` recompiles the code. + +The watcher includes an interactive command-line interface with the ability to run all tests, or focus on a search pattern. It is designed this way so that you can keep it open and enjoy fast re-runs. You can learn the commands from the “Watch Usage” note that the watcher prints after every run: + +![Jest watch mode](http://facebook.github.io/jest/img/blog/15-watch.gif) + +### Version Control Integration + +By default, when you run `npm test`, Jest will only run the tests related to files changed since the last commit. This is an optimization designed to make your tests run fast regardless of how many tests you have. However it assumes that you don’t often commit the code that doesn’t pass the tests. + +Jest will always explicitly mention that it only ran tests related to the files changed since the last commit. You can also press `a` in the watch mode to force Jest to run all tests. + +Jest will always run all tests on a [continuous integration](#continuous-integration) server or if the project is not inside a Git or Mercurial repository. + +### Writing Tests + +To create tests, add `it()` (or `test()`) blocks with the name of the test and its code. You may optionally wrap them in `describe()` blocks for logical grouping but this is neither required nor recommended. + +Jest provides a built-in `expect()` global function for making assertions. A basic test could look like this: + +```js +import sum from './sum'; + +it('sums numbers', () => { + expect(sum(1, 2)).toEqual(3); + expect(sum(2, 2)).toEqual(4); +}); +``` + +All `expect()` matchers supported by Jest are [extensively documented here](https://facebook.github.io/jest/docs/en/expect.html#content).
+You can also use [`jest.fn()` and `expect(fn).toBeCalled()`](https://facebook.github.io/jest/docs/en/expect.html#tohavebeencalled) to create “spies” or mock functions. + +### Testing Components + +There is a broad spectrum of component testing techniques. They range from a “smoke test” verifying that a component renders without throwing, to shallow rendering and testing some of the output, to full rendering and testing component lifecycle and state changes. + +Different projects choose different testing tradeoffs based on how often components change, and how much logic they contain. If you haven’t decided on a testing strategy yet, we recommend that you start with creating simple smoke tests for your components: + +```js +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); +}); +``` + +This test mounts a component and makes sure that it didn’t throw during rendering. Tests like this provide a lot of value with very little effort so they are great as a starting point, and this is the test you will find in `src/App.test.js`. + +When you encounter bugs caused by changing components, you will gain a deeper insight into which parts of them are worth testing in your application. This might be a good time to introduce more specific tests asserting specific expected output or behavior. + +If you’d like to test components in isolation from the child components they render, we recommend using [`shallow()` rendering API](http://airbnb.io/enzyme/docs/api/shallow.html) from [Enzyme](http://airbnb.io/enzyme/). To install it, run: + +```sh +npm install --save enzyme enzyme-adapter-react-16 react-test-renderer +``` + +Alternatively you may use `yarn`: + +```sh +yarn add enzyme enzyme-adapter-react-16 react-test-renderer +``` + +As of Enzyme 3, you will need to install Enzyme along with an Adapter corresponding to the version of React you are using. (The examples above use the adapter for React 16.) + +The adapter will also need to be configured in your [global setup file](#initializing-test-environment): + +#### `src/setupTests.js` + +```js +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; + +configure({ adapter: new Adapter() }); +``` + +> Note: Keep in mind that if you decide to "eject" before creating `src/setupTests.js`, the resulting `package.json` file won't contain any reference to it. [Read here](#initializing-test-environment) to learn how to add this after ejecting. + +Now you can write a smoke test with it: + +```js +import React from 'react'; +import { shallow } from 'enzyme'; +import App from './App'; + +it('renders without crashing', () => { + shallow(); +}); +``` + +Unlike the previous smoke test using `ReactDOM.render()`, this test only renders `` and doesn’t go deeper. For example, even if `` itself renders a ` + + + + + ) + } +} + +export default AddFeature; \ No newline at end of file diff --git a/src/components/AddFeature/package.json b/src/components/AddFeature/package.json new file mode 100644 index 0000000..3f1c230 --- /dev/null +++ b/src/components/AddFeature/package.json @@ -0,0 +1,6 @@ +{ + "name": "AddFeature", + "version": "0.0.0", + "private": true, + "main": "./AddFeature.js" +} \ No newline at end of file diff --git a/src/components/AdmTree/AdmTree.css b/src/components/AdmTree/AdmTree.css new file mode 100644 index 0000000..b559efa --- /dev/null +++ b/src/components/AdmTree/AdmTree.css @@ -0,0 +1,101 @@ +.adm-tree { + font-size: 12px; +} + +.adm-tree-checkbox-container { + margin-left: 10px; + margin-bottom: 5px; + margin-top: 5px; +} + +#adm-tree-container { + font-size: 12px; + /*height: 220px;*/ + height: 440px; + overflow: auto; + padding: 0px !important; +} + +.checkbox-opt { + font-size: 11px; + padding: 0px !important; +} + +.ant-tree-child-tree > li:first-child { + padding-top: 0px !important; +} + +.ant-tree li { + margin: 0; + padding: 0px 0 !important; + white-space: nowrap; + list-style: none; + outline: 0; +} + +.adm-tree-checkbox-opt-container { + padding-left: 0px !important +} + +.adm-dropdown-container { + z-index: 50; + position: absolute; + max-height: 350px; + border-radius: 5px; + background-color: rgba(0,0,0,0.5); + /*background-color: #ebebeb;*/ + color: #ffffff; + /*color: #000000;*/ +} + +.adm-dropdown-title { + font-size: 14px; + margin-top: 0px; + margin-left: 10px; + margin-bottom: 5px; + font-weight: bold; + width: 200px; +} + +.adm-dropdown-close { + float: right; + font-size: 18px; + font-weight: bold; + margin-left: 0px; + margin-right: 10px; + margin-top: -5px; + cursor: pointer; +} + +.adm-dropdown-body { + max-height: 200px; + overflow: auto; + margin: 5px; + text-align: left; +} + +.adm-dropdown-list { + cursor: pointer; + font-size: 14px; + padding: 5px; + margin-left: 0px; + text-align: left; +} + +.adm-dropdown-icon { + margin-right: 5px; +} + +.adm-dropdown-list:hover { + background-color: rgba(0,0,0,0.25); +} + +.filter-coverage-container { + margin-left: 25px; + margin-top: 5px; + margin-bottom: 5px; + margin-right: 25px; + color: blue; + cursor: pointer; + /*float: right;*/ +} \ No newline at end of file diff --git a/src/components/AdmTree/AdmTree.js b/src/components/AdmTree/AdmTree.js new file mode 100644 index 0000000..6971c33 --- /dev/null +++ b/src/components/AdmTree/AdmTree.js @@ -0,0 +1,734 @@ +import React, { Component, Fragment } from 'react'; +import { Form, FormGroup, Label, Input, Row, Col, ListGroup, ListGroupItem } from 'reactstrap'; +// Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +import { Tree, Input as SearchInput, Menu, Dropdown } from 'antd'; +import { API_KOMINFO_GET_PROV, API_KOMINFO_GET_KABKOT, API_KOMINFO_GET_KEC, API_KOMINFO_GET_DESA } from '../../const/ApiConst.js'; +import Loader from 'react-loader-spinner' +import "react-loader-spinner/dist/loader/css/react-spinner-loader.css" +import './AdmTree.css' +import '../../assets/css/customscroll.css' +import { findWhere } from 'underscore'; +import { opt2G, opt3G, opt4G, netQuality2G, netQuality3G, netQuality4G, bts, mukim } from '../../const/Kominfo.js'; +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +// npm install --save-dev @iconify/react @iconify/icons-mdi +import { Icon, InlineIcon } from '@iconify/react'; +import mapMarker from '@iconify/icons-mdi/map-marker'; + +const { TreeNode } = Tree; +const { Search } = SearchInput; + +const menu = ( + + 1st menu item + 2nd menu item + 3rd menu item + +); + + + +class AdmTree extends Component { + + constructor(props) { + super(props); + this.state = { + treeData: [], + checkedKeys: [], + isReady: false, + admDropdownOpen: false, + admDropdownVisible: false, + allCheckNodes: [], + rightClickNodeTreeItem: { + pageX: "", + pageY: "", + id: "", + dropdownTitle: "" + }, + searchAdmValue: "" + } + } + + componentDidMount() { + this.getProv(); + } + + getProv = async () => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_KOMINFO_GET_PROV, param).then(response => response.json()).then(res => res) + // console.log(result); + if (result.data){ + let treeData = []; + let checkedKeys = []; + let allProv = []; + let checkNodes = []; + for(let i=0; i < result.data.length; i++) { + treeData.push({ + title: result.data[i].PROV, + key: i, + group: 'prov' + }); + checkedKeys.push(i); + allProv.push(i); + let checkNode_ = { + props: { + dataRef: { + title: result.data[i].PROV, + key: i, + group: 'prov' + } + } + } + checkNodes.push(checkNode_); + } + this.setState({ + // treeData: treeData, + // checkedKeys: checkedKeys, + allProv: allProv, + allCheckNodes: checkNodes, + isReady: true}); + // console.log('checkNodes', checkNodes); + this.props.initAdmTree(treeData, checkNodes); + this.props.setCheckKeysAdm(checkedKeys); + this.props.setAllProv(allProv, true); + this.props.setToggleCheckAllValue(true); + + } else { + + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + toast.warn(err.message.toString()); + } + } + + getChild = async (title, group, prov, kabkot, kec, childKey) => { + + let URL = ''; + let childGroup = ''; + let childTitle = ''; + let bodyParam = {}; + + if (group == 'prov') { // buat nyari kabkot + URL = API_KOMINFO_GET_KABKOT; + childGroup = 'kabkot'; + childTitle = 'KAB_KOT'; + bodyParam = { + [group]: title + }; + } + else if (group == 'kabkot') { // buat nyari kecamatan + URL = API_KOMINFO_GET_KEC; + childGroup = 'kec'; + childTitle = 'KEC'; + bodyParam = { + [group]: title, + prov: prov + }; + } + else if (group == 'kec') { // buat nyari desa + URL = API_KOMINFO_GET_DESA; + childGroup = 'desa'; + childTitle = 'KEL_DES'; + bodyParam = { + [group]: title, + prov: prov, + kabkot, kabkot + }; + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + console.log(URL); + + const result = await fetch(URL, param).then(response => response.json()).then(res => res) + console.log(result); + if (result.data){ + let treeData = []; + if (group == 'prov') { // buat nyari kabkot + for(let i=0; i < result.data.length; i++) { + treeData.push({ + title: result.data[i][childTitle], + key: childKey+'-'+i, + group: childGroup, + prov: result.data[i].PROV, + kabkot: result.data[i].KAB_KOT + }); + } + } + else if (group == 'kabkot') { // buat nyari kecamatan + for(let i=0; i < result.data.length; i++) { + treeData.push({ + title: result.data[i][childTitle], + key: childKey+'-'+i, + group: childGroup, + prov: result.data[i].PROV, + kabkot: result.data[i].KAB_KOT, + kec: result.data[i].KEC + }); + } + } + else if (group == 'kec') { // buat nyari desa + for(let i=0; i < result.data.length; i++) { + treeData.push({ + title: result.data[i][childTitle], + key: childKey+'-'+i, + group: childGroup, + prov: result.data[i].PROV, + kabkot: result.data[i].KAB_KOT, + kec: result.data[i].KEC, + desa: result.data[i].KEL_DES + }); + } + } + + + // this.setState({treeData: treeData, isReady: true}); + // console.log('getChild func', treeData); + return treeData; + } else { + + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + toast.warn(err.message.toString()); + } + } + + /*onLoadData = treeNode => + new Promise(resolve => { + if (treeNode.props.children) { + resolve(); + return; + } + setTimeout(() => { + console.log('treeNode', treeNode); + let title = treeNode.props.dataRef.title; + let group = treeNode.props.dataRef.group; + let childKey = treeNode.props.eventKey; + let getChild = this.getChild(title, group, childKey); + console.log('getChild',getChild); + + treeNode.props.dataRef.children = [ + { title: 'Child Node', key: `${treeNode.props.eventKey}-0` }, + { title: 'Child Node', key: `${treeNode.props.eventKey}-1` }, + ]; + this.setState({ + treeData: [...this.state.treeData], + }); + resolve(); + }, 1000); + });*/ + + onLoadData = async (treeNode) => { + console.log('onLoadData treeNode',treeNode); + if (treeNode.props.children) { + return; + } + let title = treeNode.props.dataRef.title; + let group = treeNode.props.dataRef.group; + let childKey = treeNode.props.eventKey; + let getChild = null; + + // let getChild = await this.getChild(title, group, prov, kabkot, kec, childKey); + if (group == 'prov') { + getChild = await this.getChild(title, group, group, null, null, childKey); + } + else if (group == 'kabkot') { + let prov = treeNode.props.dataRef.prov; + getChild = await this.getChild(title, group, prov, group, null, childKey); + } + else if (group == 'kec') { + let prov = treeNode.props.dataRef.prov; + let kabkot = treeNode.props.dataRef.kabkot; + getChild = await this.getChild(title, group, prov, kabkot, group, childKey); + } + + // let getChild = await this.getChild(title, group, childKey); + console.log('getChild',getChild); + + if (getChild.length > 0) { + let treeDataChild = []; + let treeDataChildKeys = []; + + if (group == 'prov') { // buat nyari kabkot + this.props.setExpandedTree('prov'); + for (let i=0; i < getChild.length; i++) { + treeDataChild.push({ + title: getChild[i].title, + key: getChild[i].key, + group: getChild[i].group, + prov: getChild[i].prov, + kabkot: getChild[i].kabkot + }); + } + } + else if (group == 'kabkot') { // buat nyari kec + this.props.setExpandedTree('kab'); + for (let i=0; i < getChild.length; i++) { + treeDataChild.push({ + title: getChild[i].title, + key: getChild[i].key, + group: getChild[i].group, + prov: getChild[i].prov, + kabkot: getChild[i].kabkot, + kec: getChild[i].kec + }); + } + } + else if (group == 'kec') { // buat nyari desa / kel_des + this.props.setExpandedTree('kec'); + for (let i=0; i < getChild.length; i++) { + treeDataChild.push({ + title: getChild[i].title, + key: getChild[i].key, + group: getChild[i].group, + prov: getChild[i].prov, + kabkot: getChild[i].kabkot, + kec: getChild[i].kec, + desa: getChild[i].desa, + isLeaf: true + }); + } + } + + if (treeNode.props.checked) { + for (let i=0; i < getChild.length; i++) { + treeDataChildKeys.push(getChild[i].key); + } + this.props.setAdmTreeData(treeNode, treeDataChild); + this.props.appendCheckedKeysAdm(treeDataChildKeys, treeNode.props.children) + } + else { + this.props.setAdmTreeData(treeNode, treeDataChild); + this.props.appendCheckedKeysAdm(treeDataChildKeys, []) + } + + } + } + + onInitData = (loadedKeys, e) => { + console.log('onInitData', e); + } + + onCheckAdmTree = (checkedKeys, e) => { + console.log('onCheckAdmTree', checkedKeys); + console.log('onCheckAdmTree checkedNodes', e.checkedNodes); + this.props.setCheckKeysAdm(checkedKeys); + this.props.setCheckNodesAdm(e.checkedNodes); + + // this.setState({ checkedKeysAdm, checkedNodesAdm: e.checkedNodes }, () => { + // let getCheckedValue = this.getCheckedValue(); + // console.log('getCheckedValue', getCheckedValue); + // }); + }; + + onCheckAllAdmTree = () => { + + /*const { checkedKeys, allProv } = this.state; + const totalProv = 34; + + if (checkedKeys.length > 0) { + this.setState({checkedKeys: [], toggleCheckAllValue: false}) + } + else if (checkedKeys.length == 0) { + this.setState({checkedKeys: allProv, toggleCheckAllValue: true}) + }*/ + const { checkedKeysAdm, checkedKeysAdmTemp, toggleCheckAllValue, allProv } = this.props; + const { allCheckNodes } = this.state; + this.props.setToggleCheckAllValue(!toggleCheckAllValue); + } + + onCheckOpt = (checkedKeys, e) => { + const { mode } = this.props; + this.props.setCheckKeysOpt(mode, checkedKeys); + } + + onCheckQty = (checkedKeys) => { + const { mode } = this.props; + this.props.setCheckKeysQty(mode, checkedKeys); + } + + onCheckBts = (checkedKeys) => { + this.props.setCheckKeysBts(checkedKeys); + } + + /*onRightClickAdmTree = (callback) => { + console.log('onRightClickAdmTree', callback); + this.admDropdownToggle(); + }*/ + + // tree right-click events on the list + onRightClickAdmTree = e => { + e.event.persist() + console.log('onRightClickAdmTree', e); + this.setState({ + rightClickNodeTreeItem: { + pageX: e.event.pageX, + pageY: e.event.pageY, + id: e.node.props.dataRef.key, + dropdownTitle: e.node.props.dataRef.title + } + }); + }; + + closeAdmDropdown = () => { + this.setState({ + rightClickNodeTreeItem: { + pageX: "", + pageY: "", + id: "", + dropdownTitle: "" + } + }) + } + + getNodeTreeRightClickMenu = () => { + const { pageX, pageY, id, dropdownTitle } = { ...this.state.rightClickNodeTreeItem }; + // const tmpStyle = { + // position: "absolute", + // left: `${pageX - 220}px`, + // top: `${pageY - 102}px` + // }; + const tmpStyle = { + position:"absolute", + left: `${pageX - 50}px`, + top: `${pageY - 80}px`, + zIndex:10000, + width:"200px", + background:"#ededed", + borderRadius:"5px" + } + const menu = ( +
+
+ x +
{ dropdownTitle }
+
+
Go to map
+
+
+
+ ); + return this.state.rightClickNodeTreeItem.dropdownTitle == "" ? "" : menu; + }; + + getCheckedValue = () => { + const { treeData, checkedKeys } = this.state; + let checkedData = []; + + for(let i=0; i < checkedKeys.length; i++) { + let item = findWhere(treeData, {key: parseInt(checkedKeys[i])}); + checkedData.push(item); + } + return checkedData; + } + + renderCheckboxOpt = () => { + let checkboxes = []; + const { mode, checkedKeys2GOpt, checkedKeys3GOpt, checkedKeys4GOpt} = this.props; + + if (mode == '2G') { + // checkboxes = opt2G; + return ( + this.onCheckOpt(checkedKeys2GOpt, e)} + > + {this.renderTreeNodes(opt2G)} + + ) + } + else if (mode == '3G') { + // checkboxes = opt3G; + return ( + this.onCheckOpt(checkedKeys3GOpt)} + > + {this.renderTreeNodes(opt3G)} + + ) + } + else { + // checkboxes = opt4G; + return ( + this.onCheckOpt(checkedKeys4GOpt)} + > + {this.renderTreeNodes(opt4G)} + + ) + } + } + + onExpand = expandedKeys => { + // console.log('onExpand', expandedKeys); + const {closeChart} = this.props + console.log("length", expandedKeys.length) + console.log("closeChart", closeChart) + if(expandedKeys.length === 1){ + if(closeChart) { + document.getElementById('adm-tree-container').style.height = '320px' + } else { + document.getElementById('adm-tree-container').style.height = '120px' + } + } else { + if(closeChart) { + document.getElementById('adm-tree-container').style.height = '440px' + } else { + document.getElementById('adm-tree-container').style.height = '220px' + } + } + }; + + renderCheckboxQuality = () => { + let checkboxes = []; + const { mode, checkedKeys2GQty, checkedKeys3GQty, checkedKeys4GQty } = this.props; + + if (mode == '2G') { + // checkboxes = netQuality2G; + return ( + this.onCheckQty(checkedKeys2GQty)} + > + {this.renderTreeNodes(netQuality2G)} + + ) + } + else if (mode == '3G') { + // checkboxes = netQuality3G; + return ( + this.onCheckQty(checkedKeys3GQty)} + > + {this.renderTreeNodes(netQuality3G)} + + ) + } + else { + // checkboxes = netQuality4G; + return ( + this.onCheckQty(checkedKeys4GQty)} + > + {this.renderTreeNodes(netQuality4G)} + + ) + } + } + + renderCheckboxBts = () => { + const { checkedKeysBts } = this.props; + return ( + this.onCheckBts(checkedKeysBts)} + > + {this.renderTreeNodes(bts)} + + ) + } + + + /*renderTreeNodes = data => + data.map(item => { + if (item.children) { + return ( + + {this.renderTreeNodes(item.children)} + + ); + } + return ; + })*/ + + renderTreeNodes = data => + data.map(item => { + if (item.children) { + return ( + + + {this.renderTreeNodes(item.children)} + + + ); + } + return ( + + + + ); + }) + + admDropdownToggle = () => { + // this.setState({admDropdownOpen: !this.state.admDropdownOpen}) + this.setState({admDropdownVisible: !this.state.admDropdownVisible}); + } + + admMenu = () => { + const menu = ( + + 1st menu item + 2nd menu item + 3rd menu item + + ); + + return menu; + } + + goToLocation = () => { + + } + + onChangeSearchAdm = (e) => { + const { value } = e.target; + this.setState({searchAdmValue: value}, () => console.log('searchAdmValue:', this.state.searchAdmValue)); + } + + renderCheckboxMukim = () => { + const { checkedKeysMukim } = this.props; + return ( + this.onCheckMukim(checkedKeysMukim)} + > + {this.renderTreeNodes(mukim)} + + ) + } + + onCheckMukim = (checkedKeys) => { + this.props.setCheckKeysMukim(checkedKeys); + } + + renderFilterCoverage = () => { + return
this.filterCoverage()}>Filter Coverage
+ } + + filterCoverage = () => { + this.props.toggleModalOptions(); + } + + render() { + const { isReady, checkedKeys, admDropdownOpen, admDropdownVisible, searchAdmValue } = this.state; + const { admTreeData, checkedKeysAdm, toggleCheckAllValue } = this.props; + if (!isReady) { + return (
+ +
) + } + + return ( + + + {/*This is future kominfo feature*/} + + + { this.renderCheckboxMukim() } + + + { this.renderCheckboxBts() } + + + {/*end This is future kominfo feature*/} + + + + { this.renderCheckboxOpt() } + + + { this.renderCheckboxQuality() } + + + + {/*
+ { this.renderCheckboxQuality() } +
*/} + {/* + + { this.renderCheckboxBts() } + + */} + + {/*This is future kominfo feature*/} + + + { this.renderFilterCoverage() } + + + {/*end This is future kominfo feature*/} +
+ + {/*This is future kominfo feature*/} + + {/*end This is future kominfo feature*/} + +
+ + + +
+
+
+ this.onCheckAdmTree(checkedKeys, e)} + onRightClick={this.onRightClickAdmTree} + onSelect={this.closeAdmDropdown} + > + {/* this.admDropdownToggle()}>*/} + {this.renderTreeNodes(admTreeData)} + {/**/} + + {this.getNodeTreeRightClickMenu()} +
+
+ ) + } +} + +export default AdmTree; \ No newline at end of file diff --git a/src/components/AdmTree/AdmTree_backup.js b/src/components/AdmTree/AdmTree_backup.js new file mode 100644 index 0000000..01e934b --- /dev/null +++ b/src/components/AdmTree/AdmTree_backup.js @@ -0,0 +1,544 @@ +import React, { Component, Fragment } from 'react'; +import { Form, FormGroup, Label, Input, Row, Col } from 'reactstrap'; +import { Tree, Input as SearchInput } from 'antd'; +import { API_KOMINFO_GET_PROV, API_KOMINFO_GET_KABKOT, API_KOMINFO_GET_KEC, API_KOMINFO_GET_DESA } from '../../const/ApiConst.js'; +import Loader from 'react-loader-spinner' +import "react-loader-spinner/dist/loader/css/react-spinner-loader.css" +import './AdmTree.css' +import '../../assets/css/customscroll.css' +import { findWhere } from 'underscore'; +import { opt2G, opt3G, opt4G, netQuality2G, netQuality3G, netQuality4G } from '../../const/Kominfo.js'; +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; + +const { TreeNode } = Tree; +const { Search } = SearchInput; + + + +class AdmTree extends Component { + + constructor(props) { + super(props); + this.state = { + treeData: [], + checkedKeys: [], + isReady: false + } + } + + componentDidMount() { + this.getProv(); + } + + getProv = async () => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_KOMINFO_GET_PROV, param).then(response => response.json()).then(res => res) + // console.log(result); + if (result.data){ + let treeData = []; + let checkedKeys = []; + let allProv = []; + let checkNodes = []; + for(let i=0; i < result.data.length; i++) { + treeData.push({ + title: result.data[i].PROV, + key: i, + group: 'prov' + }); + checkedKeys.push(i); + allProv.push(i); + let checkNode_ = { + props: { + dataRef: { + title: result.data[i].PROV, + key: i, + group: 'prov' + } + } + } + checkNodes.push(checkNode_); + } + this.setState({ + // treeData: treeData, + // checkedKeys: checkedKeys, + allProv: allProv, + isReady: true}); + // console.log('checkNodes', checkNodes); + this.props.initAdmTree(treeData, checkNodes); + this.props.setCheckKeysAdm(checkedKeys); + this.props.setAllProv(allProv, true); + this.props.setToggleCheckAllValue(true); + + } else { + + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + toast.warn(err.message.toString()); + } + } + + getChild = async (title, group, prov, kabkot, kec, childKey) => { + + let URL = ''; + let childGroup = ''; + let childTitle = ''; + let bodyParam = {}; + + if (group == 'prov') { // buat nyari kabkot + URL = API_KOMINFO_GET_KABKOT; + childGroup = 'kabkot'; + childTitle = 'KAB_KOT'; + bodyParam = { + [group]: title + }; + } + else if (group == 'kabkot') { // buat nyari kecamatan + URL = API_KOMINFO_GET_KEC; + childGroup = 'kec'; + childTitle = 'KEC'; + bodyParam = { + [group]: title, + prov: prov + }; + } + else if (group == 'kec') { // buat nyari desa + URL = API_KOMINFO_GET_DESA; + childGroup = 'desa'; + childTitle = 'KEL_DES'; + bodyParam = { + [group]: title, + prov: prov, + kabkot, kabkot + }; + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + console.log(URL); + + const result = await fetch(URL, param).then(response => response.json()).then(res => res) + console.log(result); + if (result.data){ + let treeData = []; + if (group == 'prov') { // buat nyari kabkot + for(let i=0; i < result.data.length; i++) { + treeData.push({ + title: result.data[i][childTitle], + key: childKey+'-'+i, + group: childGroup, + prov: result.data[i].PROV, + kabkot: result.data[i].KAB_KOT + }); + } + } + else if (group == 'kabkot') { // buat nyari kecamatan + for(let i=0; i < result.data.length; i++) { + treeData.push({ + title: result.data[i][childTitle], + key: childKey+'-'+i, + group: childGroup, + prov: result.data[i].PROV, + kabkot: result.data[i].KAB_KOT, + kec: result.data[i].KEC + }); + } + } + else if (group == 'kec') { // buat nyari desa + for(let i=0; i < result.data.length; i++) { + treeData.push({ + title: result.data[i][childTitle], + key: childKey+'-'+i, + group: childGroup, + prov: result.data[i].PROV, + kabkot: result.data[i].KAB_KOT, + kec: result.data[i].KEC, + desa: result.data[i].KEL_DES + }); + } + } + + + // this.setState({treeData: treeData, isReady: true}); + // console.log('getChild func', treeData); + return treeData; + } else { + + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + toast.warn(err.message.toString()); + } + } + + /*onLoadData = treeNode => + new Promise(resolve => { + if (treeNode.props.children) { + resolve(); + return; + } + setTimeout(() => { + console.log('treeNode', treeNode); + let title = treeNode.props.dataRef.title; + let group = treeNode.props.dataRef.group; + let childKey = treeNode.props.eventKey; + let getChild = this.getChild(title, group, childKey); + console.log('getChild',getChild); + + treeNode.props.dataRef.children = [ + { title: 'Child Node', key: `${treeNode.props.eventKey}-0` }, + { title: 'Child Node', key: `${treeNode.props.eventKey}-1` }, + ]; + this.setState({ + treeData: [...this.state.treeData], + }); + resolve(); + }, 1000); + });*/ + + onLoadData = async (treeNode) => { + console.log('onLoadData treeNode',treeNode); + if (treeNode.props.children) { + return; + } + let title = treeNode.props.dataRef.title; + let group = treeNode.props.dataRef.group; + let childKey = treeNode.props.eventKey; + let getChild = null; + + // let getChild = await this.getChild(title, group, prov, kabkot, kec, childKey); + if (group == 'prov') { + getChild = await this.getChild(title, group, group, null, null, childKey); + } + else if (group == 'kabkot') { + let prov = treeNode.props.dataRef.prov; + getChild = await this.getChild(title, group, prov, group, null, childKey); + } + else if (group == 'kec') { + let prov = treeNode.props.dataRef.prov; + let kabkot = treeNode.props.dataRef.kabkot; + getChild = await this.getChild(title, group, prov, kabkot, group, childKey); + } + + // let getChild = await this.getChild(title, group, childKey); + console.log('getChild',getChild); + + if (getChild.length > 0) { + let treeDataChild = []; + let treeDataChildKeys = []; + + if (group == 'prov') { // buat nyari kabkot + this.props.setExpandedTree('prov'); + for (let i=0; i < getChild.length; i++) { + treeDataChild.push({ + title: getChild[i].title, + key: getChild[i].key, + group: getChild[i].group, + prov: getChild[i].prov, + kabkot: getChild[i].kabkot + }); + } + } + else if (group == 'kabkot') { // buat nyari kec + this.props.setExpandedTree('kab'); + for (let i=0; i < getChild.length; i++) { + treeDataChild.push({ + title: getChild[i].title, + key: getChild[i].key, + group: getChild[i].group, + prov: getChild[i].prov, + kabkot: getChild[i].kabkot, + kec: getChild[i].kec + }); + } + } + else if (group == 'kec') { // buat nyari desa / kel_des + this.props.setExpandedTree('kec'); + for (let i=0; i < getChild.length; i++) { + treeDataChild.push({ + title: getChild[i].title, + key: getChild[i].key, + group: getChild[i].group, + prov: getChild[i].prov, + kabkot: getChild[i].kabkot, + kec: getChild[i].kec, + desa: getChild[i].desa, + isLeaf: true + }); + } + } + + if (treeNode.props.checked) { + for (let i=0; i < getChild.length; i++) { + treeDataChildKeys.push(getChild[i].key); + } + this.props.setAdmTreeData(treeNode, treeDataChild); + this.props.appendCheckedKeysAdm(treeDataChildKeys, treeNode.props.children) + } + else { + this.props.setAdmTreeData(treeNode, treeDataChild); + this.props.appendCheckedKeysAdm(treeDataChildKeys, []) + } + + } + } + + onInitData = (loadedKeys, e) => { + console.log('onInitData', e); + } + + onCheckAdmTree = (checkedKeys, e) => { + console.log('onCheckAdmTree', checkedKeys); + console.log('onCheckAdmTree checkedNodes', e.checkedNodes); + this.props.setCheckKeysAdm(checkedKeys); + this.props.setCheckNodesAdm(e.checkedNodes); + + // this.setState({ checkedKeysAdm, checkedNodesAdm: e.checkedNodes }, () => { + // let getCheckedValue = this.getCheckedValue(); + // console.log('getCheckedValue', getCheckedValue); + // }); + }; + + onCheckAllAdmTree = () => { + + /*const { checkedKeys, allProv } = this.state; + const totalProv = 34; + + if (checkedKeys.length > 0) { + this.setState({checkedKeys: [], toggleCheckAllValue: false}) + } + else if (checkedKeys.length == 0) { + this.setState({checkedKeys: allProv, toggleCheckAllValue: true}) + }*/ + const { checkedKeysAdm, checkedKeysAdmTemp, toggleCheckAllValue, allProv } = this.props; + this.props.setToggleCheckAllValue(!toggleCheckAllValue); + } + + onCheckOpt = (checkedKeys, e) => { + const { mode } = this.props; + this.props.setCheckKeysOpt(mode, checkedKeys); + } + + onCheckQty = (checkedKeys) => { + const { mode } = this.props; + this.props.setCheckKeysQty(mode, checkedKeys); + } + + onRightClickAdmTree = (callback) => { + console.log('onRightClickAdmTree', callback); + } + + getCheckedValue = () => { + const { treeData, checkedKeys } = this.state; + let checkedData = []; + + for(let i=0; i < checkedKeys.length; i++) { + let item = findWhere(treeData, {key: parseInt(checkedKeys[i])}); + checkedData.push(item); + } + return checkedData; + } + + renderCheckboxOpt = () => { + let checkboxes = []; + const { mode, checkedKeys2GOpt, checkedKeys3GOpt, checkedKeys4GOpt} = this.props; + + if (mode == '2G') { + // checkboxes = opt2G; + return ( + this.onCheckOpt(checkedKeys2GOpt, e)} + > + {this.renderTreeNodes(opt2G)} + + ) + } + else if (mode == '3G') { + // checkboxes = opt3G; + return ( + this.onCheckOpt(checkedKeys3GOpt)} + > + {this.renderTreeNodes(opt3G)} + + ) + } + else { + // checkboxes = opt4G; + return ( + this.onCheckOpt(checkedKeys4GOpt)} + > + {this.renderTreeNodes(opt4G)} + + ) + } + + /*return ( + this.onCheckOpt(checkedKeysOpt)} + > + {this.renderTreeNodes(checkboxes)} + + )*/ + } + + onExpand = expandedKeys => { + // console.log('onExpand', expandedKeys); + const {closeChart} = this.props + console.log("length", expandedKeys.length) + console.log("closeChart", closeChart) + if(expandedKeys.length === 1){ + if(closeChart) { + document.getElementById('adm-tree-container').style.height = '320px' + } else { + document.getElementById('adm-tree-container').style.height = '120px' + } + } else { + if(closeChart) { + document.getElementById('adm-tree-container').style.height = '440px' + } else { + document.getElementById('adm-tree-container').style.height = '220px' + } + } + }; + + renderCheckboxQuality = () => { + let checkboxes = []; + const { mode, checkedKeys2GQty, checkedKeys3GQty, checkedKeys4GQty } = this.props; + + if (mode == '2G') { + // checkboxes = netQuality2G; + return ( + this.onCheckQty(checkedKeys2GQty)} + > + {this.renderTreeNodes(netQuality2G)} + + ) + } + else if (mode == '3G') { + // checkboxes = netQuality3G; + return ( + this.onCheckQty(checkedKeys3GQty)} + > + {this.renderTreeNodes(netQuality3G)} + + ) + } + else { + // checkboxes = netQuality4G; + return ( + this.onCheckQty(checkedKeys4GQty)} + > + {this.renderTreeNodes(netQuality4G)} + + ) + } + + + } + + + renderTreeNodes = data => + data.map(item => { + if (item.children) { + return ( + + {this.renderTreeNodes(item.children)} + + ); + } + return ; + }) + + render() { + const { isReady, checkedKeys } = this.state; + const { admTreeData, checkedKeysAdm, toggleCheckAllValue } = this.props; + if (!isReady) { + return (
+ +
) + } + + return ( + + + + { this.renderCheckboxOpt() } + + + { this.renderCheckboxQuality() } + + + {/*
+ { this.renderCheckboxQuality() } +
*/} +
+
+ + + +
+
+
+ this.onCheckAdmTree(checkedKeys, e)} + onRightClick={(callback) => this.onRightClickAdmTree(callback)} + > + {this.renderTreeNodes(admTreeData)} + +
+
+ ) + } +} + +export default AdmTree; \ No newline at end of file diff --git a/src/components/AdmTree/example.js b/src/components/AdmTree/example.js new file mode 100644 index 0000000..e347bef --- /dev/null +++ b/src/components/AdmTree/example.js @@ -0,0 +1,136 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import 'antd/dist/antd.css'; +import { Tree, Input } from 'antd'; + +const { Search } = Input; + +const x = 3; +const y = 2; +const z = 1; +const gData = []; + +const generateData = (_level, _preKey, _tns) => { + const preKey = _preKey || '0'; + const tns = _tns || gData; + + const children = []; + for (let i = 0; i < x; i++) { + const key = `${preKey}-${i}`; + tns.push({ title: key, key }); + if (i < y) { + children.push(key); + } + } + if (_level < 0) { + return tns; + } + const level = _level - 1; + children.forEach((key, index) => { + tns[index].children = []; + return generateData(level, key, tns[index].children); + }); +}; +generateData(z); + +const dataList = []; +const generateList = data => { + for (let i = 0; i < data.length; i++) { + const node = data[i]; + const { key } = node; + dataList.push({ key, title: key }); + if (node.children) { + generateList(node.children); + } + } +}; +generateList(gData); + +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; +}; + +class SearchTree extends React.Component { + state = { + expandedKeys: [], + searchValue: '', + autoExpandParent: true, + }; + + onExpand = expandedKeys => { + this.setState({ + expandedKeys, + autoExpandParent: false, + }); + }; + + onChange = e => { + const { value } = e.target; + const expandedKeys = dataList + .map(item => { + if (item.title.indexOf(value) > -1) { + return getParentKey(item.key, gData); + } + return null; + }) + .filter((item, i, self) => item && self.indexOf(item) === i); + this.setState({ + expandedKeys, + searchValue: value, + autoExpandParent: true, + }); + }; + + render() { + const { searchValue, expandedKeys, autoExpandParent } = this.state; + const loop = data => + data.map(item => { + const index = item.title.indexOf(searchValue); + const beforeStr = item.title.substr(0, index); + const afterStr = item.title.substr(index + searchValue.length); + const title = + index > -1 ? ( + + {beforeStr} + {searchValue} + {afterStr} + + ) : ( + {item.title} + ); + if (item.children) { + return { title, key: item.key, children: loop(item.children) }; + } + + return { + title, + key: item.key, + }; + }); + return ( +
+ + +
+ ); + } +} + +// ReactDOM.render(, document.getElementById('container')); +export default SearchTree; \ No newline at end of file diff --git a/src/components/AdmTree/package.json b/src/components/AdmTree/package.json new file mode 100644 index 0000000..241b519 --- /dev/null +++ b/src/components/AdmTree/package.json @@ -0,0 +1,6 @@ +{ + "name": "AdmTree", + "version": "0.0.0", + "private": true, + "main": "./AdmTree.js" +} \ No newline at end of file diff --git a/src/components/BaseMap/BaseMap.css b/src/components/BaseMap/BaseMap.css new file mode 100644 index 0000000..362fa5f --- /dev/null +++ b/src/components/BaseMap/BaseMap.css @@ -0,0 +1,74 @@ +.basemap-container { + z-index: 50; + top: 60px; + right: 50px; + /*background-color: red;*/ + /*padding: 100px;*/ + position: absolute; + max-height: 350px; + border-radius: 5px; + background-color: rgba(0,0,0,0.5); + /*float: right;*/ + color: #ffffff; + min-width: 180px; + justify-content: center; +} + +.basemap-title { + font-size: 14px; + margin-top: 5px; + margin-left: 20px; + margin-bottom: 10px; + font-weight: bold; +} + +.basemap-close { + float: right; + margin-right: 15px; + margin-top: -5px; + cursor: pointer; +} + +.basemap-body { + max-height: 200px; + overflow: auto; + margin: 5px; + text-align: center; +} + +.basemap-text-container { + display: block; +} + +.basemap-graphic { + display: inline-block; +} + +.basemap-text { + display: inline-block; + margin-left: 10px; + margin-right: 10px; + font-size: 12px; +} + +.basemap-active { + border-color: #ffffff !important; + background-color: #ffffff !important; +} + +.basemap-item { + display: inline-block; + padding: 10px; + border: solid; + border-radius: 5px; + margin: 2px; + cursor: pointer; + background-color: #d4d4d4; + margin-bottom: 10px; + text-align: center; + border-color: #d4d4d4; +} + +.basemap-item-title { + color: #000000; +} \ No newline at end of file diff --git a/src/components/BaseMap/BaseMap.js b/src/components/BaseMap/BaseMap.js new file mode 100644 index 0000000..7c0de7d --- /dev/null +++ b/src/components/BaseMap/BaseMap.js @@ -0,0 +1,71 @@ +import React, { Component } from 'react'; +import './BaseMap.css'; +import '../../assets/css/customscroll.css'; +import { Row, Col } from 'reactstrap'; +// import Legend from '@terrestris/react-geo/dist/Legend/Legend'; + +class BaseMap extends Component { + + constructor(props) { + super(props); + this.state = { + activeBaseMap: '' + } + } + + componentDidMount() { + this.getActiveBaseMap(); + } + + componentDidUpdate(prevState, prevProps) { + + } + + getActiveBaseMap = () => { + let activeBaseMap = ''; + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('type') == 'base') { + console.log('activeBaseMap', layer.get('name')); + activeBaseMap = layer.get('name'); + } + }); + + this.setState({activeBaseMap: activeBaseMap}, () => this.renderBaseMap()); + } + + changeBaseLayer = (layer) => { + this.setState({activeBaseMap: layer.get('name')}, () =>{ + this.renderBaseMap(); + this.props.changeBaseLayer(layer); + }); + } + + renderBaseMap = () => { + return this.props.baseLayers.map((item, index) => { + // console.log('activeBaseMap', this.state.activeBaseMap); + // console.log('layerName', item.get('name')); + // console.log('active?', this.state.activeBaseMap == item.get('name')); + + return ( +
this.changeBaseLayer(item)}> + {item.get('imageName')}
+
{item.get('name')}
+
+ ) + }) + } + + render() { + const { toggleBaseMap } = this.props; + return ( +
+
Change Base Map x
+
+ { this.renderBaseMap() } +
+
+ ) + } +} + +export default BaseMap; \ No newline at end of file diff --git a/src/components/BaseMap/package.json b/src/components/BaseMap/package.json new file mode 100644 index 0000000..eeac42e --- /dev/null +++ b/src/components/BaseMap/package.json @@ -0,0 +1,6 @@ +{ + "name": "BaseMap", + "version": "0.0.0", + "private": true, + "main": "./BaseMap.js" +} diff --git a/src/components/CreateNewLayer/CreateNewLayer.css b/src/components/CreateNewLayer/CreateNewLayer.css new file mode 100644 index 0000000..8cfab82 --- /dev/null +++ b/src/components/CreateNewLayer/CreateNewLayer.css @@ -0,0 +1,19 @@ +.modal-dialog-newlayer { + width: 100%; + height: 100%; +} + +.modal-content-newlayer { + width: 100%; +} + +.modal-dialog-body { + max-height: 325px; + width: 100%; + overflow: auto; +} + +.row-input { + margin-right: 0px !important; + margin-left: -10px !important; +} \ No newline at end of file diff --git a/src/components/CreateNewLayer/CreateNewLayer.js b/src/components/CreateNewLayer/CreateNewLayer.js new file mode 100644 index 0000000..06d5403 --- /dev/null +++ b/src/components/CreateNewLayer/CreateNewLayer.js @@ -0,0 +1,348 @@ +import React, { Component } from 'react'; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, Row, Col, Form, FormGroup, Label, Input} from 'reactstrap'; +import { Icon, InlineIcon } from '@iconify/react'; +import removeCircle from '@iconify/icons-ion/remove-circle'; +import { findWhere, without } from 'underscore'; +import './CreateNewLayer.css'; +import '../../assets/css/customscroll.css'; +import { createNewLayer } from '../../const/GeoserverFunc.js'; + +class CreateNewLayer extends Component { + constructor(props) { + super(props); + this.state = { + layerName: '', + layerType: '', + columns: [], + disableCreateLayerButton: true + } + // this.handleChangeInputColumnName = this.handleChangeInputColumnName.bind(this); + // this.sendCreateNewLayer = this.sendCreateNewLayer.bind(this); + // this.checkBeforeSend = this.checkBeforeSend.bind(this); + } + + componentDidMount() { + // console.log('CreateNewLayer resTableData', this.props.resTableData); + } + + componentDidUpdate(prevProps, prevState) { + // this.checkBeforeSend(); + // if (prevState.layerName !== this.state.layerName) { + // this.checkBeforeSend(); + // } + // if (prevState.layerType !== this.state.layerType) { + // this.checkBeforeSend(); + // } + // if (prevState.columns !== this.state.columns) { + // this.checkBeforeSend(); + // } + } + + addColumn = () => { + let { columns } = this.state; + let maxIdx = null; + let newIdx = null; + if (columns.length < 1) { + newIdx = 1; + } + else { + maxIdx = Math.max.apply(Math, columns.map((column) => column.idx)); + console.log('maxIdx',maxIdx); + newIdx = maxIdx + 1; + } + let oneColumn = { + idx: newIdx, + column_name: "", + column_type: "" + } + this.setState({columns: [...columns, oneColumn]}, () => { + console.log(this.state.columns) + this.renderColumnAttribute(); + }); + } + + renderColumnAttribute = () => { + let { columns } = this.state; + + if (columns.length < 1) { + return null; + } + else { + return columns.map((item, index) => { + return ( + + + + + + + + + + + + + + + + + + + + + ) + }) + // for (let i=0; i < columns.length; i++) { + // return ( + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // ) + // } + } + } + + deleteColumn = (idx) => { + console.log('deleteColumn', idx); + let { columns } = this.state; + console.log('columns', columns); + let deletedItem = findWhere(columns, {idx: idx}); + console.log(deletedItem); + let afterRemove = without(columns, deletedItem); + console.log(console.log('afterRemove', afterRemove)); + this.setState({columns: afterRemove}); + } + + handleChangeInputLayerName(event) { + // const validation = !/[0-9a-zA-Z-_]/.test(String.fromCharCode(event.which)); + // console.log('validation', validation); + // const value = event.target.value.split(" ").join(""); + let value = event.target.value; + // value = value.replace(/[^A-Za-z_]/gi, ""); + // value = value.replace(/[^\w]/gi, ""); + value = value.replace(/[^\w]/gi, ""); + // if (validation) { return false; } + this.setState({layerName: value}, () => console.log('layerName', this.state.layerName)); + } + + handleChangeInputLayerType(event) { + const value = event.target.value.split(" ").join(""); + this.setState({layerType: value}, () => console.log('layerType', this.state.layerType)); + } + + handleChangeInputColumnName(idx, event) { + // dicari state columns terus dicari idx nya, kemudian set text nya + + let { columns } = this.state; + let changedItem = findWhere(columns, {idx: idx}); + // let value = event.target.value.split(" ").join(""); + let value = event.target.value; + // value = value.replace(/[^A-Za-z0-9_]/gi, ""); + value = value.replace(/[^\w]/gi, ""); + changedItem.column_name = value; + console.log('changedItem', changedItem); + this.setState({columns: columns}); + console.log('columns', this.state.columns); + } + + handleChangeInputColumnType(idx, event) { + let { columns } = this.state; + let changedItem = findWhere(columns, {idx: idx}); + // let value = event.target.value.split(" ").join(""); + let value = event.target.value; + changedItem.column_type = value; + console.log('changedItem', changedItem); + this.setState({columns: columns}); + console.log('columns', this.state.columns); + } + + preventSpace(e) { + // Prevent space in attribute name + // $('input[name^="column_name"]').on("keypress", function(e) { + // if(!/[0-9a-zA-Z-_]/.test(String.fromCharCode(e.which))) + // return false; + // }); + console.log('event keypress',e); + // console.log(!/[0-9a-zA-Z-_]/.test(String.fromCharCode(e.which))); + // if(!/[0-9a-zA-Z-_]/.test(String.fromCharCode(e.which))) { + // return false; + // } + } + + // checkBeforeSend = () => { + // // make sure no empty fields + // // enable disableCreateLayerButton if no empty fields + // const { layerName, layerType, columns, disableCreateLayerButton } = this.state; + // let toEnableValue = false; + + // if (layerName !== "" && layerType !== "") { + // if (columns.length > 0) { + // for (let i = 0; i < columns.length; i++) { + // if (columns[i].column_name === "") { + // toEnableValue = false; + // // return false; + // } + // else if (columns[i].column_type === "") { + // toEnableValue = false; + // // return false; + // } + // else { + // toEnableValue = true; + // // return true; + // } + // } + // } + // else { + // toEnableValue = false; + // // return false; + // } + // } + + // console.log('toEnableValue', toEnableValue); + // if (toEnableValue === true) { + // // this.setState({disableCreateLayerButton: toEnableValue}); + // // this.enableCreateLayerButton(); + // console.log('enabling create layer button....') + + // } + // } + + // enableCreateLayerButton = () => { + // // this.setState({disableCreateLayerButton: true}); + // console.log('enabling create layer button....') + // } + + sendCreateNewLayer = async () => { + const { layerName, layerType, columns } = this.state; + let reqPayload = { + layer_name: layerName, + layer_type: layerType, + columns: columns + } + + if (layerName === '') { + alert("Please input the layer name"); + return; + } + if (layerType === '') { + alert("Please input the layer type"); + return; + } + if (columns.length < 1) { + alert("Please add at least one attribute column"); + return; + } + if (columns.length > 0) { + for (let i = 0; i < columns.length; i++) { + if (columns[i].column_name === '') { + alert("Please make sure attibute column name is filled") + return; + } + if (columns[i].column_type === '') { + alert("Please make sure attibute column type is filled") + return; + } + } + } + + let processCreateNewLayer = await createNewLayer(reqPayload); + console.log('sendCreateNewLayer',processCreateNewLayer); + alert("Success sending create new layer"); + } + + + + render() { + return ( + + {this.props.createNewLayerTitle ? this.props.createNewLayerTitle : 'Create New Layer'} + +
+ {/*
*/} + {/*
*/} + {/*
*/} + {/* + + + + */} +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + { this.renderColumnAttribute() } + + + + + + + + + + {/*
*/} + {/*
*/} + {/*
*/} + +
+
+ + {/*{' '}*/} + {' '} + + +
+ ) + } +} + +export default CreateNewLayer; \ No newline at end of file diff --git a/src/components/CreateNewLayer/package.json b/src/components/CreateNewLayer/package.json new file mode 100644 index 0000000..c41e84d --- /dev/null +++ b/src/components/CreateNewLayer/package.json @@ -0,0 +1,6 @@ +{ + "name": "CreateNewLayer", + "version": "0.0.0", + "private": true, + "main": "./CreateNewLayer.js" +} diff --git a/src/components/CustomModal/CustomModal.js b/src/components/CustomModal/CustomModal.js new file mode 100644 index 0000000..8068929 --- /dev/null +++ b/src/components/CustomModal/CustomModal.js @@ -0,0 +1,25 @@ +import React, { Component } from 'react'; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; + +class CustomModal extends Component { + constructor(props) { + super(props) + } + + render() { + return( + + {this.props.mapTableTitle} + + Lorem ipsum..... + + + + + + ) + } +} + +export default CustomModal; \ No newline at end of file diff --git a/src/components/CustomModal/package.json b/src/components/CustomModal/package.json new file mode 100644 index 0000000..151a2a1 --- /dev/null +++ b/src/components/CustomModal/package.json @@ -0,0 +1,6 @@ +{ + "name": "CustomModal", + "version": "0.0.0", + "private": true, + "main": "./CustomModal.js" +} diff --git a/src/components/DailyInfo/DailyInfo.css b/src/components/DailyInfo/DailyInfo.css new file mode 100644 index 0000000..9f382ce --- /dev/null +++ b/src/components/DailyInfo/DailyInfo.css @@ -0,0 +1,52 @@ +.daily-info-container { + margin-top: -10px; +} + +.daily-info-container-empty { + /*z-index: 9999;*/ + /*position: fixed;*/ + /*height: 200px;*/ +} + +.daily-info-group-container { + margin-bottom: -10px; +} + +.daily-info-group-title { + font-weight: 700; +} + +.daily-info-list { + width: 100%; + display: inline-block; + padding-left: 10px; + padding-right: 5px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 5px; + border-radius: 10px; + background-color: #f2f2f2; + cursor: pointer; +} + +.daily-info-list:hover { + background-color: #d9d9d9; +} + +.daily-info-name { + width: 70%; + display: inline-block; + font-weight: 700; + font-size: 12px; +} + +.daily-info-count { + width: 30%; + display: inline-block; + text-align: center; +} + +.daily-info-badge { + font-size: 14px !important; + text-align: right; +} \ No newline at end of file diff --git a/src/components/DailyInfo/DailyInfo.js b/src/components/DailyInfo/DailyInfo.js new file mode 100644 index 0000000..6ad5f4e --- /dev/null +++ b/src/components/DailyInfo/DailyInfo.js @@ -0,0 +1,655 @@ +import * as React from 'react'; + +import { Row, Col, Badge } from 'reactstrap'; +import './DailyInfo.css'; +import '../../assets/css/customscroll.css'; +import { formatLabel, COLUMN_DAILY_INFO_TABLE } from '../../const/CustomFunc.js' +import { getDailyInfoApi, requestTableDailyInfoApi } from '../../const/GeohrApiFunc.js'; +import ContentLoader from 'react-content-loader' +import { + AT_TRIP_COLOR, + AT_CUSTOMER_COLOR, + AT_OFFICE_COLOR, + PRESENT_COLOR, + ABSENT_COLOR, + TOTAL_ATTENDANCE_COLOR, + ACHIEVE_COLOR, + NOT_ACHIEVE_COLOR, + DONE_COLOR, + NOT_YET_COLOR, + IZIN_COLOR, + TOTAL_COLOR, + RED_COLOR, + ORANGE_COLOR, + GREEN_COLOR, + DARK_GREY_COLOR, + BLUE_COLOR, + PURPLE_COLOR, + BROWN_COLOR, + YELLOW_COLOR, + WHITE_COLOR, + BLACK_COLOR +} from '../../const/AppConst.js' +import axios from 'axios'; +import { API_GEOHR_KARYAWAN, API_DAILY_INFO, API_DAILY_INFO_DETAIL, DASHBOARD_PROYEK_SEARCH, PROYEK_SEARCH } from '../../const/ApiConst'; +// import DialogBottom from './DialogBottom' +import { toast } from 'react-toastify'; +import moment from 'moment'; +const id_org = window.localStorage.getItem('id_org'); + +// const token = window.localStorage.getItem('token'); +// const config = { +// headers: +// { +// "Authorization": `Bearer ${token}`, +// "Content-type": `application/json` +// } +// }; + +class DailyInfo extends React.Component { + + constructor(props) { + + super(props); + this.state = { + menu: null, + isReady: false, + sumPresensi: 0, + sumAbsensi: 0, + sumIzin: 0, + sumTracked: 0, + sumUntracked: 0, + sumEmployee: 0, + sumPanicBtn: 0, + dataNyPresence: [], + dataPresence: [], + dataPermission: [], + dataAbsent: [], + openDialogBottom: false, + itemDialog: [], + dataTableDialog: [], + sumTelat: 0, + sumTanpaKet: 0, + dataTelat: [], + dataTanpaKet: [], + dataAbsensi:[], + dataPanicBtn: [], + dataEmployee: [], + totalProyek: 0, + planning: 0, + realisasi: 0, + status_project: { + good: 0, + warning: 0, + critical: 0 + }, + waspang_status: { + presensi: 0, + absensi: 0 + }, + panic_button: 0 + } + } + + async componentDidMount() { + // this.getTotalProyek(); + // this.getDailyInfo() + // this.getDailyEmployee(); + // this.setState({ isReady: true }) + } + + componentDidUpdate(prevProps, prevState) { + + } + + getDailyInfo = async () => { + const payload = { + "columns": [ + {"name": "created_at","logic_operator": "range","value": `${moment().utc().format('YYYY-MM-DD')} 00:00:00`,"value1": `${moment().utc().format('YYYY-MM-DD')} 23:59:59`,"operator": "AND"} + ], + "paging": {"start": 0,"length": -1} + } + + const config = { + method: 'POST', // *GET, POST, PUT, DELETE, etc. + // mode: 'cors', // no-cors, *cors, same-origin + // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+window.localStorage.getItem('token') + // 'Content-Type': 'application/x-www-form-urlencoded', + }, + // redirect: 'follow', // manual, *follow, error + // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: JSON.stringify(payload) // body data type must match "Content-Type" header + } + try { + const result = await fetch(DASHBOARD_PROYEK_SEARCH, config).then(response => response.json()).then(res => res); + console.log('getDailyInfo result', result); + if (result.code === 200 && result.data) { + const dataSum = result.data; + this.setState({ + planning: dataSum.planning, + realisasi: dataSum.realisasi, + status_project: dataSum.status_project, + waspang_status: dataSum.waspang_status, + panic_button: dataSum.panic_button, + isReady: true + }); + } + // const result = await axios + // .get(DASHBOARD_PROYEK, config) + // .then(res => res) + // .catch((error) => error.response); + + // console.log('result', result); + + // if (result && result.data && result.data.code == 200) { + // const dataSum = result.data; + // this.setState({ + // planning: dataSum.planning, + // realisasi: dataSum.realisasi, + // status_project: dataSum.status_project, + // waspang_status: dataSum.waspang_status, + // panic_button: dataSum.panic_button, + // isReady: true + // }); + // } + } + catch (e) { + console.log('error getDailyInfo', e); + // return false; + } + } + + getTotalProyek = async () => { + const payload = { + + } + const config = { + method: 'POST', // *GET, POST, PUT, DELETE, etc. + // mode: 'cors', // no-cors, *cors, same-origin + // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+window.localStorage.getItem('token') + // 'Content-Type': 'application/x-www-form-urlencoded', + }, + // redirect: 'follow', // manual, *follow, error + // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: JSON.stringify(payload) // body data type must match "Content-Type" header + } + try { + const result = await fetch(PROYEK_SEARCH, config).then(response => response.json()).then(res => res); + console.log('getDailyInfo result', result); + if (result.code === 200 && result.data) { + const dataSum = result.data; + this.setState({ + totalProyek: result.data.length, + isReady: true + }); + } + + // const result = await axios + // .get(PROYEK_LIST, config) + // .then(res => res) + // .catch((error) => error.response); + + // if (result && result.data && result.data.code == 200) { + // const dataSum = result.data; + // this.setState({ + // totalProyek: result.data.length, + // isReady: true + // }); + // } + + } + catch (e) { + console.log('error getDailyInfo', e); + // return false; + } + } + + openDialogBottom = async(item) => { + let data = [] + const { dataPresence, dataAbsensi, dataEmployee, dataPanicBtn, dataTelat, dataTanpaKet } = this.state + if (item.key === "presensi") { + data = dataPresence + } else if (item.key === "absensi") { + data = dataAbsensi + } else if (item.key === "panic button") { + data = dataPanicBtn + } else if (item.key === "total dengan keterangan") { + data = dataTelat + } else if (item.key === "total tanpa keterangan") { + data = dataTanpaKet + } else if (item.key === "total karyawan") { + data = dataEmployee + } + this.setState({ openDialogBottom: true, itemDialog: item, dataTableDialog: data }) + } + + closeDialogBottom = () => { + this.setState({ openDialogBottom: false }) + } + + // getListDailyInfo = () => { + // let menu = [ + // { + // "id": 1, + // "title": "TOTAL KARYAWAN", + // "key": "total karyawan", + // "total": this.state.sumEmployee, + // "color": TOTAL_COLOR, + // "text_color": WHITE_COLOR + // }, + // { + // "id": 2, + // "title": "HADIR", + // "key": "presensi", + // "total": this.state.sumPresensi, + // "color": PRESENT_COLOR, + // "text_color": WHITE_COLOR + // }, + // { + // "id": 3, + // "title": "TIDAK HADIR", + // "key": "izin", + // "total": this.state.sumAbsensi, + // "color": IZIN_COLOR, + // "text_color": WHITE_COLOR + // }, + // { + // "id": 4, + // "title": "KARYAWAN TELAT", + // "key": "total dengan keterangan", + // "total": this.state.sumTelat, + // "color": NOT_YET_COLOR, +// "text_color": WHITE_COLOR + // }, + // { + // "id": 5, + // "title": "KARYAWAN TANPA KETERANGAN", + // "key": "total tanpa keterangan", + // "total": this.state.sumTanpaKet, + // "color": DONE_COLOR, + // "text_color": WHITE_COLOR + // }, + // { + // "id": 6, + // "title": "PANIK BUTTON", + // "key": "panic button", + // "total": this.state.sumPanicBtn, + // "color": ABSENT_COLOR, + // "text_color": WHITE_COLOR + // } + // ] + + // return menu; + // } + + // renderDailyInfo = () => { + // const list = this.getListDailyInfo() || []; + // return ( + // list.map((item) => { + // return ( + //
this.openTable(item)} key={item.id} className="daily-info-list"> + //
+ // {item.title} + //
+ //
+ // {item.total} + //
+ //
+ // ) + // }) + // ) + // } + + getListDailyInfo = () => { + let menu = [ + { + "id": 1, + "title": "PROYEK", + "key": "proyek", + "total": this.state.totalProyek, + "color": BROWN_COLOR, + "text_color": WHITE_COLOR + }, + { + "id": 2, + "title": "PLANNING", + "key": "plannng", + "total": this.state.planning, + "color": DARK_GREY_COLOR, + "text_color": WHITE_COLOR + }, + { + "id": 3, + "title": "REALISASI", + "key": "realisasi", + "total": this.state.realisasi, + "color": BLUE_COLOR, + "text_color": WHITE_COLOR + }, + { + "id": 4, + "title": "STATUS PROJECT", + "key": "status_project", + "total": undefined, + "color": DONE_COLOR, + "text_color": WHITE_COLOR, + "children": [ + { + "id": 1, + "title": "Aman", + "key": "status_project_good", + "total": this.state.status_project.good, + "color": GREEN_COLOR, + "text_color": WHITE_COLOR + }, + { + "id": 2, + "title": "Alert", + "key": "status_project_warning", + "total": this.state.status_project.warning, + "color": YELLOW_COLOR, + "text_color": DARK_GREY_COLOR + }, + { + "id": 3, + "title": "Critical", + "key": "status_project_critical", + "total": this.state.status_project.critical, + "color": RED_COLOR, + "text_color": WHITE_COLOR + }] + }, + { + "id": 5, + "title": "WASPANG STATUS", + "key": "waspang_status", + "total": undefined, + "color": IZIN_COLOR, + "text_color": WHITE_COLOR, + "children": [ + { + "id": 1, + "title": "Presensi", + "key": "waspang_status_presensi", + "total": this.state.waspang_status.presensi, + "color": GREEN_COLOR, + "text_color": WHITE_COLOR + }, + { + "id": 2, + "title": "Absensi", + "key": "waspang_status_absensi", + "total": this.state.waspang_status.absensi, + "color": ORANGE_COLOR, + "text_color": WHITE_COLOR + }], + }, + { + "id": 6, + "title": "PANIC BUTTON", + "key": "panic_button", + "total": this.state.panic_button, + "color": PURPLE_COLOR, + "text_color": WHITE_COLOR + }, + ] + + return menu; + } + + renderDailyInfo = () => { + const list = this.getListDailyInfo() || []; + return this.renderChildDailyInfo(list); + } + + renderChildDailyInfo = (data) => { + return ( + data.map((item) => { + return ( +
+
+ {item.title} +
+
+ {item.total !== undefined ? item.total : null} +
+ { item.children ? this.renderChildDailyInfo(item.children) : null } +
+ ) + }) + ) + } + + // requestTableDailyInfoApi = async (table_type) => { + // const formData = new FormData(); + // // formData.append('start', 0); + // // formData.append('length', 100); + // formData.append('table_type', table_type); + + // // if(this.state.search!==""){ + // // formData.append('value', this.state.search); + // // } + + // const start = 0; + // const length = 100; + + // const param = { + // method: 'POST', + // header: JSON.stringify({'Content-Type': 'multipart/form-data'}), + // body: formData + // } + + // try { + // const result = await fetch(API_DAILY_INFO_DETAIL(start,length), param).then(response => response.json()).then(res => res); + // console.log('requestTableDailyInfoApi result', result); + // if (result.code_status === 200 && result.data) { + // return result.data; + // } + // else { + // return null; + // } + // } + // catch (e) { + // console.log('error get', e); + // toast.warn(e.toString()); + // return null; + // } + // } + + openTable = async (item) => { + const { dataPresence, dataAbsensi, dataEmployee, dataPanicBtn, dataTelat, dataTanpaKet} = this.state + + console.log('openTable', item); + + await this.props.setIsProcessing(true); + this.props.removeLayerByName('routeLayer'); // remove the previous routeLayer + + let nameUpperCase = item.title.toUpperCase(); + this.props.setMapTableTitle(nameUpperCase); + // let table = await requestTableDailyInfoApi(item); + // console.log('[DailyInfo] openTable table', table); + + let column = []; + + let data = null; + + // const editFormatter = () => { + // return ( + //
+ // console.log('goToFeature')}> + //
+ // ) + // } + + // if (table && table.length > 0) { + // let oneRow = table[0]; + // let optionCell = { + // dataField: '_option_', + // text: 'Action', + // formatter: editFormatter + // } + + // for (let key in oneRow) { + // column.push({ + // "dataField": key, + // "text": formatLabel(key) + // }) + // } + + + + // data = table; + // } + + // await this.requestTableDailyInfoApi(item.key); + // let table_type = ''; + // if(type==="presensi"){ + // table_type = "hadir"; + // }else if(type==="panic button"){ + // table_type = "panik_button"; + // }else if(type==="absent"){ + // table_type = "tidak_hadir"; + // }else if(type==="late"){ + // table_type = "karyawan_telat"; + // }else if(type==="without_ket"){ + // table_type = "karyawan_tanpa_keterangan"; + // }else if(type==="sum_employee"){ + // table_type = "total_karyawan"; + // } + + + let table_type = ''; + + // if (item.key === "presensi") { + // data = dataPresence + // } else if (item.key === "absensi") { + // data = dataAbsensi + // } else if (item.key === "panic button") { + // data = dataPanicBtn + // } else if (item.key === "total dengan keterangan") { + // data = dataTelat + // } else if (item.key === "total tanpa keterangan") { + // data = dataTanpaKet + // } else if (item.key === "total karyawan") { + // data = dataEmployee + // } + + if (item.key === "total karyawan") { + table_type = "total_karyawan"; + } + else if (item.key === "presensi") { + table_type = "hadir"; + } + else if (item.key === "izin") { + table_type = "tidak_hadir"; + } + else if (item.key === "total dengan keterangan") { + table_type = "karyawan_telat"; + } + else if (item.key === "total tanpa keterangan") { + table_type = "karyawan_tanpa_keterangan"; + } + else if (item.key === "panic button") { + table_type = "panik_button"; + } + this.props.setTableType(table_type) + + // let dataRes = await requestTableDailyInfoApi(table_type); + + // this.props.setMapTableData(dataRes) + + + if (!this.props.mapTableModalVisible) { + this.props.toggleMapTable(); + } + + await this.props.setIsProcessing(false); + } + + render() { + const { openDialogBottom, itemDialog, dataTableDialog, isReady } = this.state; + if (!isReady) { + return ( +
+ + + + + + + + + + + + + + +
+ ) + } + + return ( +
+ {/* {openDialogBottom && ( + + )} */} + +
+ {/*
Status
*/} + {this.renderDailyInfo()} +
+ + {/* {menu && menu.length > 0 && menu.map((menuItem, index) => { + return ( +
+
{menuItem.title.toUpperCase()}
+ {menuItem.children.length > 0 && menuItem.children.map((childMenuItem, index) => { + return ( +
this.openTable(childMenuItem)} key={index}> +
+ {childMenuItem.name.toUpperCase()} +
+
+ {childMenuItem.total} +
+
+ ) + })} + +
+ ) + }) + } */} +
+ ) + } +} + +export default DailyInfo; \ No newline at end of file diff --git a/src/components/DailyInfo/DialogBottom.js b/src/components/DailyInfo/DialogBottom.js new file mode 100644 index 0000000..54455ac --- /dev/null +++ b/src/components/DailyInfo/DialogBottom.js @@ -0,0 +1,357 @@ +import React, { Component } from 'react' +import { Resizable } from "re-resizable"; +import closeCircleOutline from '@iconify/icons-ion/close-circle-outline'; +import removeCircleOutline from '@iconify/icons-ion/remove-circle-outline'; +import windowMaximaze from '@iconify/icons-mdi/window-maximize'; +import { Col, Row, Table, Card, CardHeader, CardBody, CardFooter } from 'reactstrap'; +import { Icon } from '@iconify/react'; +import '../../views/Dashboard/Dashboard.css'; +import moment from 'moment'; + +const style = { + position: "fixed", + width: "100%", + bottom: 0, + left: 0, + right: 0, + border: "solid 1px #ddd", + background: "#ffffff", + zIndex: 9999 +}; + +class DialogBottom extends Component { + + state = { + tableHeight: 150, + resizableWidth: "100%", + resizableHeight: 300, + maximizeTitle: "maximize" + } + + setMapTableWindow = (type) => { + console.log('setMapTableWindow', type); + if (type === 'min') { + this.setState({ resizableHeight: 55, tableHeight: 150 }); + } + else if (type === "max") { + if (this.state.resizableHeight === 725) { + // restore (back to default) + this.setState({ resizableHeight: 300, tableHeight: 150, maximizeTitle: "maximize" }); + } + else { + // maximize + this.setState({ resizableHeight: 725, tableHeight: 950, maximizeTitle: "restore" }); + } + + } + } + render() { + const { isCloseDialog, itemDialog, dataTable } = this.props + const { resizableHeight } = this.state + // console.log('dataTable', dataTable.length) + // console.log('itemDialog', itemDialog) + console.log(`resizableHeight`, resizableHeight) + return ( + { + // console.log('onResizeStop,e,direction,ref,d', { e, direction, ref, d }) + this.setState({ + resizableWidth: "100%", // to keep always full width + resizableHeight: this.state.resizableHeight + d.height + }); + + // if drag the table header + if (e.screenY < 100) { + this.setState({ tableHeight: 450 }) + } + else if (e.screenY < 150) { + this.setState({ tableHeight: 400 }) + } + else if (e.screenY < 200) { + this.setState({ tableHeight: 350 }) + } + else if (e.screenY < 250) { + this.setState({ tableHeight: 300 }) + } + else if (e.screenY < 300) { + this.setState({ tableHeight: 250 }) + } + else if (e.screenY < 400) { + this.setState({ tableHeight: 200 }) + } + else if (e.screenY < 600) { + this.setState({ tableHeight: 150 }) // default + } + else if (e.screenY >= 600) { + // this.props.toggleMapTable(); // close the MapTable + this.setMapTableWindow('min'); + } + }} + > +
+ + + {/*

{itemDialog.title}

*/} + + + this.setMapTableWindow("min")} + > + + + this.setMapTableWindow("max")} + > + + + isCloseDialog()} + > + + + +
+ {itemDialog.key === "presensi" && ( + + + Daftar Kehadiran Karyawan Hari Ini + + + {dataTable.length === 0 ? + + + + +
No Data Available
+ : + + + + + + + + + + + {dataTable.map((item, index) => { + return ( + + + + + + + ) + })} + +
#Nama KaryawanJam MasukJam Keluar
{++index}{item.employee_name}{item.clock_time ? moment(item.clock_time).format('DD-MM-YYYY HH:mm') : "-"}{item.clock_out_time ? moment(item.clock_out_time).format('DD-MM-YYYY HH:mm') : "-"}
+ } +
+
+ )} + {itemDialog.key === "karyawan telat" && ( + + + Daftar Karyawan Telat Hari Ini + + + {dataTable.length === 0 ? + + + + +
No Data Available
+ : + + + + + + + + + + + {dataTable.map((item, index) => { + return ( + + + + + + + ) + })} + +
#Nama KaryawanJam MasukJam Keluar
{++index}{item.employee_name}{item.clock_time ? moment(item.clock_time).format('DD-MM-YYYY HH:mm') : "-"}{item.clock_out_time ? moment(item.clock_out_time).format('DD-MM-YYYY HH:mm') : "-"}
+ } +
+
+ )} + {itemDialog.key === "total karyawan" && ( + + + Daftar Total Karyawan + + + {dataTable.length === 0 ? + + + + +
No Data Available
+ : + + + + + + + + + + + + + {dataTable.map((item, index) => { + return ( + + + + + + + + + ) + })} + +
#DevisiNama KaryawanJenis KelaminNo HPAlamat
{++index}{item.devisi_name}{item.name}{item.gender == "P" ? "Perempuan" : "Laki-laki"}{item.phone_number}{item.address}
+ } +
+
+ )} + {itemDialog.key === "absent" && ( + + + Daftar Karyawan Absent Hari ini + + + {dataTable.length === 0 ? + + + + +
No Data Available
+ : + + + + + + + + + +
#Nama KaryawanJam MasukJam Keluar
} + +
+
+ )} + {itemDialog.key === "karyawan tanpa keterangan" && ( + + + Daftar Karyawan Tanpa Keterangan + + + {dataTable.length === 0 ? + + + + +
No Data Available
+ : + + + + + + + + + + + + + {dataTable.map((item, index) => { + return ( + + + + + + + + + ) + })} + +
#DevisiNama KaryawanJenis KelaminNo HPAlamat
{++index}{item.devisi_name}{item.name}{item.gender == "P" ? "Perempuan" : "Laki-laki"}{item.phone_number}{item.address}
+ } +
+
+ )} + {itemDialog.key === "panic button" && ( + + + Daftar Karyawan Melakukan Panik Button Hari Ini + + + {dataTable.length === 0 ? + + + + +
No Data Available
+ : + + + + + + + + + + + {dataTable.map((item, index) => { + return ( + + + + + + + ) + })} + +
#Nama KaryawanJam MasukJam Keluar
{++index}{item.employee_name}{item.clock_time ? moment(item.clock_time).format('DD-MM-YYYY HH:mm') : "-"}{item.clock_out_time ? moment(item.clock_out_time).format('DD-MM-YYYY HH:mm') : "-"}
+ } +
+
+ )} +
+
+ ) + } +} + +export default DialogBottom diff --git a/src/components/DailyInfo/package.json b/src/components/DailyInfo/package.json new file mode 100644 index 0000000..28c96c9 --- /dev/null +++ b/src/components/DailyInfo/package.json @@ -0,0 +1,6 @@ +{ + "name": "DailyInfo", + "version": "0.0.0", + "private": true, + "main": "./DailyInfo.js" +} diff --git a/src/components/DataTable/DataTable.css b/src/components/DataTable/DataTable.css new file mode 100644 index 0000000..0c79600 --- /dev/null +++ b/src/components/DataTable/DataTable.css @@ -0,0 +1,11 @@ +.table-option-button-group { + +} + +.switch-button-upper { + margin-top: -5px; +} + +.add-button-container { + float: right; +} \ No newline at end of file diff --git a/src/components/DataTable/DataTable.js b/src/components/DataTable/DataTable.js new file mode 100644 index 0000000..112ac3a --- /dev/null +++ b/src/components/DataTable/DataTable.js @@ -0,0 +1,463 @@ +import React, { Component } from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import cellEditFactory from 'react-bootstrap-table2-editor'; +import { Badge, Card, CardBody, CardHeader, Col, Row, Table, Button, + Modal, ModalHeader, ModalBody, ModalFooter, + Form, FormGroup, Label, Input, FormText +} from 'reactstrap'; +import { Icon, InlineIcon } from '@iconify/react'; +import createOutline from '@iconify/icons-ion/create-outline'; +import archiveOutline from '@iconify/icons-ion/archive-outline'; +import addCircleOutline from '@iconify/icons-ion/add-circle-outline'; +import { AppSwitch } from '@coreui/react'; +import { UncontrolledTooltip } from 'reactstrap'; +import './DataTable.css'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import moment from "moment" + +import { resetWarningCache } from 'prop-types'; + +const dataColumns = [{ + dataField: 'id', + text: 'Id' +}]; + +const { SearchBar } = Search; +const { ExportCSVButton } = CSVExport; + +class DataTable extends Component { + constructor(props) { + const {columns} = props + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + // console.log("convert state", val) + super(props) + this.state = { + columns: [], + data: [], + selectRow: { + mode: 'checkbox', + clickToSelect: true, + clickToEdit: false, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e), + }, + editCellMode: false, + onSelectAllMode: false, + selectedRows: [], + alert: false, + alertWarning: false, + successAlert: false, + dangerAlert: false, + messageAlert: "", + modalAdd : false, + ... val + } + // this.toggleEditCell = this.toggleEditCell.bind(this); + this.mapTableInit = this.mapTableInit.bind(this); + this.addNewData = this.addNewData.bind(this); + // this.activateCellEdit = this.activateCellEdit.bind(this); + } + + getDataTable = () => { + fetch(this.props.urlParamGet, { + method: 'POST', + header: JSON.stringify({ + 'Content-Type': 'application/json' + }) + }).then((response) => response.json()) + .then((responseJson) => { + if(responseJson.code_message === "Success"){ + // console.log(responseJson) + let dataArray = [] + + for(let i = 0; i < responseJson.data.length; i++){ + dataArray.push(responseJson.data[i]); + // console.log("data ", responseJson.data[i]) + } + + this.mapTableInit(dataArray) + // console.log("dataTable ", dataArray) + + }else{ + + console.log("gagal login"); + } + }).catch((error) => { + + }); + } + + + componentDidMount() { + this.getDataTable(); + } + + // componentDidUpdate(prevProps, prevState) { + // if (prevState.data !== this.state.data) { + // this.mapTableInit(); + // } + // } + + mapTableInit(data) { + + // let {data} = this.state; + let columns = []; + if (data.length > 0) { + for (let key in data[0]) { + columns.push({ + dataField: key, + text: key, + sort: true + }) + } + } + + this.setState({ + columns: data.length !== 0 ? columns:dataColumns, + data: data + }) + } + + toggleEditCell = () => { + let { editCellMode } = this.state; + console.log("editCellMode ", editCellMode) + // when editCellMode === true -> false (deactive) + editCellMode ? + this.setState({ + editCellMode: false, + selectRow: { + mode: 'checkbox', + clickToSelect: true, + clickToEdit: false, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e), + }, + selectedRows: [] + }) : + // when editCellMode === false -> true (activate) + this.setState({ + editCellMode: true, + selectRow: { + mode: 'checkbox', + clickToSelect: false, + clickToEdit: true, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e), + } + }); + } + + addNewData() { + alert('add new data'); + } + + onSelectRowTable = ({row, isSelect, e}) => { + const {selectedRows} = this.state + // console.log({row, isSelect, e}) + if(typeof row === "object"){ + if(isSelect){ + this.setState({selectedRows: [...selectedRows, row]}) + } else { + const idx = selectedRows.indexOf(row) + selectedRows.splice(idx, 1) + this.setState({selectedRows}) + } + + } else { + console.log("type array") + this.setState({selectedRows: row}) + } + } + + onSelectAllRowTable = (isSelect, rows, e) => { + console.log('onSelectAllRowTable isSelect, rows, e', isSelect, rows, e); + const { selectedRows } = this.state; + + if (isSelect === false) { + // clear all this.state.selectedRows + this.setState({selectedRows: []}, () => { + console.log('onSelectAllRowTable selectedRows', this.state.selectedRows); + }); + } + else if (isSelect === true) { + let row = rows.map((item, index) => { + return item; + }); + console.log('row', row); + this.setState({selectedRows: row}, () => { + console.log('onSelectAllRowTable selectedRows', this.state.selectedRows); + }); + } + } + + // Add Country Function + openModalAdd = () => this.setState({modalAdd: true}) + + closeModalAdd = () => this.setState({modalAdd: false}) + + saveModalAdd = async () => { + + const {columns} = this.props + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + // const val = columns.reduce((obj, item) => (obj[item.dataField] = item.dataField === "last_updated" ? moment().format("YYYY-MM-DD HH:mm:ssZ"): this.state[item.dataField], obj) ,{}); + + const obj = columns.reduce((obj, item) => Object.assign(obj, { + [item.dataField]: item.dataField === "last_updated" || item.dataField === "created_time" || item.dataField === "modified_time" + ? moment().format("YYYY-MM-DD HH:mm:ssZ"): this.state[item.dataField] + }), {}); + + console.log("payload insert", obj) + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(obj) + } + + try { + const result = await fetch(this.props.urlParamInsert, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable(); + this.setState({modalAdd: false, alert: true, messageAlert: result.code_message, successAlert: true, dangerAlert: false, ...val}) + } else { + this.setState({modalAdd: false, alert: true, messageAlert: result.code_message, successAlert: false, dangerAlert: true, ...val}) + } + } catch { + this.setState({modalAdd: false, alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + + } + // end Add Country Function + + modalAdd = () => { + const {columns} = this.props + return ( +
+ + Add new {this.props.title} + + {columns.map((res, idx) => { + if(res.showInput){ + return ( + + + this.setState({[res.dataField]: e.target.value})} type={res.type} /> + + ) + } + + })} + + + + + {' '} + + +
+ ) + } + + updateFunction = async (oldValue, newValue, row, column) => { + let {editCellMode} = this.state + console.log('editCellMode', editCellMode); + // console.log("before save:", {oldValue, newValue, row, column}) + console.log(row) + // row.last_updated = moment().format("YYYY-MM-DD HH:mm:ssZ") + // console.log(row) + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(row) + } + + const result = await fetch(this.props.urlParamUpdate, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable() + this.setState({alert: true, successAlert: true, dangerAlert: false, messageAlert: result.code_message, editCellMode: false, selectedRows: []}) + } else { + this.setState({alert: true, successAlert: false, dangerAlert: true, messageAlert: result.code_message, editCellMode: false, selectedRows: [] }) + } + } + + handleDelete = data => { + + } + + deleteFunction = async param => { + const {selectedRows} = this.state + console.log("row selected",selectedRows) + if(param === "delete") { + + const {columns} = this.props + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(selectedRows) + } + + try { + const result = await fetch(this.props.urlParamDelete, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable(); + this.setState({alert: true, alertWarning: false, messageAlert: result.code_message, successAlert: true, dangerAlert: false, ...val, selectedRows: []}) + } else { + this.setState({alert: true, alertWarning: false, messageAlert: result.code_message, successAlert: false, dangerAlert: true, ...val, selectedRows: []}) + } + } catch { + this.setState({alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true, selectedRows: []}) + } + } else { + this.setState({alertWarning: false}) + } + } + + allDeleteFunction = () => { + console.log() + } + + render() { + const { columns, data, selectedRows, selectRow, editCellMode, alert, messageAlert, + alertWarning, deleteData, successAlert, dangerAlert } = this.state; + // console.log("render cellmode", editCellMode) + return ( + +
+ this.setState({alert: false, successAlert: false, dangerAlert: false})}> + {messageAlert} + + + this.deleteFunction("delete")} + onCancel={() => this.deleteFunction("cancel")} + focusCancelBtn + > + This {this.props.title} will be deleted + + + + + + + + + Master { this.props.title !== undefined ? this.props.title : 'DataTable' } + + +
+ + + {this.modalAdd()} +
+ +
+
+ + {data.length === 0 ? ( +
No result found
+ ):( +
+ + { + props => ( +
+ + +
+ +
+ + +
+ + +
+ Edit
+ + this.toggleEditCell()} checked={editCellMode} /> +
+ +
+ {selectedRows.length > 0 && !editCellMode && } + + + + + Export CSV + +
+
+
+ +
+ {/*
*/} +
+ + { editCellMode ? + this.updateFunction(oldValue, newValue, row, column), + }) } + /> : + + } +
+
+ ) + } +
+
+ + )} + +
+
+ +
+
+ ) + } +} + +export default DataTable; \ No newline at end of file diff --git a/src/components/DataTable/package.json b/src/components/DataTable/package.json new file mode 100644 index 0000000..c232e7f --- /dev/null +++ b/src/components/DataTable/package.json @@ -0,0 +1,6 @@ +{ + "name": "DataTable", + "version": "0.0.0", + "private": true, + "main": "./DataTable.js" +} diff --git a/src/components/DataTableEditDialog/DataTable.css b/src/components/DataTableEditDialog/DataTable.css new file mode 100644 index 0000000..0c79600 --- /dev/null +++ b/src/components/DataTableEditDialog/DataTable.css @@ -0,0 +1,11 @@ +.table-option-button-group { + +} + +.switch-button-upper { + margin-top: -5px; +} + +.add-button-container { + float: right; +} \ No newline at end of file diff --git a/src/components/DataTableEditDialog/DataTable.js b/src/components/DataTableEditDialog/DataTable.js new file mode 100644 index 0000000..add1ff8 --- /dev/null +++ b/src/components/DataTableEditDialog/DataTable.js @@ -0,0 +1,437 @@ +import React, { Component } from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import cellEditFactory from 'react-bootstrap-table2-editor'; +import { Badge, Card, CardBody, CardHeader, Col, Row, Table, Button, + Modal, ModalHeader, ModalBody, ModalFooter, + Form, FormGroup, Label, Input, FormText +} from 'reactstrap'; +import { Icon, InlineIcon } from '@iconify/react'; +import createOutline from '@iconify/icons-ion/create-outline'; +import archiveOutline from '@iconify/icons-ion/archive-outline'; +import addCircleOutline from '@iconify/icons-ion/add-circle-outline'; +import { AppSwitch } from '@coreui/react'; +import { UncontrolledTooltip } from 'reactstrap'; +import './DataTable.css'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import moment from "moment" + +import { resetWarningCache } from 'prop-types'; + +const dataColumns = [{ + dataField: 'id', + text: 'Id' +}]; + +const { SearchBar } = Search; +const { ExportCSVButton } = CSVExport; + +class DataTable extends Component { + constructor(props) { + const {columns} = props + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + // console.log("convert state", val) + super(props) + this.state = { + columns: [], + data: [], + selectRow: { + mode: 'checkbox', + clickToSelect: true, + clickToEdit: false, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectRowTable({row: rows, isSelect, e}), + }, + editCellMode: false, + onSelectAllMode: false, + selectedRows: [], + alert: false, + alertWarning: false, + successAlert: false, + dangerAlert: false, + messageAlert: "", + openDialog : false, + editData : false, + ... val + } + // this.toggleEditCell = this.toggleEditCell.bind(this); + this.mapTableInit = this.mapTableInit.bind(this); + // this.activateCellEdit = this.activateCellEdit.bind(this); + } + + getDataTable = () => { + fetch(this.props.urlParamGet, { + method: 'POST', + header: JSON.stringify({ + 'Content-Type': 'application/json' + }) + }).then((response) => response.json()) + .then((responseJson) => { + if(responseJson.code_message === "Success"){ + // console.log(responseJson) + let dataArray = [] + + for(let i = 0; i < responseJson.data.length; i++){ + dataArray.push(responseJson.data[i]); + // console.log("data ", responseJson.data[i]) + } + + this.mapTableInit(dataArray) + // console.log("dataTable ", dataArray) + + }else{ + + console.log("gagal login"); + } + }).catch((error) => { + + }); + } + + + componentDidMount() { + this.getDataTable(); + } + + mapTableInit(data) { + + // let {data} = this.state; + let columns = []; + if (data.length > 0) { + for (let key in data[0]) { + columns.push({ + dataField: key, + text: key, + sort: true + }) + } + } + + this.setState({ + columns: data.length !== 0 ? columns:dataColumns, + data: data + }) + } + + // toggleEditCell = () => { + // let { editCellMode } = this.state; + // console.log("editCellMode ", editCellMode) + // editCellMode ? + // this.setState({ + // editCellMode: editCellMode, + // selectRow: { + // mode: 'checkbox', + // clickToSelect: editCellMode, + // clickToEdit: editCellMode, + // bgColor: '#e4e5e6', + // onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + // onSelectAll: (isSelect, rows, e) => this.onSelectRowTable({row: rows, isSelect, e}), + // } + // }) : + // this.setState({ + // editCellMode: !editCellMode, + // selectRow: { + // mode: 'checkbox', + // clickToSelect: editCellMode, + // clickToEdit: !editCellMode, + // bgColor: '#e4e5e6', + // onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + // onSelectAll: (isSelect, rows, e) => this.onSelectRowTable({row: rows, isSelect, e}), + // } + // }); + // } + + onSelectRowTable = ({row, isSelect, e}) => { + const {selectedRows} = this.state + // console.log({row, isSelect, e}) + if(typeof row === "object"){ + if(isSelect){ + this.setState({selectedRows: [...selectedRows, row]}) + } else { + const idx = selectedRows.indexOf(row) + selectedRows.splice(idx, 1) + this.setState({selectedRows}) + } + + } else { + console.log("type array") + this.setState({selectedRows: row}) + } + } + + // Add Country Function + openDialog = () => this.setState({openDialog: true}) + + closeDialog = () => this.setState({openDialog: false}) + + saveDialog = async () => { + const {editData} = this.state + const {columns, urlParamInsert, urlParamUpdate} = this.props + + const urlParam = editData ? urlParamUpdate:urlParamInsert + + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + // const val = columns.reduce((obj, item) => (obj[item.dataField] = item.dataField === "last_updated" ? moment().format("YYYY-MM-DD HH:mm:ssZ"): this.state[item.dataField], obj) ,{}); + + const obj = columns.reduce((obj, item) => Object.assign(obj, { + [item.dataField]: item.dataField === "last_updated" || item.dataField === "created_time" || item.dataField === "modified_time" + ? moment().format("YYYY-MM-DD HH:mm:ssZ"): this.state[item.dataField] + }), {}); + + // console.log("payload insert", obj) + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(obj) + } + this.setState({selectedRows: []}) + try { + const result = await fetch(urlParam, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable(); + this.setState({openDialog: false, alert: true, messageAlert: result.code_message, successAlert: true, dangerAlert: false, ...val}) + } else { + this.setState({openDialog: false, alert: true, messageAlert: result.code_message, successAlert: false, dangerAlert: true, ...val}) + } + } catch { + this.setState({openDialog: false, alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true, ...val}) + } + + } + // end Add Country Function + + dialogContent = () => { + const {editData, openDialog} = this.state + const {columns} = this.props + return ( +
+ + {editData ? "Update data":"Add new"} {this.props.title} + + {columns.map((res, idx) => { + if(res.showInput){ + return ( + + + this.setState({[res.dataField]: e.target.value})} type={res.type} /> + + ) + } + + })} + + + + + {' '} + + +
+ ) + } + + getDataUpdate = () => { + const {selectedRows} = this.state + this.setState({ + ...selectedRows[0], + openDialog: true, + editData: true + }) + } + + updateFunction = async (oldValue, newValue, row, column) => { + let {editCellMode} = this.state + console.log('editCellMode', editCellMode); + // console.log("before save:", {oldValue, newValue, row, column}) + console.log(row) + // row.last_updated = moment().format("YYYY-MM-DD HH:mm:ssZ") + // console.log(row) + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(row) + } + + const result = await fetch(this.props.urlParamUpdate, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable() + this.setState({alert: true, successAlert: true, dangerAlert: false, messageAlert: result.code_message, editCellMode: false}) + } else { + this.setState({alert: true, successAlert: false, dangerAlert: true, messageAlert: result.code_message }) + } + } + + handleDelete = data => { + + } + + deleteFunction = async param => { + const {selectedRows} = this.state + console.log("row selected",selectedRows) + if(param === "delete") { + + const {columns} = this.props + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(selectedRows) + } + + try { + const result = await fetch(this.props.urlParamDelete, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable(); + this.setState({alert: true, alertWarning: false, messageAlert: result.code_message, successAlert: true, dangerAlert: false, ...val}) + } else { + this.setState({alert: true, alertWarning: false, messageAlert: result.code_message, successAlert: false, dangerAlert: true, ...val}) + } + } catch { + this.setState({alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + } else { + this.setState({alertWarning: false}) + } + + + } + + allDeleteFunction = () => { + console.log() + } + + render() { + const { columns, data, selectedRows, selectRow, editCellMode, alert, messageAlert, + alertWarning, deleteData, successAlert, dangerAlert } = this.state; + // console.log("render cellmode", editCellMode) + return ( + +
+ this.setState({alert: false, successAlert: false, dangerAlert: false})}> + {messageAlert} + + + this.deleteFunction("delete")} + onCancel={() => this.deleteFunction("cancel")} + focusCancelBtn + > + This {this.props.title} will be deleted + + + + + + + + + Master { this.props.title !== undefined ? this.props.title : 'DataTable' } + + +
+ + + {this.dialogContent()} +
+ +
+
+ + {data.length === 0 ? ( +
No result found
+ ):( +
+ + { + props => ( +
+ + +
+ +
+ + +
+ + + {/*
+ Edit
+ + this.toggleEditCell()} defaultChecked={editCellMode} /> +
*/} + +
+ {selectedRows.length === 1 && } + {selectedRows.length > 0 && } + + + + + + Export CSV + +
+
+
+ +
+ {/*
*/} +
+ this.editBeforeSave(oldValue, newValue, row, column), + afterSaveCell: (oldValue, newValue, row, column) => this.updateFunction(oldValue, newValue, row, column), + // onErrorMessageDisappear: () => { ... }, + // nonEditableRows: () => { ... } + }) : false } + /> +
+
+ ) + } +
+
+ + )} + +
+
+ +
+
+ ) + } +} + +export default DataTable; \ No newline at end of file diff --git a/src/components/DataTableEditDialog/package.json b/src/components/DataTableEditDialog/package.json new file mode 100644 index 0000000..c232e7f --- /dev/null +++ b/src/components/DataTableEditDialog/package.json @@ -0,0 +1,6 @@ +{ + "name": "DataTable", + "version": "0.0.0", + "private": true, + "main": "./DataTable.js" +} diff --git a/src/components/DefaultImageSlider/DefaultImageSlider.css b/src/components/DefaultImageSlider/DefaultImageSlider.css new file mode 100644 index 0000000..44b3e8a --- /dev/null +++ b/src/components/DefaultImageSlider/DefaultImageSlider.css @@ -0,0 +1,23 @@ +/*.carousel-img { + width: 100%; + height: 100%; + height: 150px; + object-fit: contain; +}*/ + +.carousel-item { + /*position: absolute;*/ +} +.carousel-img { + /*flex-shrink: 0;*/ + max-width: 100%; + max-height: 350px; + /*object-fit: contain;*/ + /*width: 768px;*/ + /*height: 450px; */ + /*float: left;*/ +} +/*.image { + width: 100%; + height: 150px; +}*/ \ No newline at end of file diff --git a/src/components/DefaultImageSlider/DefaultImageSlider.js b/src/components/DefaultImageSlider/DefaultImageSlider.js new file mode 100644 index 0000000..49f6c93 --- /dev/null +++ b/src/components/DefaultImageSlider/DefaultImageSlider.js @@ -0,0 +1,140 @@ +import React, { Component } from 'react'; +import { + Modal, ModalHeader, ModalBody, ModalFooter, Button, Row, Col, + Carousel, + CarouselItem, + CarouselControl, + CarouselIndicators, + CarouselCaption +} from 'reactstrap'; +import './DefaultImageSlider.css'; +import '../../assets/css/customscroll.css'; +import { getImagePopup } from '../../const/GeoserverFunc.js'; +import { BASE_IMAGE } from '../../const/ApiConst.js'; + +class DefaultImageSlider extends Component { + + constructor(props) { + super(props); + this.state = { + fid: '', + activeIndex: 0, + animating: false, + items: [ + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa1d%20text%20%7B%20fill%3A%23555%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa1d%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.921875%22%20y%3D%22218.3%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 1', + caption: 'Slide 1' + }, + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa20%20text%20%7B%20fill%3A%23444%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa20%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23666%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22247.3203125%22%20y%3D%22218.3%22%3ESecond%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 2', + caption: 'Slide 2' + }, + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa21%20text%20%7B%20fill%3A%23333%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa21%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23555%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22277%22%20y%3D%22218.3%22%3EThird%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 3', + caption: 'Slide 3' + } + ] + } + this.setActiveIndex = this.setActiveIndex.bind(this); + this.setAnimating = this.setAnimating.bind(this); + this.next = this.next.bind(this); + this.previous = this.previous.bind(this); + this.goToIndex = this.goToIndex.bind(this); + } + + componentDidMount() { + // const { fid } = this.props; + // console.log('ImageSlider componentDidMount fid',fid); + + } + + componentDidUpdate(prevState, prevProps) { + + } + + /*getImagePopup = async (fid) => { + let resGetImagePopup = await getImagePopup(fid); + console.log('imagePopup resGetImagePopup',resGetImagePopup); + + if (resGetImagePopup.data !== undefined) { + if (resGetImagePopup.data.data.length > 0) { + let imageData = resGetImagePopup.data.data; + let imageArr = []; + for (let i=0; i { + return this.state.items.map((item) => { + return ( + this.setAnimating(true)} + onExited={() => this.setAnimating(false)} + > + {item.altText} + {/**/} + + ); + }); + } + + render() { + return ( + + + {this.slides()} + + + + ) + } +} + +export default DefaultImageSlider \ No newline at end of file diff --git a/src/components/DefaultImageSlider/package.json b/src/components/DefaultImageSlider/package.json new file mode 100644 index 0000000..bff7541 --- /dev/null +++ b/src/components/DefaultImageSlider/package.json @@ -0,0 +1,6 @@ +{ + "name": "DefaultImageSlider", + "version": "0.0.0", + "private": true, + "main": "./DefaultImageSlider.js" +} \ No newline at end of file diff --git a/src/components/DialogConfirmation/DialogConfirmation.css b/src/components/DialogConfirmation/DialogConfirmation.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/DialogConfirmation/DialogConfirmation.js b/src/components/DialogConfirmation/DialogConfirmation.js new file mode 100644 index 0000000..f8b6343 --- /dev/null +++ b/src/components/DialogConfirmation/DialogConfirmation.js @@ -0,0 +1,51 @@ +import React, { Component } from 'react'; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'; +import './DialogConfirmation.css'; + +class DialogConfirmation extends Component { + + + render() { + return ( + + Delete Layer + + Are you sure you want to delete this layer? + + + {' '} + + + + ) + } +} + +/*const DialogConfirmation = (props) => { + const { + buttonLabel, + className + } = props; + + const [modal, setModal] = useState(false); + + const toggle = () => setModal(!modal); + + return ( +
+ + + Modal title + + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + + + {' '} + + + +
+ ); +}*/ + +export default DialogConfirmation; \ No newline at end of file diff --git a/src/components/DialogConfirmation/package.json b/src/components/DialogConfirmation/package.json new file mode 100644 index 0000000..e69de29 diff --git a/src/components/DrawingTool/DrawingTool.css b/src/components/DrawingTool/DrawingTool.css new file mode 100644 index 0000000..3824f81 --- /dev/null +++ b/src/components/DrawingTool/DrawingTool.css @@ -0,0 +1,63 @@ +.mapBarContainer { + text-align: center; +} + +.mapbarContent { + display: block; + position: absolute; + top: 50px; + left: 45%; + margin-left: -50px; + min-width: 175px; + max-width: 100%; + padding: 5px; + z-index: 50; + background-color: rgba(0,0,0,0.5); +} + +.layerDrawDetails { + color: #ffffff; + line-height: 17px; +} + +.titleDrawing { + font-size: 15px; + font-weight: bold; +} + +.layerDrawName { + font-size: 12px; +} + +.layerDrawGeomType { + font-size: 12px; +} + +.drawButtonGroup { + margin-top: -5px; +} + +.addRemoveFeatureDraw { + display: none; +} + +.draw-operation-group { + float: left; + margin-left: 10px; + margin-right: 10px; +} + +.draw-option-group { + float: right; + margin-left: 10px; + margin-right: 10px; + margin-top: -10px; +} + +.draw-option { + margin-left: 10px; +} + +.text-white { + color: #FFFFFF; +} \ No newline at end of file diff --git a/src/components/DrawingTool/DrawingTool.js b/src/components/DrawingTool/DrawingTool.js new file mode 100644 index 0000000..76d9cb8 --- /dev/null +++ b/src/components/DrawingTool/DrawingTool.js @@ -0,0 +1,674 @@ +import React, { Component } from 'react'; +import { Button, ButtonGroup, UncontrolledTooltip } from 'reactstrap'; +import { Icon, InlineIcon } from '@iconify/react'; +import closeCircleOutline from '@iconify/icons-ion/close-circle-outline'; +import checkmarkCircleOutline from '@iconify/icons-ion/checkmark-circle-outline'; +import turfDestination from '@iconify/icons-geo/turf-destination'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import addCircleOutline from '@iconify/icons-ion/add-circle-outline'; +import removeCircleOutline from '@iconify/icons-ion/remove-circle-outline'; +import {Draw, Modify, Snap, Select, DragBox, DragAndDrop, Translate} from 'ol/interaction'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Vector as VectorSource} from 'ol/source'; +import {Circle as CircleStyle, Fill, Stroke, Style, Text} from 'ol/style'; +import { AppSwitch } from '@coreui/react'; +import DigitizeButton from '@terrestris/react-geo/dist/Button/DigitizeButton/DigitizeButton'; +import './DrawingTool.css'; +import { updateFeatureGeometry } from '../../const/GeoserverFunc.js'; + +/*const olmap = this.props.olmap; +const vector = new VectorLayer({ + source: new VectorSource(), + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#ffcc33', + // color: '#ff9900', + // color: '#2e72d9', + width: 3 + }), + image: new CircleStyle({ + radius: 7, + fill: new Fill({ + color: '#ffcc33' + }) + }) + }), + zIndex: 99 +}); + +const ExampleDraw = { + init: function() { + olmap.addInteraction(this.Point); + this.Point.setActive(false); + olmap.addInteraction(this.LineString); + this.LineString.setActive(false); + olmap.addInteraction(this.Polygon); + this.Polygon.setActive(false); + olmap.addInteraction(this.Circle); + this.Circle.setActive(false); + }, + Point: new Draw({ + source: vector.getSource(), + type: 'Point' + }), + LineString: new Draw({ + source: vector.getSource(), + type: 'LineString' + }), + Polygon: new Draw({ + source: vector.getSource(), + type: 'Polygon' + }), + Circle: new Draw({ + source: vector.getSource(), + type: 'Circle' + }), + getActive: function() { + return this.activeType ? this[this.activeType].getActive() : false; + }, + setActive: function(type, active) { + if (active) { + this.activeType && this[this.activeType].setActive(false); + this[type].setActive(true); + this.activeType = type; + } else { + this.activeType && this[this.activeType].setActive(false); + this.activeType = null; + } + } +}; +ExampleDraw.init();*/ + +class DrawingTool extends Component { + + /* + props on this component: + visible + layerName + geomType + removeFeature + saveDraw + olmap*/ + + constructor(props) { + super(props) + // this.cancelDraw = this.cancelDraw.bind(this); + this.toggleSnapping = this.toggleSnapping.bind(this); + this.toggleSelect = this.toggleSelect.bind(this); + this.toggleModify = this.toggleModify.bind(this); + this.toggleDrag = this.toggleDrag.bind(this); + this.checkDrawingVisible = this.checkDrawingVisible.bind(this); + const olmap = this.props.olmap; + const drawingLayer = new VectorLayer({ + name: 'DrawingLayer', + source: new VectorSource(), + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#ffcc33', + // color: '#ff9900', + // color: '#2e72d9', + width: 3 + }), + image: new CircleStyle({ + radius: 7, + fill: new Fill({ + color: '#ffcc33' + }) + }) + }), + zIndex: 99, + type: 'vector' + }); + + const ExampleDraw = { + init: function() { + olmap.addInteraction(this.Point); + this.Point.setActive(false); + olmap.addInteraction(this.MultiPoint); + this.MultiPoint.setActive(false); + olmap.addInteraction(this.LineString); + this.LineString.setActive(false); + olmap.addInteraction(this.MultiLineString); + this.MultiLineString.setActive(false); + olmap.addInteraction(this.Polygon); + this.Polygon.setActive(false); + olmap.addInteraction(this.MultiPolygon); + this.MultiPolygon.setActive(false); + olmap.addInteraction(this.Circle); + this.Circle.setActive(false); + }, + Point: new Draw({ + source: drawingLayer.getSource(), + type: 'Point' + }), + MultiPoint: new Draw({ + source: drawingLayer.getSource(), + type: 'MultiPoint' + }), + LineString: new Draw({ + source: drawingLayer.getSource(), + type: 'LineString' + }), + MultiLineString: new Draw({ + source: drawingLayer.getSource(), + type: 'MultiLineString' + }), + Polygon: new Draw({ + source: drawingLayer.getSource(), + type: 'Polygon' + }), + MultiPolygon: new Draw({ + source: drawingLayer.getSource(), + type: 'MultiPolygon' + }), + Circle: new Draw({ + source: drawingLayer.getSource(), + type: 'Circle' + }), + getActive: function() { + return this.activeType ? this[this.activeType].getActive() : false; + }, + setActive: function(type, active) { + if (active) { + this.activeType && this[this.activeType].setActive(false); + this[type].setActive(true); + this.activeType = type; + } else { + this.activeType && this[this.activeType].setActive(false); + this.activeType = null; + } + }, + getActiveType: function() { + return this.activeType; + }, + destroy: function() { + olmap.removeInteraction(this.Point); + this.Point.setActive(false); + olmap.removeInteraction(this.MultiPoint); + this.MultiPoint.setActive(false); + olmap.removeInteraction(this.LineString); + this.LineString.setActive(false); + olmap.removeInteraction(this.MultiLineString); + this.MultiLineString.setActive(false); + olmap.removeInteraction(this.Polygon); + this.Polygon.setActive(false); + olmap.removeInteraction(this.MultiPolygon); + this.MultiPolygon.setActive(false); + olmap.removeInteraction(this.Circle); + this.Circle.setActive(false); + } + }; + // ExampleDraw.init(); + + // console.log(ExampleDraw.init()); + + // The snap interaction must be added after the Modify and Draw interactions + // in order for its map browser event handlers to be fired first. Its handlers + // are responsible of doing the snapping. + const snap = new Snap({ + source: drawingLayer.getSource() + }); + + const select = new Select(); + + const modify = new Modify({ + source: drawingLayer.getSource(), + features: select.getFeatures() + // features: drawingLayer.getSource().getFeatures() + }); + + const drag = new Translate({ + features: select.getFeatures() + }); + + this.state = { + DrawingUtil: ExampleDraw, + // drawingType: this.props.geomType, + drawingLayer: drawingLayer, + snap: snap, + snapMode: false, + select: select, + selectMode: false, + selectedFeatures: null, + modify: modify, + modifyMode: false, + drag: drag, + dragMode: false, + isMultiGeom: false, + chosenLayer: null, + modifySelectedFeature: null + } + } + + componentDidMount() { + const { mode } = this.props; + if (mode == 'add') { + this.checkDrawingVisible(); + } + else if (mode == 'edit') { + this.editSelectedFeature(); + } + } + + componentDidUpdate(prevProps, prevState) { + if (this.props.layerName !== prevProps.layerName) { + this.rebuildDrawPanel(); + } + if (this.state.modifyMode) { + this.enableModify(); + } + else if (!this.state.modifyMode) { + this.disableModify(); + } + if (this.state.snapMode) { + this.props.olmap.addInteraction(this.state.snap); + } + else if (!this.state.snapMode) { + this.props.olmap.removeInteraction(this.state.snap); + } + } + + rebuildDrawPanel = () => { + const { drawingLayer } = this.state; + // Clear the drawing drawingLayer in drawingLayer layer + drawingLayer.getSource().clear(); + + // Set snapping mode to false + this.setState({snapMode: false}); + + this.disableModify(); + this.checkDrawingVisible(); + } + + enableModify = () => { + this.props.olmap.addInteraction(this.state.select); + this.props.olmap.addInteraction(this.state.modify); + this.state.DrawingUtil.setActive(null, false); + this.state.DrawingUtil.destroy(); + + } + + disableModify = () => { + this.props.olmap.removeInteraction(this.state.select); + this.props.olmap.removeInteraction(this.state.modify); + this.state.DrawingUtil.setActive(this.props.geomType, true); + } + + checkDrawingVisible() { + const { DrawingUtil, drawingLayer, snap, snapMode, modify } = this.state; + const olmap = this.props.olmap; + let drawingExist = false; + + // Check if Vector Layer named DrawingLayer exist + olmap.getLayers().forEach(function (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'DrawingLayer') { + drawingExist = true; + } + else { + drawingExist = false; + } + }); + + // if DrawingLayer is not exist, START DRAWING HERE!! + if (!drawingExist) { + // then add drawingLayer layer to map + olmap.addLayer(drawingLayer); + + // Enable modify drawingLayer layer + // olmap.addInteraction(modify); + + // + } + + DrawingUtil.init(); + DrawingUtil.setActive(this.props.geomType, true); + // console.log('DrawingUtil', DrawingUtil); + // console.log('DrawingUtil getActive()',DrawingUtil.getActive()); + // console.log('getActiveType', DrawingUtil.getActiveType()); + + let drawingActiveType = DrawingUtil.getActiveType(); + console.log('drawingActiveType', drawingActiveType); + + // console.log('isMultiGeom', drawingActiveType.toLowerCase().includes('multi')); + + if (drawingActiveType.toLowerCase().includes('multi')) { + this.setState({isMultiGeom: true}); + } + else { + this.setState({isMultiGeom: false}); + } + + DrawingUtil[drawingActiveType].on('drawend', (evt) => { + // this.enableModify(); + this.setState({modifyMode: true}); + }, this) + + } + + cancelDraw = () => { + const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + + // Set Drawing type to null and active is false + DrawingUtil.setActive(null, false); + DrawingUtil.destroy(); + + // Remove interaction, so user can click to map to select other features + this.props.olmap.removeInteraction(this.state.select); + this.props.olmap.removeInteraction(this.state.modify); + + // Clear the drawing drawingLayer in drawingLayer layer + if (drawingLayer) { + drawingLayer.getSource().clear(); + } + if (chosenLayer) { + chosenLayer.getSource().clear(); + } + + // Removing DrawingLayer and ChosenLayer layer + let layersToRemove = []; + this.props.olmap.getLayers().forEach(function (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'DrawingLayer') { + layersToRemove.push(layer); + } + if (layer.get('name') !== undefined && layer.get('name') == 'ChosenLayer') { + layersToRemove.push(layer); + } + }); + for(var i = 0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + + // Set snapping mode to false + this.setState({snapMode: false}); + + // Calling props cancelDraw in MapToolbar.js + this.props.cancelDraw(); + } + + toggleSnapping() { + // let furtherSnapMode = null; + // if (this.state.snapMode === true) { + // furtherSnapMode = false; + // } + // else { + // furtherSnapMode = true; + // } + // this.setState({ snapMode: furtherSnapMode }, () => { + // this.state.snapMode ? this.props.olmap.addInteraction(this.state.snap) : this.props.olmap.removeInteraction(this.state.snap); + // }); + + this.setState({snapMode: !this.state.snapMode}); + } + + toggleSelect() { + this.setState({selectMode: !this.state.selectMode}); + } + + toggleModify() { + this.setState({ + selectMode: !this.state.selectMode, // select interaction should be enabled + modifyMode: !this.state.modifyMode + }); + } + + toggleDrag() { + this.setState({dragMode: !this.state.dragMode}); + } + + addNewGeometry = () => { + const { DrawingUtil } = this.state; + DrawingUtil.init(); + this.setState({ modifyMode: false }); + } + + removeSelectedGeometry = () => { + const { olmap } = this.props; + olmap.getLayers().forEach((layer, i) => { + console.log('layer', layer); + }) + } + + saveDraw = async () => { + const { mode } = this.props; + + // if mode == add then showing modal to fill the attributes + if (mode == 'add') { + const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + let featuresToSave = []; + let features = drawingLayer.getSource().getFeatures(); + // let coords = null; + let obj = { + layer_name: this.props.layerName, + layer_geom_type: this.props.geomType, + map_projection: this.props.olmap.getView().getProjection().code_, + coordinates: [], + layer_attributes: null + } + console.log('obj', obj); + + if (features.length < 1) { + alert('Please create a feature before continuing'); + return; + } + else { + // if (features.length > 0) { + features.forEach((feature, i) => { + console.log('feature', feature); + obj.coordinates[i] = feature.getGeometry().getCoordinates(); + // console.log(obj); + }); + } + + this.props.setAddFeatureValue(obj); + this.props.toggleAddFeatureVisible(); + this.props.setDrawingState(DrawingUtil, drawingLayer, chosenLayer); + } + // if mode == edit, just update the geometry without filling the attributes + else if (mode == 'edit') { + console.log('mode ediitttt'); + let confirmation = window.confirm('Are you sure you want to update this feature geometry?'); + if (!confirmation) { + return; + } + const { modifySelectedFeature, chosenLayer } = this.state; + let featuresToSave = []; + let features = chosenLayer.getSource().getFeatures(); + // let coords = null; + let obj = { + layer_name: this.props.layerName, + layer_geom_type: this.props.geomType, + map_projection: this.props.olmap.getView().getProjection().code_, + coordinates: [], + layer_attributes: null + } + console.log('obj', obj); + + features.forEach((feature, i) => { + // console.log('feature', feature); + obj.coordinates[i] = feature.getGeometry().getCoordinates(); + // console.log(obj); + }); + + let response = await updateFeatureGeometry(obj); + if (response.success) { + alert('Successfully update feature geometry.'); + window.location.reload(); + } + else { + console.log('error', response.result); + alert('Failed to update feature geometry'); + return; + } + + } + + } + + editSelectedFeature = () => { + // const { DrawingUtil } = this.state; + // DrawingUtil.init(); + // DrawingUtil.setActive(this.props.geomType, true); + // this.enableModify(); + this.state.DrawingUtil.setActive(null, false); + this.state.DrawingUtil.destroy(); + + let modifySelectedFeature = null; + let chosenLayer = null; + + + /* + + DrawingUtil.init(); + DrawingUtil.setActive(this.props.geomType, true); + + let drawingActiveType = DrawingUtil.getActiveType(); + console.log('drawingActiveType', drawingActiveType); + + // console.log('isMultiGeom', drawingActiveType.toLowerCase().includes('multi')); + + if (drawingActiveType.toLowerCase().includes('multi')) { + this.setState({isMultiGeom: true}); + } + else { + this.setState({isMultiGeom: false}); + }*/ + + if (this.props.geomType.toLowerCase().includes('multi')) { + this.setState({isMultiGeom: true}); + } + else { + this.setState({isMultiGeom: false}); + } + + // enable edit on ChosenLayer vector source + this.props.olmap.getLayers().forEach(function (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + modifySelectedFeature = new Modify({ + source: layer.getSource(), + features: layer.getSource().getFeatures() + }); + } + }); + if (!modifySelectedFeature) { + alert('Please select a feature to edit'); + return; + } + else { + console.log(modifySelectedFeature); + this.props.olmap.addInteraction(this.state.select); + this.props.olmap.addInteraction(modifySelectedFeature); + this.setState({chosenLayer: chosenLayer, modifySelectedFeature: modifySelectedFeature}); + } + } + + render() { + + const { snapMode, modifyMode, dragMode, isMultiGeom } = this.state; + + return ( +
+
+

+ {this.props.title}
+ {this.props.layerName ? this.props.layerName : "Layer Name is not defined"}
+ {this.props.geomType} +

+
+ {/*
+ + +
*/} + +
+ {/**/} + + + + Cancel Feature + + + {/*{ modifyMode ? + + : null }*/} + + { isMultiGeom && + + + + Add New Geometry + + + + Remove Selected Geometry + + + } + {/* + */} + + {/**/} + + + Accept Feature + +
+ +
+
+ Snapping
+ { snapMode ? + + : + } +
+ + {/*
+ Modify
+ { modifyMode ? + + : + } +
*/} + + {/*
+ Drag
+ { dragMode ? + + : + } +
*/} +
+
+
+
+ ) + + } +} + +export default DrawingTool; \ No newline at end of file diff --git a/src/components/DrawingTool/package.json b/src/components/DrawingTool/package.json new file mode 100644 index 0000000..caad0fa --- /dev/null +++ b/src/components/DrawingTool/package.json @@ -0,0 +1,6 @@ +{ + "name": "DrawingTool", + "version": "0.0.0", + "private": true, + "main": "./DrawingTool.js" +} diff --git a/src/components/EditFeature/EditFeature.css b/src/components/EditFeature/EditFeature.css new file mode 100644 index 0000000..8d7b4af --- /dev/null +++ b/src/components/EditFeature/EditFeature.css @@ -0,0 +1,9 @@ +.edit-input-container { + max-height: 300px; + overflow: auto; +} + +.row-input { + margin-right: 0px !important; + margin-left: -10px !important; +} \ No newline at end of file diff --git a/src/components/EditFeature/EditFeature.js b/src/components/EditFeature/EditFeature.js new file mode 100644 index 0000000..6ea1ca3 --- /dev/null +++ b/src/components/EditFeature/EditFeature.js @@ -0,0 +1,169 @@ +import React, { Component, Fragment } from 'react'; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem, +Form, FormGroup, Label, Input, Row, Col } from 'reactstrap'; +import { Icon } from '@iconify/react'; +import removeCircle from '@iconify/icons-ion/remove-circle'; +import './EditFeature.css'; +import '../../assets/css/customscroll.css'; +import { updateFeature } from '../../const/GeoserverFunc.js'; +import { findWhere } from 'underscore'; +import { formatLabel } from '../../const/CustomFunc.js'; + +class EditFeature extends Component { + /* + Properties that EditFeature Component need: + editFeatureVisible={this.state.editFeatureVisible} + toggleEditFeature={() => this.toggleEditFeature()} + editFeatureTitle={this.state.editFeatureTitle ? this.state.editFeatureTitle : 'Edit Attribute'} + data={this.state.selectedPopupData} + layerName={this.props.layerName} + */ + constructor(props) { + super(props); + this.state = { + dataArr: null, + fid: '' + } + } + + componentDidMount() { + + const { data } = this.props; + // console.log('EditFeature data', data); + let properties = data.properties; + let dataArr = []; + let i = 0; + + // convert obj to array so I can render to component with .map function + + for (let k in properties) { + dataArr.push({ + idx: i++, + label: k, + value: properties[k] + }); + } + + this.setState({fid: data.id, dataArr: dataArr}); + + } + + matchLayerAttribute = (propKey) => { + console.log('[EditFeature] matchLayerAttribute propKey', propKey); + const { layer_attribute } = this.props; + console.log('[EditFeature] layer_attribute', layer_attribute); + if (layer_attribute.length > 0) { + for (let i=0; i < layer_attribute.length; i++) { + if (layer_attribute[i].attribute !== 'the_geom') { + // skip the_geom + if (layer_attribute[i].attribute_label !== null && layer_attribute[i].attribute_label !== '' ) { + if (propKey == layer_attribute[i].attribute) { + // console.log(layer_attribute[i].attribute_label); + return layer_attribute[i].attribute_label; + } + } + else { + if (propKey == layer_attribute[i].attribute) { + // console.log(layer_attribute[i].attribute); + return layer_attribute[i].attribute; + } + } + } + } + } + else { + let output = formatLabel(propKey); + return output; + } + } + + renderEditInputs = () => { + + const { dataArr } = this.state; + + // console.log('renderEditInputs', dataArr); + return dataArr.map((item, index) => { + return ( + + + + {/**/} + + + + + + ) + }); + } + + handleChangeInputPropertyValue = (idx, event) => { + event.preventDefault(); + let value = event.target.value; + let { dataArr } = this.state; + let changedItem = findWhere(dataArr, {idx: idx}); + changedItem.value = value; + this.setState({dataArr: dataArr}, () => { + // console.log('dataArr now', this.state.dataArr); + }); + } + + updateFeature = async (e) => { + e.preventDefault(); + let { layerName } = this.props; + let { fid, dataArr } = this.state; + let response = await updateFeature(layerName, fid, dataArr); + + console.log('updateFeature response', response); + + /*let parser = new DOMParser(); + let xmlDoc = null; + if (response.data !== undefined) { + xmlDoc = parser.parseFromString(response.data, "text/xml"); + } + + console.log('xmlDoc',xmlDoc); + alert('Update success!');*/ + + if (response.success) { + alert(response.result); + this.props.toggleEditFeature(); + this.props.closePopupRight(); + // this.props.setPopupDataTemp() + } + else { + alert(response.result); + return; + } + } + + render() { + const { dataArr } = this.state; + return( +
+ + {this.props.editFeatureTitle ? this.props.editFeatureTitle : 'Edit Feature' } + + {dataArr !== undefined ? + dataArr !== null ? +
+
this.updateFeature(e)}> + { this.renderEditInputs() } +
+
+ : 'Data is null' + : 'Data is undefined' + } +
+ + + + +
+
+ ) + } +} + +export default EditFeature; \ No newline at end of file diff --git a/src/components/EditFeature/package.json b/src/components/EditFeature/package.json new file mode 100644 index 0000000..fccfa7f --- /dev/null +++ b/src/components/EditFeature/package.json @@ -0,0 +1,6 @@ +{ + "name": "EditFeature", + "version": "0.0.0", + "private": true, + "main": "./EditFeature.js" +} \ No newline at end of file diff --git a/src/components/EditTableColumn/EditTableColumn.css b/src/components/EditTableColumn/EditTableColumn.css new file mode 100644 index 0000000..d4bb2e1 --- /dev/null +++ b/src/components/EditTableColumn/EditTableColumn.css @@ -0,0 +1,13 @@ +.column-input-container { + max-height: 300px; + overflow: auto; +} + +.row-input { + margin-right: 0px !important; + margin-left: -10px !important; +} + +.add-column-button-container{ + float: right; +} \ No newline at end of file diff --git a/src/components/EditTableColumn/EditTableColumn.js b/src/components/EditTableColumn/EditTableColumn.js new file mode 100644 index 0000000..54f1de3 --- /dev/null +++ b/src/components/EditTableColumn/EditTableColumn.js @@ -0,0 +1,190 @@ +import React, { Component, Fragment } from 'react'; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem, +Form, FormGroup, Label, Input, Row, Col } from 'reactstrap'; +import { Icon } from '@iconify/react'; +import removeCircle from '@iconify/icons-ion/remove-circle'; +import './EditTableColumn.css'; +import '../../assets/css/customscroll.css'; +import { findWhere, without } from 'underscore'; + +class EditTableColumn extends Component { + constructor(props) { + super(props); + this.state = { + columns: [] + } + } + + componentDidMount() { + let newColumns = []; + console.log('editedColumnsData',this.props.editedColumnsData); + if (this.props.editedColumnsData !== undefined) { + this.props.editedColumnsData.map((item, index) => { + newColumns.push({ + idx: index, + column_name: item.column_name, + column_type: item.column_type + }) + }) + console.log('newColumns', newColumns); + this.setState({columns: newColumns}); + } + } + + renderColumnsHeader = () => { + return ( + + + + + + + + + + + + + + + + + + ) + } + + renderColumnsInput = () => { + const { columns } = this.state; + console.log('renderColumnsInput', columns); + return columns.map((item, index) => { + return ( + + + + + + + + + + + + + + + + + + + + + + + ) + }); + } + + renderAddColumnBtn = () => { + return ( + + + + + + + + + ) + } + + handleChangeInputColumnName(idx, event) { + // dicari state columns terus dicari idx nya, kemudian set text nya + + let { columns } = this.state; + let changedItem = findWhere(columns, {idx: idx}); + let value = event.target.value.split(" ").join(""); + changedItem.column_name = value; + console.log('changedItem', changedItem); + this.setState({columns: columns}); + console.log('columns', this.state.columns); + } + + handleChangeInputColumnType(idx, event) { + let { columns } = this.state; + let changedItem = findWhere(columns, {idx: idx}); + // let value = event.target.value.split(" ").join(""); + let value = event.target.value; + changedItem.column_type = value; + console.log('changedItem', changedItem); + this.setState({columns: columns}); + console.log('columns', this.state.columns); + } + + addColumn = () => { + let { columns } = this.state; + let maxIdx = null; + let newIdx = null; + if (columns.length < 1) { + newIdx = 1; + } + else { + maxIdx = Math.max.apply(Math, columns.map((column) => column.idx)); + console.log('maxIdx',maxIdx); + newIdx = maxIdx + 1; + } + let oneColumn = { + idx: newIdx, + column_name: "", + column_type: "" + } + this.setState({columns: [...columns, oneColumn]}, () => { + console.log(this.state.columns) + this.renderColumnsInput(); + }); + } + + deleteColumn = (idx) => { + console.log('deleteColumn', idx); + let { columns } = this.state; + console.log('columns', columns); + let deletedItem = findWhere(columns, {idx: idx}); + console.log(deletedItem); + let afterRemove = without(columns, deletedItem); + console.log(console.log('afterRemove', afterRemove)); + this.setState({columns: afterRemove}); + } + + updateTableColumn = () => { + + } + + render() { + const { columns } = this.state; + return( + + {this.props.modalTitle ? this.props.modalTitle : 'Edit Layer Attribute' } + + {columns !== undefined ? + columns !== null ? +
+
+ { this.renderColumnsHeader() } + { this.renderColumnsInput() } + { this.renderAddColumnBtn() } +
+
+ : 'Columns is null' + : 'Columns is undefined' + } +
+ + + + +
+ ) + } +} + +export default EditTableColumn; \ No newline at end of file diff --git a/src/components/EditTableColumn/EditTableColumn_backup.js b/src/components/EditTableColumn/EditTableColumn_backup.js new file mode 100644 index 0000000..54f1de3 --- /dev/null +++ b/src/components/EditTableColumn/EditTableColumn_backup.js @@ -0,0 +1,190 @@ +import React, { Component, Fragment } from 'react'; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem, +Form, FormGroup, Label, Input, Row, Col } from 'reactstrap'; +import { Icon } from '@iconify/react'; +import removeCircle from '@iconify/icons-ion/remove-circle'; +import './EditTableColumn.css'; +import '../../assets/css/customscroll.css'; +import { findWhere, without } from 'underscore'; + +class EditTableColumn extends Component { + constructor(props) { + super(props); + this.state = { + columns: [] + } + } + + componentDidMount() { + let newColumns = []; + console.log('editedColumnsData',this.props.editedColumnsData); + if (this.props.editedColumnsData !== undefined) { + this.props.editedColumnsData.map((item, index) => { + newColumns.push({ + idx: index, + column_name: item.column_name, + column_type: item.column_type + }) + }) + console.log('newColumns', newColumns); + this.setState({columns: newColumns}); + } + } + + renderColumnsHeader = () => { + return ( + + + + + + + + + + + + + + + + + + ) + } + + renderColumnsInput = () => { + const { columns } = this.state; + console.log('renderColumnsInput', columns); + return columns.map((item, index) => { + return ( + + + + + + + + + + + + + + + + + + + + + + + ) + }); + } + + renderAddColumnBtn = () => { + return ( + + + + + + + + + ) + } + + handleChangeInputColumnName(idx, event) { + // dicari state columns terus dicari idx nya, kemudian set text nya + + let { columns } = this.state; + let changedItem = findWhere(columns, {idx: idx}); + let value = event.target.value.split(" ").join(""); + changedItem.column_name = value; + console.log('changedItem', changedItem); + this.setState({columns: columns}); + console.log('columns', this.state.columns); + } + + handleChangeInputColumnType(idx, event) { + let { columns } = this.state; + let changedItem = findWhere(columns, {idx: idx}); + // let value = event.target.value.split(" ").join(""); + let value = event.target.value; + changedItem.column_type = value; + console.log('changedItem', changedItem); + this.setState({columns: columns}); + console.log('columns', this.state.columns); + } + + addColumn = () => { + let { columns } = this.state; + let maxIdx = null; + let newIdx = null; + if (columns.length < 1) { + newIdx = 1; + } + else { + maxIdx = Math.max.apply(Math, columns.map((column) => column.idx)); + console.log('maxIdx',maxIdx); + newIdx = maxIdx + 1; + } + let oneColumn = { + idx: newIdx, + column_name: "", + column_type: "" + } + this.setState({columns: [...columns, oneColumn]}, () => { + console.log(this.state.columns) + this.renderColumnsInput(); + }); + } + + deleteColumn = (idx) => { + console.log('deleteColumn', idx); + let { columns } = this.state; + console.log('columns', columns); + let deletedItem = findWhere(columns, {idx: idx}); + console.log(deletedItem); + let afterRemove = without(columns, deletedItem); + console.log(console.log('afterRemove', afterRemove)); + this.setState({columns: afterRemove}); + } + + updateTableColumn = () => { + + } + + render() { + const { columns } = this.state; + return( + + {this.props.modalTitle ? this.props.modalTitle : 'Edit Layer Attribute' } + + {columns !== undefined ? + columns !== null ? +
+
+ { this.renderColumnsHeader() } + { this.renderColumnsInput() } + { this.renderAddColumnBtn() } +
+
+ : 'Columns is null' + : 'Columns is undefined' + } +
+ + + + +
+ ) + } +} + +export default EditTableColumn; \ No newline at end of file diff --git a/src/components/EditTableColumn/package.json b/src/components/EditTableColumn/package.json new file mode 100644 index 0000000..2391c1d --- /dev/null +++ b/src/components/EditTableColumn/package.json @@ -0,0 +1,6 @@ +{ + "name": "EditTableColumn", + "version": "0.0.0", + "private": true, + "main": "./EditTableColumn.js" +} diff --git a/src/components/ImagePopup/ImagePopup.css b/src/components/ImagePopup/ImagePopup.css new file mode 100644 index 0000000..46eb1dc --- /dev/null +++ b/src/components/ImagePopup/ImagePopup.css @@ -0,0 +1,9 @@ +.image-modal-container { + /*height: 300px;*/ + width: 100%; + max-height: 400px; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden +} \ No newline at end of file diff --git a/src/components/ImagePopup/ImagePopup.js b/src/components/ImagePopup/ImagePopup.js new file mode 100644 index 0000000..ed61e54 --- /dev/null +++ b/src/components/ImagePopup/ImagePopup.js @@ -0,0 +1,87 @@ +import React, { Component } from 'react'; +import { + Modal, ModalHeader, ModalBody, ModalFooter, Button, Row, Col, + Carousel, + CarouselItem, + CarouselControl, + CarouselIndicators, + CarouselCaption +} from 'reactstrap'; +import './ImagePopup.css'; +import '../../assets/css/customscroll.css'; +import ImageSlider from '../ImageSlider'; +// import { getImagePopup } from '../../const/GeoserverFunc.js'; +// import { BASE_IMAGE } from '../../const/ApiConst.js'; + +class ImagePopup extends Component { + + constructor(props) { + super(props); + this.state = { + imageArr: [] + } + } + + componentDidMount() { + // this.setState() + // if () {} =====> if fid is Feauture Image ====>> should show error getting image + + // this.getImagePopup(imagePopupTitle); + } + + /*getImagePopup = async (fid) => { + let resGetImagePopup = await getImagePopup(fid); + console.log('imagePopup resGetImagePopup',resGetImagePopup); + + if (resGetImagePopup.data !== undefined) { + if (resGetImagePopup.data.data.length > 0) { + let imageData = resGetImagePopup.data.data; + let imageArr = []; + for (let i=0; i + {this.props.imagePopupTitle ? this.props.imagePopupTitle : 'Feature Image'} + +
+ +
+
+ {/*List*/} +
+
+ + this.fileInputFrontLicense = fileInputFrontLicense} /> + {/* */} + + + + + ) + } +} + +export default ImagePopup; \ No newline at end of file diff --git a/src/components/ImagePopup/package.json b/src/components/ImagePopup/package.json new file mode 100644 index 0000000..a487fde --- /dev/null +++ b/src/components/ImagePopup/package.json @@ -0,0 +1,6 @@ +{ + "name": "ImagePopup", + "version": "0.0.0", + "private": true, + "main": "./ImagePopup.js" +} \ No newline at end of file diff --git a/src/components/ImagePopupPreview/ImagePopup.css b/src/components/ImagePopupPreview/ImagePopup.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/ImagePopupPreview/ImagePopupPreview.js b/src/components/ImagePopupPreview/ImagePopupPreview.js new file mode 100644 index 0000000..88875c9 --- /dev/null +++ b/src/components/ImagePopupPreview/ImagePopupPreview.js @@ -0,0 +1,43 @@ +import React, { Component } from 'react'; +import { + Modal, ModalHeader, ModalBody, ModalFooter, Button, Row, Col, + Carousel, + CarouselItem, + CarouselControl, + CarouselIndicators, + CarouselCaption +} from 'reactstrap'; +import './ImagePopup.css'; +import '../../assets/css/customscroll.css'; +import ImageSlider from '../ImageSlider'; + +class ImagePopupPreview extends Component { + + constructor(props) { + super(props); + } + + render() { + return ( + this.props.toggleImagePopup("cancel")} + className="modal-dialog-newlayer" + contentClassName="modal-content-newlayer" + size="lg" + > + this.props.toggleImagePopup("cancel")}>Preview Image + + {/* */} + + + + + + + + ) + } +} + +export default ImagePopupPreview; \ No newline at end of file diff --git a/src/components/ImagePopupPreview/package.json b/src/components/ImagePopupPreview/package.json new file mode 100644 index 0000000..a487fde --- /dev/null +++ b/src/components/ImagePopupPreview/package.json @@ -0,0 +1,6 @@ +{ + "name": "ImagePopup", + "version": "0.0.0", + "private": true, + "main": "./ImagePopup.js" +} \ No newline at end of file diff --git a/src/components/ImageSlider/ImageSlider.css b/src/components/ImageSlider/ImageSlider.css new file mode 100644 index 0000000..44b3e8a --- /dev/null +++ b/src/components/ImageSlider/ImageSlider.css @@ -0,0 +1,23 @@ +/*.carousel-img { + width: 100%; + height: 100%; + height: 150px; + object-fit: contain; +}*/ + +.carousel-item { + /*position: absolute;*/ +} +.carousel-img { + /*flex-shrink: 0;*/ + max-width: 100%; + max-height: 350px; + /*object-fit: contain;*/ + /*width: 768px;*/ + /*height: 450px; */ + /*float: left;*/ +} +/*.image { + width: 100%; + height: 150px; +}*/ \ No newline at end of file diff --git a/src/components/ImageSlider/ImageSlider.js b/src/components/ImageSlider/ImageSlider.js new file mode 100644 index 0000000..2262141 --- /dev/null +++ b/src/components/ImageSlider/ImageSlider.js @@ -0,0 +1,159 @@ +import React, { Component } from 'react'; +import { + Modal, ModalHeader, ModalBody, ModalFooter, Button, Row, Col, + Carousel, + CarouselItem, + CarouselControl, + CarouselIndicators, + CarouselCaption +} from 'reactstrap'; +import { Image } from 'antd'; +import './ImageSlider.css'; +import '../../assets/css/customscroll.css'; +import { getImagePopup } from '../../const/GeoserverFunc.js'; +import { BASE_IMAGE, OSPRO_BASE_IMAGE, SIMPRO_BASE_IMAGE } from '../../const/ApiConst.js'; + +const DEFAULT_IMAGE_SLIDER = [ + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa1d%20text%20%7B%20fill%3A%23555%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa1d%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.921875%22%20y%3D%22218.3%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 1', + caption: 'Slide 1' + }, + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa20%20text%20%7B%20fill%3A%23444%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa20%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23666%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22247.3203125%22%20y%3D%22218.3%22%3ESecond%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 2', + caption: 'Slide 2' + }, + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa21%20text%20%7B%20fill%3A%23333%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa21%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23555%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22277%22%20y%3D%22218.3%22%3EThird%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 3', + caption: 'Slide 3' + } +]; + +class ImageSlider extends Component { + + constructor(props) { + super(props); + this.state = { + fid: '', + activeIndex: 0, + animating: false, + items: DEFAULT_IMAGE_SLIDER + } + this.setActiveIndex = this.setActiveIndex.bind(this); + this.setAnimating = this.setAnimating.bind(this); + this.next = this.next.bind(this); + this.previous = this.previous.bind(this); + this.goToIndex = this.goToIndex.bind(this); + } + + componentDidMount() { + const { type, id } = this.props; + let category = null; + if (id) { + category = id.split("."); + this.renderImagePopup(category[0], category[1]); + } + } + + componentDidUpdate(prevProps, prevState) { + if (this.props.id !== prevProps.id) { + if (this.props.id) { + let category = this.props.id.split("."); + this.renderImagePopup(category[0], category[1]); + } + } + } + + renderImagePopup = async (type, id) => { + // console.log(type, 'id'+id); + console.log('renderImagePopup type: ',type); + console.log('renderImagePopup id: ', id); + let resGetImagePopup = await getImagePopup(type, id); + console.log("resGetImagePopup", resGetImagePopup) + if (resGetImagePopup.data !== undefined) { + if (resGetImagePopup.data.data.length > 0) { + let imageData = resGetImagePopup.data.data; + let category = ""; + let imageArr = []; + for (let i=0; i { + return this.state.items.map((item) => { + return ( + this.setAnimating(true)} + onExited={() => this.setAnimating(false)} + > + {item.altText} + {/**/} + + ); + }); + } + + render() { + return ( + + + {this.slides()} + + + + ) + } +} + +export default ImageSlider \ No newline at end of file diff --git a/src/components/ImageSlider/package.json b/src/components/ImageSlider/package.json new file mode 100644 index 0000000..70da8f0 --- /dev/null +++ b/src/components/ImageSlider/package.json @@ -0,0 +1,6 @@ +{ + "name": "ImageSlider", + "version": "0.0.0", + "private": true, + "main": "./ImageSlider.js" +} \ No newline at end of file diff --git a/src/components/LayerTreeAdm/LayerTreeAdm.js b/src/components/LayerTreeAdm/LayerTreeAdm.js new file mode 100644 index 0000000..64133bf --- /dev/null +++ b/src/components/LayerTreeAdm/LayerTreeAdm.js @@ -0,0 +1,860 @@ +import React, { Component, Fragment } from 'react'; +import { Form, FormGroup, Label, Input, Row, Col } from 'reactstrap'; +import { Tree, Input as SearchInput, Menu, Dropdown } from 'antd'; +import { API_GEOHR_GROUP_SALES_SEARCH, API_GEOHR_EMPLOYEE_DIVISION_LIST, BASE_SIMPRO, PROYEK_SEARCH_DETAIL, PROYEK_SEARCH } from '../../const/ApiConst.js'; +import { salesTree, customerTree, officeTree, demografiTree, analisaTree, employeeTree, projectTreeConst } from '../../const/LayerTreeConst.js'; +import Loader from 'react-loader-spinner' +import "react-loader-spinner/dist/loader/css/react-spinner-loader.css" +import './LayerTreeGeoHR.css' +import '../../assets/css/customscroll.css' +import { findWhere } from 'underscore'; +// import { opt2G, opt3G, opt4G, netQuality2G, netQuality3G, netQuality4G } from '../../const/Kominfo.js'; +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import ContentLoader from "react-content-loader" +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import axios from 'axios'; + +const { TreeNode } = Tree; +const { Search } = SearchInput; + +let arr_id = []; + +const token = window.localStorage.getItem('token'); +const proyek_id = localStorage.getItem('proyek_id'); +const role_id = localStorage.getItem('role_id'); +const config = { + headers: + { + Authorization: `Bearer ${token}`, + "Content-type": `application/json` + } +}; + +const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + } +} + +const DUMMY_TREE = { + "code": 200, + "message": "OK", + "executionTime": "16.986202ms", + "data": [ + { + "id": 1, + "nama": "Andi", + "biaya": "5000000", + "color_progress": "green", + "jumlah_pekerja": 15, + "pic": "Hanif", + "mulai_proyek": "2021-11-05T11:25:38Z", + "akhir_proyek": "2021-11-11T11:25:38Z", + "created_by": "admin", + "created_at": "2021-11-06T11:20:49.53701Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-0" + }, + { + "id": 2, + "nama": "Proyek Pembangunan Jalan", + "biaya": "10000000", + "color_progress": "green", + "jumlah_pekerja": 15, + "pic": "Ardhi", + "mulai_proyek": "2021-11-05T11:25:38Z", + "akhir_proyek": "2021-11-11T11:25:38Z", + "created_by": "admin", + "created_at": "2021-11-06T11:23:30.257636Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1", + "subproyeks": [ + { + "id": 1, + "proyek_id": 2, + "parent_id": null, + "nama": "Proyek Pembangunan Sekolah Jakarta", + "biaya": "50000000", + "color_progress": "blue", + "jumlah_pekerja": 20, + "pic": "Hanif", + "mulai_proyek": "2021-11-05T11:25:38Z", + "akhir_proyek": "2021-11-11T11:25:38Z", + "created_by": "admin", + "created_at": "2021-11-06T12:04:06.498787Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-0", + "join": { + "m_proyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_proyek_biaya": "10000000", + "m_proyek_color_progress": "green", + "m_proyek_jumlah_pekerja": "15", + "m_proyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_proyek_nama": "Proyek Pembangunan Jalan", + "m_proyek_pic": "Ardhi", + "m_subproyek_akhir_proyek": "", + "m_subproyek_biaya": "", + "m_subproyek_color_progress": "", + "m_subproyek_jumlah_pekerja": "", + "m_subproyek_mulai_proyek": "", + "m_subproyek_nama": "", + "m_subproyek_pic": "" + }, + "plannings": [ + { + "id": 1, + "subproyek_id": 1, + "user_id": 2, + "nama": "Cluster 1", + "jumlah_titik": 10, + "target_planning": "2021-11-05T11:25:38Z", + "address": "jl. smp 126", + "created_by": "admin", + "created_at": "2021-11-06T12:04:45.412016Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-0-0", + "join": { + "m_subproyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_subproyek_biaya": "50000000", + "m_subproyek_color_progress": "blue", + "m_subproyek_jumlah_pekerja": "20", + "m_subproyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_subproyek_nama": "Proyek Pembangunan Sekolah Jakarta", + "m_subproyek_pic": "Hanif", + "m_users_email": "galaxijsersey4@gmail.com", + "m_users_gender": "L", + "m_users_name": "Hanif", + "m_users_phone_number": "083808547423", + "m_users_username": "galaxiarmy" + } + }, + { + "id": 2, + "subproyek_id": 1, + "user_id": 2, + "nama": "Cluster 2", + "jumlah_titik": 10, + "target_planning": "2021-11-05T11:25:38Z", + "address": "jl. radio dalam 128", + "created_by": "admin", + "created_at": "2021-11-06T12:06:44.518877Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-0-1", + "join": { + "m_subproyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_subproyek_biaya": "50000000", + "m_subproyek_color_progress": "blue", + "m_subproyek_jumlah_pekerja": "20", + "m_subproyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_subproyek_nama": "Proyek Pembangunan Sekolah Jakarta", + "m_subproyek_pic": "Hanif", + "m_users_email": "galaxijsersey4@gmail.com", + "m_users_gender": "L", + "m_users_name": "Hanif", + "m_users_phone_number": "083808547423", + "m_users_username": "galaxiarmy" + } + }, + { + "id": 3, + "subproyek_id": 1, + "user_id": 2, + "nama": "Cluster 3", + "jumlah_titik": 20, + "target_planning": "2021-11-05T11:25:38Z", + "address": "jl. mt.haryono no.17", + "created_by": "admin", + "created_at": "2021-11-06T12:07:15.574419Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-0-2", + "join": { + "m_subproyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_subproyek_biaya": "50000000", + "m_subproyek_color_progress": "blue", + "m_subproyek_jumlah_pekerja": "20", + "m_subproyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_subproyek_nama": "Proyek Pembangunan Sekolah Jakarta", + "m_subproyek_pic": "Hanif", + "m_users_email": "galaxijsersey4@gmail.com", + "m_users_gender": "L", + "m_users_name": "Hanif", + "m_users_phone_number": "083808547423", + "m_users_username": "galaxiarmy" + } + }, + { + "id": 4, + "subproyek_id": 1, + "user_id": 2, + "nama": null, + "jumlah_titik": 11, + "target_planning": "2021-11-05T11:25:38Z", + "address": "jl. kebayoran lama", + "created_by": "admin", + "created_at": "2021-11-06T13:14:52.416919Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-0-3", + "join": { + "m_subproyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_subproyek_biaya": "50000000", + "m_subproyek_color_progress": "blue", + "m_subproyek_jumlah_pekerja": "20", + "m_subproyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_subproyek_nama": "Proyek Pembangunan Sekolah Jakarta", + "m_subproyek_pic": "Hanif", + "m_users_email": "galaxijsersey4@gmail.com", + "m_users_gender": "L", + "m_users_name": "Hanif", + "m_users_phone_number": "083808547423", + "m_users_username": "galaxiarmy" + } + } + ] + }, + { + "id": 2, + "proyek_id": 2, + "parent_id": null, + "nama": "test sub proyek", + "biaya": "10000", + "color_progress": "RED", + "jumlah_pekerja": 10, + "pic": "ade", + "mulai_proyek": "2021-11-06T14:35:53Z", + "akhir_proyek": "2021-11-07T14:35:53Z", + "created_by": "admin", + "created_at": "2021-11-06T14:27:27.249764Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-1", + "join": { + "m_proyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_proyek_biaya": "10000000", + "m_proyek_color_progress": "green", + "m_proyek_jumlah_pekerja": "15", + "m_proyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_proyek_nama": "Proyek Pembangunan Jalan", + "m_proyek_pic": "Ardhi", + "m_subproyek_akhir_proyek": "", + "m_subproyek_biaya": "", + "m_subproyek_color_progress": "", + "m_subproyek_jumlah_pekerja": "", + "m_subproyek_mulai_proyek": "", + "m_subproyek_nama": "", + "m_subproyek_pic": "" + } + }, + { + "id": 3, + "proyek_id": 2, + "parent_id": 2, + "nama": "test sub sub proyek", + "biaya": "10000", + "color_progress": "BLUE", + "jumlah_pekerja": 10, + "pic": "dadang", + "mulai_proyek": "2021-11-06T14:57:51Z", + "akhir_proyek": "2021-11-07T14:57:51Z", + "created_by": "admin", + "created_at": "2021-11-06T14:49:25.30324Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-2", + "join": { + "m_proyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_proyek_biaya": "10000000", + "m_proyek_color_progress": "green", + "m_proyek_jumlah_pekerja": "15", + "m_proyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_proyek_nama": "Proyek Pembangunan Jalan", + "m_proyek_pic": "Ardhi", + "m_subproyek_akhir_proyek": "2021-11-07 14:35:53+00", + "m_subproyek_biaya": "10000", + "m_subproyek_color_progress": "RED", + "m_subproyek_jumlah_pekerja": "10", + "m_subproyek_mulai_proyek": "2021-11-06 14:35:53+00", + "m_subproyek_nama": "test sub proyek", + "m_subproyek_pic": "ade" + }, + "subproyeks": [ + { + "id": 2, + "proyek_id": 2, + "parent_id": null, + "nama": "test sub proyek", + "biaya": "10000", + "color_progress": "RED", + "jumlah_pekerja": 10, + "pic": "ade", + "mulai_proyek": "2021-11-06T14:35:53Z", + "akhir_proyek": "2021-11-07T14:35:53Z", + "created_by": "admin", + "created_at": "2021-11-06T14:27:27.249764Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-2-0", + } + ] + }, + { + "id": 4, + "proyek_id": 2, + "parent_id": 3, + "nama": "test with parent id", + "biaya": "10000", + "color_progress": "BLUE", + "jumlah_pekerja": 10, + "pic": "test", + "mulai_proyek": "2021-11-06T14:58:59Z", + "akhir_proyek": "2021-11-07T14:58:59Z", + "created_by": "admin", + "created_at": "2021-11-06T14:50:33.363315Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-3", + "join": { + "m_proyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_proyek_biaya": "10000000", + "m_proyek_color_progress": "green", + "m_proyek_jumlah_pekerja": "15", + "m_proyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_proyek_nama": "Proyek Pembangunan Jalan", + "m_proyek_pic": "Ardhi", + "m_subproyek_akhir_proyek": "2021-11-07 14:57:51+00", + "m_subproyek_biaya": "10000", + "m_subproyek_color_progress": "BLUE", + "m_subproyek_jumlah_pekerja": "10", + "m_subproyek_mulai_proyek": "2021-11-06 14:57:51+00", + "m_subproyek_nama": "test sub sub proyek", + "m_subproyek_pic": "dadang" + }, + "subproyeks": [ + { + "id": 3, + "proyek_id": 2, + "parent_id": 2, + "nama": "test sub sub proyek", + "biaya": "10000", + "color_progress": "BLUE", + "jumlah_pekerja": 10, + "pic": "dadang", + "mulai_proyek": "2021-11-06T14:57:51Z", + "akhir_proyek": "2021-11-07T14:57:51Z", + "created_by": "admin", + "created_at": "2021-11-06T14:49:25.30324Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-3-0", + "subproyeks": [ + { + "id": 2, + "proyek_id": 2, + "parent_id": null, + "nama": "test sub proyek", + "biaya": "10000", + "color_progress": "RED", + "jumlah_pekerja": 10, + "pic": "ade", + "mulai_proyek": "2021-11-06T14:35:53Z", + "akhir_proyek": "2021-11-07T14:35:53Z", + "created_by": "admin", + "created_at": "2021-11-06T14:27:27.249764Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-3-0-0" + } + ] + } + ] + }, + { + "id": 5, + "proyek_id": 2, + "parent_id": 4, + "nama": "test parent", + "biaya": "10000", + "color_progress": "BLUE", + "jumlah_pekerja": 10, + "pic": "test112", + "mulai_proyek": "2021-11-06T15:00:00Z", + "akhir_proyek": "2021-11-07T15:00:00Z", + "created_by": "admin", + "created_at": "2021-11-06T14:51:34.508208Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-4", + "join": { + "m_proyek_akhir_proyek": "2021-11-11 11:25:38+00", + "m_proyek_biaya": "10000000", + "m_proyek_color_progress": "green", + "m_proyek_jumlah_pekerja": "15", + "m_proyek_mulai_proyek": "2021-11-05 11:25:38+00", + "m_proyek_nama": "Proyek Pembangunan Jalan", + "m_proyek_pic": "Ardhi", + "m_subproyek_akhir_proyek": "2021-11-07 14:58:59+00", + "m_subproyek_biaya": "10000", + "m_subproyek_color_progress": "BLUE", + "m_subproyek_jumlah_pekerja": "10", + "m_subproyek_mulai_proyek": "2021-11-06 14:58:59+00", + "m_subproyek_nama": "test with parent id", + "m_subproyek_pic": "test" + }, + "subproyeks": [ + { + "id": 4, + "proyek_id": 2, + "parent_id": 3, + "nama": "test with parent id", + "biaya": "10000", + "color_progress": "BLUE", + "jumlah_pekerja": 10, + "pic": "test", + "mulai_proyek": "2021-11-06T14:58:59Z", + "akhir_proyek": "2021-11-07T14:58:59Z", + "created_by": "admin", + "created_at": "2021-11-06T14:50:33.363315Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-4-0", + "subproyeks": [ + { + "id": 3, + "proyek_id": 2, + "parent_id": 2, + "nama": "test sub sub proyek", + "biaya": "10000", + "color_progress": "BLUE", + "jumlah_pekerja": 10, + "pic": "dadang", + "mulai_proyek": "2021-11-06T14:57:51Z", + "akhir_proyek": "2021-11-07T14:57:51Z", + "created_by": "admin", + "created_at": "2021-11-06T14:49:25.30324Z", + "updated_by": null, + "updated_at": null, + "subproyeks": [ + { + "id": 2, + "proyek_id": 2, + "parent_id": null, + "nama": "test sub proyek", + "biaya": "10000", + "color_progress": "RED", + "jumlah_pekerja": 10, + "pic": "ade", + "mulai_proyek": "2021-11-06T14:35:53Z", + "akhir_proyek": "2021-11-07T14:35:53Z", + "created_by": "admin", + "created_at": "2021-11-06T14:27:27.249764Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1-4-0-0", + } + ] + } + ] + } + ] + } + ] + }, + { + "id": 3, + "nama": "Pantura", + "biaya": "1000", + "color_progress": "orange", + "jumlah_pekerja": 12000, + "pic": "Juan", + "mulai_proyek": "2021-08-02T13:10:09.082Z", + "akhir_proyek": "2021-11-06T13:10:09.082Z", + "created_by": "admin", + "created_at": "2021-11-06T13:02:01.915678Z", + "updated_by": "admin", + "updated_at": "2021-11-06T15:24:24.080109Z", + "tree_key": "project-0-2", + } + ], + "totalRecord": 3 +} + +const DUMMY_TREE2 = { + "data": [ + { + "id": 1, + "nama": "Proyek 1", + "biaya": "5000000", + "color_progress": "green", + "jumlah_pekerja": 15, + "pic": "Ardhi", + "mulai_proyek": "2021-11-05T11:25:38Z", + "akhir_proyek": "2021-11-11T11:25:38Z", + "created_by": "admin", + "created_at": "2021-11-06T11:20:49.53701Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-0" + }, + { + "id": 2, + "nama": "Proyek 2", + "biaya": "5000000", + "color_progress": "green", + "jumlah_pekerja": 15, + "pic": "Juan", + "mulai_proyek": "2021-11-05T11:25:38Z", + "akhir_proyek": "2021-11-11T11:25:38Z", + "created_by": "admin", + "created_at": "2021-11-06T11:20:49.53701Z", + "updated_by": null, + "updated_at": null, + "tree_key": "project-0-1" + } + ] +} + + +class LayerTreeAdm extends Component { + + constructor(props) { + super(props); + this.state = { + treeData: [], + checkedKeys: [], + isReady: false, + rightClickContentVisible: false, + allCheckNodes: [], + salesGroupTree: null, + rightClickNodeTreeItem: { + pageX: "", + pageY: "", + id: "", + dropdownTitle: "" + }, + expandedKeys: [], + autoExpandParent: true + } + + this.wrapperRightClickContentRef = React.createRef(); + this.handleClickOutside = this.handleClickOutside.bind(this); + } + + componentDidMount() { + // this.getProv(); + this.setState({isReady: true}) + this.getLayerTreeData(); + document.addEventListener('mousedown', this.handleClickOutside); + } + + componentWillUnmount() { + document.removeEventListener('mousedown', this.handleClickOutside); + } + + // getLayerTreeData = async () => { + // let payload = { + // "columns": [], + // "paging": {"start": 0,"length": 25}, + // "joins": [ + // { "name": "subproyeks.m_proyek", "column_join": "proyek_id", "column_results": ["nama", "mulai_proyek", "akhir_proyek"] }, + // { "name": "subproyeks.m_subproyek", "column_join": "parent_id", "column_results": ["nama","mulai_proyek", "akhir_proyek"] }, + // // { "name": "subproyeks.plannings.m_subproyek", "column_join": "subproyek_id", "column_results": ["nama", "biaya", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek"] }, + // // { "name": "subproyeks.plannings.m_users", "column_join": "user_id", "column_results": ["name", "username", "email", "phone_number", "gender"] } + // ], + // "orders": { "columns": ["id"], "ascending": false } + // } + + // if(parseInt(localStorage.getItem('role_id'))!==1){ + // payload.columns.push( + // { + // "name": "id", + // "logic_operator": "=", + // "value": localStorage.getItem('proyek_id') ? localStorage.getItem('proyek_id') : "0", + // "operator": "AND" + // } + // ) + // } + + // const config = { + // method: 'POST', // *GET, POST, PUT, DELETE, etc. + // // mode: 'cors', // no-cors, *cors, same-origin + // // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // // credentials: 'same-origin', // include, *same-origin, omit + // headers: { + // 'Content-Type': 'application/json', + // 'Authorization': 'Bearer '+window.localStorage.getItem('token') + // // 'Content-Type': 'application/x-www-form-urlencoded', + // }, + // // redirect: 'follow', // manual, *follow, error + // // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + // body: JSON.stringify(payload) // body data type must match "Content-Type" header + // } + + // try { + // const result = await fetch(PROYEK_SEARCH_DETAIL, config).then(response => response.json()).then(res => res); + + // if (result && result.code == 200) { + // let children = this.getChildrenTree(result.data); + // // let children = this.assignDepth(result.data, undefined, 4); + // console.log('children', children); + + // projectTreeConst[0].children = children; + + // console.log('projectTreeConst', projectTreeConst); + + // this.props.setProjectTree(projectTreeConst); // passing the function to Map.js + // } else { + // NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + // } + // } + // catch (e) { + // console.log('[LayerTreeGeohr] getLayerTreeData e', e) + // NotificationManager.error('Gagal Mengambil Data! Periksa koneksi internet Anda', 'Failed'); + // } + // } + + getLayerTreeData = async () => { + const payload = { + // "columns": [ + // { "name": "nama", "logic_operator": "like", "value": search, "operator": "AND" } + // ], + "joins": [ + { "name": "m_users", "column_join": "pm_id", "column_results": ["name", "username"] }, + { "name": "m_type_proyek", "column_join": "type_proyek_id", "column_results": ["name", "description"] }, + // { "name": "subproyeks.m_subproyek", "column_join": "parent_id", "column_results": ["nama", "biaya", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual"] } + ], + "orders": { "columns": ["id"], "ascending": true }, + "paging": { "start": 0, "length": 25 } + } + + if (parseInt(role_id) !== 1) { + payload["columns"] = [ + { "name": "id", "logic_operator": "=", "value": localStorage.getItem('proyek_id'), "operator": "AND" } + ] + } + + const result = await axios + .post(PROYEK_SEARCH, payload, HEADER) + .then(res => res) + .catch((error) => error.response); + + // console.log("data sub proyek", result) + + if (result && result.data && result.data.code == 200) { + // console.log("cek proyek", result) + let dataRes = result.data.data || [] + + let children = this.getChildrenTree(dataRes); + projectTreeConst[0].children = children; + + this.props.setProjectTree(projectTreeConst); // passing the function to Map.js + + // set default check all + if (dataRes && dataRes.length > 0) { + this.props.setCheckedKeysProjectTree(['project-0']); + } + } else { + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + getChildrenTree = (data) => + data.map((item, index) => { + if (item.subproyeks && item.subproyeks.length > 0) { + return { + "children": this.getChildrenTree(item.subproyeks), + "title": item.nama, + "key": Math.random(), + "id": item.id, + ...item + } + } + else if (item.plannings && item.plannings.length > 0) { + return { + "children": this.getChildrenTree(item.plannings), + "title": item.nama, + "key": Math.random(), + ...item + } + } + else if (item.laporan_plannings && item.laporan_plannings.length > 0) { + let lp = item.laporan_plannings.map(lp_item => { + return { + // "nama_subproyek": item.join.m_subproyek_nama, + "nama_pekerjaan": item.nama, + // "nama_waspang": item.join.m_users_name, + ...lp_item, + "volume_target": lp_item.join.m_planning_jumlah_titik, + "volume_aktual": lp_item.jumlah_pekerjaan, + "satuan_kerja": lp_item.join.m_satuan_name, + "pm_subproyek": lp_item.join.m_subproyek_pic, + "email waspang": lp_item.join.m_users_email, + "jenis_kelamin_waspang": lp_item.join.m_users_gender, + "no_hp_waspang": lp_item.join.m_users_phone_number + } + }); + + return { + "key": Math.random(), + "title": item.nama, + ...item, + "laporan_plannings": lp // modify the laporan_plannings + } + } + return { + "title": item.nama, + "key": Math.random(), + ...item + } + }) + + onCheckOpt = (state, checkedKeys, e) => { + this.props.onCheckOpt(state, checkedKeys); + } + + renderTreeNodes = data => + data.map(item => { + if (item.children) { + return ( + + {this.renderTreeNodes(item.children)} + + ); + } + return ( + + ); + }) + + + onRightClick = (e, node) => { + console.log('[LayerTreeGeoHR] onRightClick e', e); + e.event.persist(); + this.setState({ + rightClickContentVisible: true, + rightClickNodeTreeItem: { + pageX: e.event.pageX, + pageY: e.event.pageY , + id: e.node.key, + dropdownTitle: e.node.title + } + }); + } + + rightClickContent = () => { + const { pageX, pageY, id, dropdownTitle } = { ...this.state.rightClickNodeTreeItem }; + + const tmpStyle = { + position:"absolute", + left: `${pageX}px`, + top: `${pageY - 50}px`, + zIndex:99999, + width:"200px", + background:"#ededed", + borderRadius:"5px" + } + const menu = ( +
+
+ x +
{ dropdownTitle }
+
+ {/*
Go to map
*/} +
this.openTable}>Show Data
+
+
+
+ ); + return this.state.rightClickNodeTreeItem.dropdownTitle == "" ? "" : menu; + } + + closeRightClickContent = () => { + this.setState({ + rightClickContentVisible: false, + rightClickNodeTreeItem: { + pageX: "", + pageY: "", + id: "", + dropdownTitle: "" + } + }) + } + + handleClickOutside = (event) => { + // console.log('event.target', event.target); + // console.log('handleClickOutside',this.wrapperRightClickContentRef.current); + if (this.wrapperRightClickContentRef.current) { + // console.log('contains?', this.wrapperRightClickContentRef.current.contains(event.target)); + if (!this.wrapperRightClickContentRef.current.contains(event.target)) { + // alert('You clicked outside of me!'); + // close the rightClickContent + this.closeRightClickContent(); + } + } + } + + render() { + const { isReady } = this.state; + const { checkedKeysSales, checkedKeysCustomer, checkedKeysOffice, checkedKeysDemografi, checkedKeysAnalisa, + salesGroupTree, checkedKeysEmployeeDivision, employeeDivisionTree, projectTree, checkedKeysProjectTree } = this.props; + if (!isReady) { + return (
+ +
) + } + + return ( + + + + + { projectTree ? + this.onCheckOpt('checkedKeysProjectTree', checkedKeysProjectTree, e)} + defaultExpandedKeys={['project-0']} + onRightClick={(e, node) => this.onRightClick(e, node)} + treeData={projectTree} + /> + : + + + + + + + + + + + } + + + + ) + } +} + +export default LayerTreeAdm; \ No newline at end of file diff --git a/src/components/LayerTreeAdm/LayerTreeGeoHR.css b/src/components/LayerTreeAdm/LayerTreeGeoHR.css new file mode 100644 index 0000000..520a27a --- /dev/null +++ b/src/components/LayerTreeAdm/LayerTreeGeoHR.css @@ -0,0 +1,67 @@ +.loader-container { + margin-top: 100px; + text-align: center; +} + +.checkbox-opt li { + font-size: 12px; + padding: 0px !important; +} + +.checkbox-sales-container { + max-height: 300px; + overflow-y: auto; +} + +.adm-dropdown-container { + z-index: 999999; + position: fixed; + max-height: 350px; + border-radius: 5px; + background-color: rgba(0,0,0,0.5); + /*background-color: #ebebeb;*/ + color: #ffffff; + /*color: #000000;*/ +} + +.adm-dropdown-title { + font-size: 14px; + margin-top: 0px; + margin-left: 10px; + margin-bottom: 5px; + font-weight: bold; + width: 200px; +} + +.adm-dropdown-close { + float: right; + font-size: 18px; + font-weight: bold; + margin-left: 0px; + margin-right: 10px; + margin-top: -5px; + cursor: pointer; +} + +.adm-dropdown-body { + max-height: 200px; + overflow: auto; + margin: 5px; + text-align: left; +} + +.adm-dropdown-list { + cursor: pointer; + font-size: 14px; + padding: 5px; + margin-left: 0px; + text-align: left; +} + +.adm-dropdown-icon { + margin-right: 5px; +} + +.adm-dropdown-list:hover { + background-color: rgba(0,0,0,0.25); +} \ No newline at end of file diff --git a/src/components/LayerTreeAdm/LayerTreeGeoHR_salesver.js b/src/components/LayerTreeAdm/LayerTreeGeoHR_salesver.js new file mode 100644 index 0000000..9f6fe02 --- /dev/null +++ b/src/components/LayerTreeAdm/LayerTreeGeoHR_salesver.js @@ -0,0 +1,342 @@ +import React, { Component, Fragment } from 'react'; +import { Form, FormGroup, Label, Input, Row, Col } from 'reactstrap'; +import { Tree, Input as SearchInput, Menu, Dropdown } from 'antd'; +import { API_GEOHR_GROUP_SALES_SEARCH } from '../../const/ApiConst.js'; +import { salesTree, customerTree, officeTree, demografiTree, analisaTree} from '../../const/LayerTreeConst.js'; +import Loader from 'react-loader-spinner' +import "react-loader-spinner/dist/loader/css/react-spinner-loader.css" +import './LayerTreeGeoHR.css' +import '../../assets/css/customscroll.css' +import { findWhere } from 'underscore'; +// import { opt2G, opt3G, opt4G, netQuality2G, netQuality3G, netQuality4G } from '../../const/Kominfo.js'; +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import ContentLoader from "react-content-loader" + +const { TreeNode } = Tree; +const { Search } = SearchInput; + + +class LayerTreeGeoHR extends Component { + + constructor(props) { + super(props); + this.state = { + treeData: [], + checkedKeys: [], + isReady: false, + rightClickContentVisible: false, + allCheckNodes: [], + salesGroupTree: null, + rightClickNodeTreeItem: { + pageX: "", + pageY: "", + id: "", + dropdownTitle: "" + } + } + + this.wrapperRightClickContentRef = React.createRef(); + this.handleClickOutside = this.handleClickOutside.bind(this); + } + + componentDidMount() { + // this.getProv(); + this.setState({isReady: true}) + this.getSalesGroup(); + document.addEventListener('mousedown', this.handleClickOutside); + } + + componentWillUnmount() { + document.removeEventListener('mousedown', this.handleClickOutside); + } + + getSalesGroup = async () => { + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify({ + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"name", "logic_operator": "like", "value": "", "operator": "and"} + ], + "orders": {"columns": ["name"], "ascending": true} + }) + } + + try { + const result = await fetch(API_GEOHR_GROUP_SALES_SEARCH, param).then(response => response.json()).then(res => res); + console.log('getSalesGroup result', result); + if (result.data) { + let salesGroupTree_ = []; + let children = []; + salesGroupTree_.push({ + "title": "Sales", + "key": "sales-0" + }); + + if (result.data.length > 0) { + for (let i=0; i < result.data.length; i++) { + children.push({ + "title": result.data[i].name, + "key": "sales-0-"+i, + "sales_group_id": result.data[i].id + }); + } + + salesGroupTree_[0].children = children; + } + + this.props.setSalesGroupTree(salesGroupTree_); // passing the function to Map.js + } + } + catch (e) { + console.log('error getSalesGroup', e); + } + } + + onCheckOpt = (state, checkedKeys, e) => { + this.props.onCheckOpt(state, checkedKeys); + } + + renderTreeNodes = data => + data.map(item => { + if (item.children) { + return ( + + {this.renderTreeNodes(item.children)} + + ); + } + return ( + + ); + }) + + + onRightClick = (e, node) => { + console.log('[LayerTreeGeoHR] onRightClick e',e); + e.event.persist(); + this.setState({ + rightClickContentVisible: true, + rightClickNodeTreeItem: { + pageX: e.event.pageX, + pageY: e.event.pageY , + id: e.node.props.dataRef.key, + dropdownTitle: e.node.props.dataRef.title + } + }); + } + + rightClickContent = () => { + const { pageX, pageY, id, dropdownTitle } = { ...this.state.rightClickNodeTreeItem }; + + const tmpStyle = { + position:"absolute", + left: `${pageX}px`, + top: `${pageY - 50}px`, + zIndex:99999, + width:"200px", + background:"#ededed", + borderRadius:"5px" + } + const menu = ( +
+
+ x +
{ dropdownTitle }
+
+ {/*
Go to map
*/} +
this.openTable}>Show Data
+
+
+
+ ); + return this.state.rightClickNodeTreeItem.dropdownTitle == "" ? "" : menu; + } + + closeRightClickContent = () => { + this.setState({ + rightClickContentVisible: false, + rightClickNodeTreeItem: { + pageX: "", + pageY: "", + id: "", + dropdownTitle: "" + } + }) + } + + handleClickOutside = (event) => { + // console.log('event.target', event.target); + // console.log('handleClickOutside',this.wrapperRightClickContentRef.current); + if (this.wrapperRightClickContentRef.current) { + // console.log('contains?', this.wrapperRightClickContentRef.current.contains(event.target)); + if (!this.wrapperRightClickContentRef.current.contains(event.target)) { + // alert('You clicked outside of me!'); + // close the rightClickContent + this.closeRightClickContent(); + } + } + } + + + + render() { + const { isReady } = this.state; + const { checkedKeysSales, checkedKeysCustomer, checkedKeysOffice, checkedKeysDemografi, checkedKeysAnalisa, + salesGroupTree } = this.props; + if (!isReady) { + return (
+ +
) + } + + return ( + + + + this.onCheckOpt('checkedKeysCustomer', checkedKeysCustomer, e)} + onRightClick={(e, node) => this.onRightClick(e, node)} + > + {this.renderTreeNodes(customerTree)} + + + + + this.onCheckOpt('checkedKeysOffice', checkedKeysOffice, e)} + onRightClick={(e, node) => this.onRightClick(e, node)} + > + {this.renderTreeNodes(officeTree)} + + + + + {/* + + this.onCheckOpt('checkedKeysSales', checkedKeysSales, e)} + defaultExpandedKeys={['sales-0']} + > + {this.renderTreeNodes(salesTree)} + + + + + + + this.onCheckOpt('checkedKeysCustomer', checkedKeysCustomer, e)} + > + {this.renderTreeNodes(customerTree)} + + + + + + this.onCheckOpt('checkedKeysOffice', checkedKeysOffice, e)} + > + {this.renderTreeNodes(officeTree)} + + + + + */} + + + + { salesGroupTree ? + this.onCheckOpt('checkedKeysSales', checkedKeysSales, e)} + defaultExpandedKeys={['sales-0']} + onRightClick={(e, node) => this.onRightClick(e, node)} + > + { this.renderTreeNodes(salesGroupTree)} + + : + + + + + + + + + + + } + + + +
+ + + + this.onCheckOpt('checkedKeysAnalisa', checkedKeysAnalisa, e)} + defaultExpandedKeys={['analisa-0']} + > + {this.renderTreeNodes(analisaTree)} + + + + + + + this.onCheckOpt('checkedKeysDemografi', checkedKeysDemografi, e)} + defaultExpandedKeys={['demografi-0']} + > + {this.renderTreeNodes(demografiTree)} + + + + + { this.state.rightClickContentVisible && this.rightClickContent()} + +
+ ) + } +} + +export default LayerTreeGeoHR; \ No newline at end of file diff --git a/src/components/LayerTreeAdm/package.json b/src/components/LayerTreeAdm/package.json new file mode 100644 index 0000000..9d7309b --- /dev/null +++ b/src/components/LayerTreeAdm/package.json @@ -0,0 +1,6 @@ +{ + "name": "LayerTreeAdm", + "version": "0.0.0", + "private": true, + "main": "./LayerTreeAdm.js" +} \ No newline at end of file diff --git a/src/components/MapHeader/MapHeader.js b/src/components/MapHeader/MapHeader.js new file mode 100644 index 0000000..4f85bd9 --- /dev/null +++ b/src/components/MapHeader/MapHeader.js @@ -0,0 +1,150 @@ +import React, { Component } from 'react'; +import { Link, NavLink } from 'react-router-dom'; +import { Badge, UncontrolledDropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, + InputGroup, Input, InputGroupAddon, InputGroupText, Button, UncontrolledTooltip +} from 'reactstrap'; +import PropTypes from 'prop-types'; + +import { AppAsideToggler, AppNavbarBrand, AppSidebarToggler } from '@coreui/react'; +import logo from '../../assets/img/brand/logo_siopas.png' +import logo_bmd_denpasar from '../../assets/img/brand/logo_bmd_denpasar.png' +import user_logo from '../../assets/img/avatars/user.png' +import sygnet from '../../assets/img/brand/sygnet.svg' +import { Icon, InlineIcon } from '@iconify/react'; +import saveOutline from '@iconify/icons-ion/save-outline'; +import printOutline from '@iconify/icons-ion/print-outline'; +import { + AppHeader +} from '@coreui/react'; +import { + mappify, + NominatimSearch +} from '@terrestris/react-geo'; +import { nominatimBaseUrl } from '../../const/MapConst'; + +const propTypes = { + children: PropTypes.node, +}; + +const defaultProps = {}; + +// const MappifiedNominatimSearch = mappify(NominatimSearch); + +class MapHeader extends Component { + // loading = () =>
Loading...
+ + getHeaderMenu = () => { + let u_group = localStorage.getItem('u_group'); + if (u_group == 'kominfo') { + return ( + + Dashboard + + ) + } + else { + return ( + + Dashboard + + ) + } + } + + getLogo = () => { + return ( +
+ OSPRO +
+ ) + } + + render() { + // eslint-disable-next-line + const { children, ...attributes } = this.props; + + return ( + + {/**/} + + { this.getLogo() } + + + + + ) + + + } +} + +MapHeader.propTypes = propTypes; +MapHeader.defaultProps = defaultProps; + +export default MapHeader; \ No newline at end of file diff --git a/src/components/MapHeader/package.json b/src/components/MapHeader/package.json new file mode 100644 index 0000000..6a145ef --- /dev/null +++ b/src/components/MapHeader/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapHeader", + "version": "0.0.0", + "private": true, + "main": "./MapHeader.js" +} diff --git a/src/components/MapLayerStyles/MapLayerStyles.css b/src/components/MapLayerStyles/MapLayerStyles.css new file mode 100644 index 0000000..6cf3da5 --- /dev/null +++ b/src/components/MapLayerStyles/MapLayerStyles.css @@ -0,0 +1,3 @@ +.apply-style-button { + margin-top: 20px; +} \ No newline at end of file diff --git a/src/components/MapLayerStyles/MapLayerStyles.js b/src/components/MapLayerStyles/MapLayerStyles.js new file mode 100644 index 0000000..7367b05 --- /dev/null +++ b/src/components/MapLayerStyles/MapLayerStyles.js @@ -0,0 +1,55 @@ +import React, { Component } from 'react'; +import { SketchPicker } from 'react-color'; +import { + Button +} from 'reactstrap'; +import './MapLayerStyles.css'; + +class MapLayerStyles extends Component { + + constructor(props) { + super(props) + this.state = { + color: '#000' + } + this.applyStyles = this.applyStyles.bind(this); + } + + handleChangeComplete = (color, event) => { + // color = { + // hex: '#333', + // rgb: { + // r: 51, + // g: 51, + // b: 51, + // a: 1, + // }, + // hsl: { + // h: 0, + // s: 0, + // l: .20, + // a: 1, + // }, + // } + console.log('handleChangeComplete',color, event); + this.setState({color: color.hex}); + } + + applyStyles() { + console.log('apply styles to color...', this.state.color); + } + + render() { + return ( +
+ + +
+ ); + } +} + +export default MapLayerStyles; \ No newline at end of file diff --git a/src/components/MapLayerStyles/package.json b/src/components/MapLayerStyles/package.json new file mode 100644 index 0000000..9d26c62 --- /dev/null +++ b/src/components/MapLayerStyles/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapLayerStyles", + "version": "0.0.0", + "private": true, + "main": "./MapLayerStyles.js" +} diff --git a/src/components/MapLayerSwitcher/MapLayerSwitcher.css b/src/components/MapLayerSwitcher/MapLayerSwitcher.css new file mode 100644 index 0000000..d575fd9 --- /dev/null +++ b/src/components/MapLayerSwitcher/MapLayerSwitcher.css @@ -0,0 +1,48 @@ +.layer-switcher-container { + position: absolute; + bottom: 0; + left: 50px; + z-index: 500; + width: 310px; + overflow-x: auto; + white-space: nowrap; +} + +.layer-switcher-item { + display: inline-block; + padding: 10px; + border: dashed; + border-color: #ededed; + border-width: 3px; + border-radius: 5px; + margin: 2px; + cursor: pointer; + background-color: #ffffff; + margin-bottom: 10px; + text-align: center; + width: 100%; +} + +.layer-switcher-item:hover { + background-color: #ededed; +} + +.layer-switcher-image-container { + margin-bottom: 5px; +} + +.layer-switcher-name { + font-weight: 700; +} + +.active-base-map { + /*outline-color: blue;*/ + border-color: #1890ff !important; + border-width: 3px !important; + border: solid; + /*background-color: blue;*/ +} + +.hide { + display: none; +} \ No newline at end of file diff --git a/src/components/MapLayerSwitcher/MapLayerSwitcher.js b/src/components/MapLayerSwitcher/MapLayerSwitcher.js new file mode 100644 index 0000000..e3f6833 --- /dev/null +++ b/src/components/MapLayerSwitcher/MapLayerSwitcher.js @@ -0,0 +1,109 @@ +import * as React from 'react'; + +import OlMap from 'ol/Map'; +import OlView from 'ol/View'; +import OlLayerTile from 'ol/layer/Tile'; +import OlSourceStamen from 'ol/source/Stamen'; +import OlSourceOsm from 'ol/source/OSM'; +import LayerSwitcher from '@terrestris/react-geo/dist/LayerSwitcher/LayerSwitcher'; +import osmImage from '../../assets/img/base_map/osm.PNG'; +import googleImage from '../../assets/img/base_map/google.PNG'; +import { Row, Col } from 'reactstrap'; + +import './MapLayerSwitcher.css'; +import '../../assets/css/customscroll.css'; + +class MapLayerSwitcher extends React.Component { + + constructor(props) { + + super(props); + this.state = { + activeBaseMap: '' + } + + this.layers = [ + new OlLayerTile({ + name: 'OSM', + source: new OlSourceOsm() + }), + new OlLayerTile({ + name: 'Stamen', + source: new OlSourceStamen({ + layer: 'watercolor' + }) + }), + ]; + } + + componentDidMount() { + this.getActiveBaseMap(); + } + + componentDidUpdate(prevProps, prevState) { + // if (this.state.activeBaseMap !== prevState.activeBaseMap) { + // this.getActiveBaseMap(); + // } + } + + getActiveBaseMap = () => { + let activeBaseMap = ''; + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('type') == 'base') { + activeBaseMap = layer.get('name'); + // console.log('getActiveBaseMap activeBaseMap', activeBaseMap); + } + }); + + this.setState({activeBaseMap: activeBaseMap}, () => this.renderBaseMapList()); + } + + changeBaseLayer = (layer) => { + this.setState({activeBaseMap: layer.get('name')}, () =>{ + this.renderBaseMapList(); + this.props.changeBaseLayer(layer); + }); + } + + + renderBaseMapList = () => { + // console.log('this.state.activeBaseMap', this.state.activeBaseMap); + return this.props.baseLayers.map((item, index) => { + return ( + +
this.changeBaseLayer(item)}> +
{item.get('imageName')}
+ {item.get('name')} +
+ + ) + }) + } + + + + render() { + // return( + //
+ // {this.props.baseLayers.map((item, index) => { + // return ( + //
this.props.changeBaseLayer(item)}> + // {item.get('imageName')}
+ // {item.get('name')} + //
+ // ) + // })} + //
+ // ) + + return( +
+ + { this.renderBaseMapList() } + +
+ ) + } +} + +export default MapLayerSwitcher; \ No newline at end of file diff --git a/src/components/MapLayerSwitcher/package.json b/src/components/MapLayerSwitcher/package.json new file mode 100644 index 0000000..3cad65c --- /dev/null +++ b/src/components/MapLayerSwitcher/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapLayerSwitcher", + "version": "0.0.0", + "private": true, + "main": "./MapLayerSwitcher.js" +} diff --git a/src/components/MapLegend/MapLegend.css b/src/components/MapLegend/MapLegend.css new file mode 100644 index 0000000..d9caaf4 --- /dev/null +++ b/src/components/MapLegend/MapLegend.css @@ -0,0 +1,51 @@ +.maplegend-container { + z-index: 50; + top: 60px; + right: 50px; + /*background-color: red;*/ + /*padding: 100px;*/ + position: absolute; + max-height: 350px; + border-radius: 5px; + background-color: rgba(0,0,0,0.5); + /*float: right;*/ + color: #ffffff; + min-width: 200px; +} + +.maplegend-title { + font-size: 14px; + margin-top: 5px; + margin-left: 20px; + margin-bottom: 10px; + font-weight: bold; +} + +.maplegend-close { + float: right; + margin-right: 15px; + margin-top: -5px; + cursor: pointer; +} + +.maplegend-body { + max-height: 200px; + overflow: auto; + margin: 5px; + text-align: left; +} + +.maplegend-text-container { + display: block; +} + +.maplegend-graphic { + display: inline-block; +} + +.maplegend-text { + display: inline-block; + margin-left: 10px; + margin-right: 10px; + font-size: 12px; +} \ No newline at end of file diff --git a/src/components/MapLegend/MapLegend.js b/src/components/MapLegend/MapLegend.js new file mode 100644 index 0000000..46ec150 --- /dev/null +++ b/src/components/MapLegend/MapLegend.js @@ -0,0 +1,76 @@ +import React, { Component } from 'react'; +import './MapLegend.css'; +import '../../assets/css/customscroll.css'; +import { Legend } from '@terrestris/react-geo'; +// import Legend from '@terrestris/react-geo/dist/Legend/Legend'; + +class MapLegend extends Component { + + constructor(props) { + super(props); + this.state = { + layers: [] + } + } + + componentDidMount() { + this.renderMapLegend(); + } + + componentDidUpdate(prevState, prevProps) { + + } + + getMapLegend = () => { + const { olmap } = this.props; + let layersLegend = []; + console.log('getMapLegend', olmap.getLayers()); + olmap.getLayers().forEach((layer, i) => { + if (layer.get('type') !== 'base') { + if (layer.get('type') !== 'vector') { + if (layer.get('type') == 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + layersLegend.push(sublayer); + } + }); + } + else { + if (layer.getVisible()) { + layersLegend.push(layer); + } + } + } + + } + }) + let reversedLayersLegend = layersLegend.reverse(); // to get the right position as layer position + return reversedLayersLegend; + } + + renderMapLegend = () => { + let layers = this.getMapLegend(); + if (layers.length > 0) { + return layers.map((item, index) => { + return
+ {item.get('name')} +
+ }) + } + } + + render() { + const { layers } = this.state; + const { toggleLegend } = this.props; + return ( +
+
Map Legend x
+
+ { this.renderMapLegend() } +
+
+ ) + } +} + +export default MapLegend; \ No newline at end of file diff --git a/src/components/MapLegend/package.json b/src/components/MapLegend/package.json new file mode 100644 index 0000000..f73e782 --- /dev/null +++ b/src/components/MapLegend/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapLegend", + "version": "0.0.0", + "private": true, + "main": "./MapLegend.js" +} \ No newline at end of file diff --git a/src/components/MapTable/MapTable.css b/src/components/MapTable/MapTable.css new file mode 100644 index 0000000..7a1f3b8 --- /dev/null +++ b/src/components/MapTable/MapTable.css @@ -0,0 +1,73 @@ +.modal-wrapper { + margin-top: 10px; +} + +.modal-dialog-data { + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} + +.modal-content-data { + width: 100vw; + min-height: 100%; + border-radius: 0; +} + +.modal-body-wrap { + /*height: auto !important;*/ + /*padding: 10px 0 10px;*/ + /*margin-bottom: -100px;*/ + /*min-height: 100%;*/ +} + +.table-wrapper { + height: 75vh; + /*width: 100%;*/ + /*bottom: 100px;*/ + overflow: auto; +} + +.react-bootstrap-table { + font-size: 12px; +} + +.search-bar-wrapper { + +} + +.map-table-button-group { + /*float: right;*/ +} + +.map-table-button { + margin-right: 5px; +} + +.text-white { + color: #FFFFFF; +} + +.table-option-button-group { + margin-right: 15px; +} + +.switch-button-upper { + margin-top: -5px; +} + +.button-option-table-wrapper { + margin-left: 15px; +} + +.button-option-table { + background-color: #ffffff; + padding: 5px 0px; + margin-top: 3px; +} + +.option-button-wrapper { + margin-right: 10px; + margin-top: 5px; +} \ No newline at end of file diff --git a/src/components/MapTable/MapTable.js b/src/components/MapTable/MapTable.js new file mode 100644 index 0000000..e410826 --- /dev/null +++ b/src/components/MapTable/MapTable.js @@ -0,0 +1,1201 @@ +import React, { Component, Fragment } from 'react'; +import Draggable, { DraggableCore } from "react-draggable"; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +// import { Rnd } from 'react-rnd'; +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import cellEditFactory from 'react-bootstrap-table2-editor'; +import { Icon, InlineIcon } from '@iconify/react'; +import createOutline from '@iconify/icons-ion/create-outline'; +import earthIcon from '@iconify/icons-ion/earth'; +import archiveOutline from '@iconify/icons-ion/archive-outline'; +import saveOutline from '@iconify/icons-ion/save-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import { Col, Row } from 'reactstrap'; +import { AppSwitch } from '@coreui/react'; +import './MapTable.css'; +import '../../assets/css/customscroll.css' +import EditTableColumn from '../EditTableColumn'; +import { reqTableData, getTableColumns, updateFeature, updateFeature2, deleteFeature, getLayerAttribute } from '../../const/GeoserverFunc.js'; +import { API_GET_COLUMN_TABLE } from '../../const/ApiConst.js'; +import { findWhere, without } from 'underscore'; + +// For Select feature then go to map +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Vector as VectorSource } from 'ol/source'; +import { fromLonLat, transformExtent } from 'ol/proj'; +import SweetAlert from 'react-bootstrap-sweetalert'; + +const { SearchBar } = Search; +const { ExportCSVButton } = CSVExport; +/*const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const products = [ +{ + "id": 0, + "name": "Item name 0", + "price": 2100 +}, +{ + "id": 1, + "name": "Item name 1", + "price": 2101 +}, +{ + "id": 2, + "name": "Item name 2", + "price": 2102 +}, +{ + "id": 3, + "name": "Item name 3", + "price": 2103 +}];*/ + +/*const TableContent = () => { + let tableData = this.props.resTableData; + if (tableData !== null) { + return ( + + { + props => ( +
+

Input something at below input field:

+ +
+ +
+ ) + } +
+ ) + } +}*/ + +// const selectRow = { +// mode: 'radio', +// clickToSelect: true, +// clickToEdit: true // Click to edit cell also +// }; + +class TableContent extends Component { + + constructor(props) { + super(props) + this.state = { + fid: 'fid', + columns: null, + layer_attribute: [], + originalData: null, + data: null, + resTableData: null, + selectRow: { + mode: 'radio', + clickToSelect: true, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + clickToEdit: false, + bgColor: '#e4e5e6', + }, + editCellMode: false, + editTableColumnVisible: false, + selectedRowData: null, + editedColumnsData: null, + // For Select Feature + mapProjection: this.props.olmap.getView().getProjection(), + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + }, + selectedRows: [], + updatedData: [], + alertConfirm: false, + alertConfirmBtnText: '', // Delete, Update + alertConfirmType: '', // danger, success, warning, info + alertConfirmMsg: '', // Under are you sure message: ... ? + confirmParam: '', + alertResponse: false, + alertResponseMsg: '', + successAlert: false, + dangerAlert: false, + saveMode: false + } + this.toggleEditCell = this.toggleEditCell.bind(this); + // this.mapTableInit = this.mapTableInit.bind(this); + this.activateCellEdit = this.activateCellEdit.bind(this); + } + + componentDidMount() { + console.log('this.state', this.state); + this.mapTableInit(); + } + + componentDidUpdate(prevProps, prevState) { + // if (this.state.editCellMode) { + // this.setState({ }) + // } + } + + // mapTableInit = async () => { + // let columns = []; + // let data = []; + // // let resTableData = this.props.resTableData; + // let { resTableData, mapTableTitle } = this.props; + // console.log('resTableData', resTableData); + // let features = this.props.resTableData.features; + // let newProperties = {}; + // let countPropKey = 0; + // let fidPosIdx = 0; + + // if (features.length > 0) { + // for (let i=0; i < features.length; i++) { + // features[i].properties.fid = features[i].id; // append the key value at the end of object + // // Object.assign({fid: features[i].id}, features[i].properties); + // // {'fid': features[i].id, ...features[i].properties}; + + // /*for(let key in features[i].properties) { + // if (countPropKey == fidPosIdx) { + // newProperties.fid = features[i].id; + // } + // newProperties[key] = features[i].properties[key]; + // countPropKey++; + // } + // console.log('newProperties', newProperties); + // data.push(newProperties);*/ + + // data.push(features[i].properties); + // } + + // for (let key in features[0].properties) { + // if (key == 'fid') { + // columns.push({ + // dataField: key, + // text: key, + // sort: true, + // hidden: true + // }); + // } + // else { + // columns.push({ + // dataField: key, + // text: key, + // sort: true + // }); + // } + // } + // } + // else { + // let reqTableColumns = await getTableColumns(mapTableTitle); + // console.log('reqTableColumns', reqTableColumns); + // if (reqTableColumns.success) { + // if (reqTableColumns.result !== null) { + // if (reqTableColumns.result.length > 0) { + // for(let i=0; i < reqTableColumns.result.length; i++) { + // let column_name = reqTableColumns.result[i].column_name; + // columns.push({ + // dataField: column_name, + // text: column_name, + // sort: true + // }); + // } + // } + // } + // } + // } + + // /*the data + // { + // type: + // id: + // geometry: + // geometry_name: + // properties: { + // gid: + // nama: + // .... + // } + // } + // I want something like this: + // { + // type: + // id: + // geometry: + // geometry_name: + // properties: { + // fid: id -> append the id to the properties, but hide on table, because table only show the properties + // gid: + // nama: + // .... + // } + // } + + // */ + + // console.log('features', features); + // console.log('columns', columns); + // console.log('data', data); + + // this.setState({ + // columns: columns, + // data: data, + // originalData: data, + // resTableData: resTableData + // }) + // } + + mapTableInit = async () => { + // req for the data + let { mapTableName } = this.props; + let res = await reqTableData(mapTableName); + if (res.success) { + this.setState({resTableData: res.result}, () => { + this.getLayerAttribute(mapTableName); + }); + } + else { + alert(res.result); + return; + } + } + + getLayerAttribute = async (layerName) => { + const res = await getLayerAttribute(layerName); + // console.log('getLayerAttribute',res); + if (res.success) { + // console.log(res.result); + if (res.result.data) { + if (res.result.data.length > 0) { + this.setState({layer_attribute: res.result.data}, () => { + console.log(this.state.layer_attribute); + this.populateMapTable(); + }); + } + } + } + else { + alert(res.result); + } + } + + populateMapTable = async () => { + const { mapTableName } = this.props; + const { resTableData, layer_attribute } = this.state; + console.log('resTableData', resTableData); + console.log('layer_attribute', layer_attribute); + let columns = []; + let data = []; + let features = resTableData.features; + let newProperties = {}; + let countPropKey = 0; + let fidPosIdx = 0; + + if (features.length > 0) { + for (let i=0; i < features.length; i++) { + features[i].properties.fid = features[i].id; // append the key value at the end of object + data.push(features[i].properties); + } + + for (let i=0; i < layer_attribute.length; i++) { + if (layer_attribute[i].attribute !== "the_geom") { + // skip the_geom column to be inserted to columns + if (layer_attribute[i].attribute_label !== null && layer_attribute[i].attribute_label !== '' ) { + columns.push({ + dataField: layer_attribute[i].attribute, + text: layer_attribute[i].attribute_label, + sort: true + }); + } + else { + columns.push({ + dataField: layer_attribute[i].attribute, + text: layer_attribute[i].attribute, + sort: true + }); + } + } + + // appending fid to columns + columns.push({ + dataField: 'fid', + text: 'fid', + sort: true, + hidden: true + }); + + } + + /*for (let key in features[0].properties) { + if (key == 'fid') { + columns.push({ + dataField: key, + text: key, + sort: true, + hidden: true + }); + } + else { + columns.push({ + dataField: key, + text: key, + sort: true + }); + } + }*/ + } + else { + let reqTableColumns = await getTableColumns(mapTableName); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + if (reqTableColumns.result !== null) { + if (reqTableColumns.result.length > 0) { + for(let i=0; i < reqTableColumns.result.length; i++) { + let column_name = reqTableColumns.result[i].column_name; + columns.push({ + dataField: column_name, + text: column_name, + sort: true + }); + } + } + } + } + } + + /*the data + { + type: + id: + geometry: + geometry_name: + properties: { + gid: + nama: + .... + } + } + I want something like this: + { + type: + id: + geometry: + geometry_name: + properties: { + fid: id -> append the id to the properties, but hide on table, because table only show the properties + gid: + nama: + .... + } + } + + */ + + console.log('features', features); + console.log('columns', columns); + console.log('data', data); + + this.setState({ + columns: columns, + data: data, + originalData: data + }) + } + + toggleEditCell() { + const { editCellMode } = this.state; + if (!editCellMode) { + this.setState({ + editCellMode: true, + selectRow: { + mode: 'checkbox', + clickToSelect: false, + clickToEdit: true, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e) + } + }); + } + else { + this.setState({ + editCellMode: false, + selectRow: { + mode: 'radio', + clickToSelect: true, + clickToEdit: false, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + bgColor: '#e4e5e6' + }, + selectedRows: [] + }); + } + } + + activateCellEdit() { + return cellEditFactory({ mode: 'click' }); + } + + handleOnSelect = (row, isSelect, rowIndex, e) => { + console.log('handleOnSelect row, isSelect, rowIndex, e', row, isSelect, rowIndex, e); + const { resTableData } = this.state; + let features = resTableData.features; + let selectedRow = findWhere(features, {id: row.fid}); + this.setState({selectedRowData: selectedRow}); + } + + onSelectRowTable = ({row, isSelect, e}) => { + const {selectedRows} = this.state + console.log({row, isSelect, e}) + console.log('typeof row',typeof(row)); + if(typeof(row) === "object"){ + if(isSelect){ + this.setState({selectedRows: [...selectedRows, row]}, () => { + console.log('onSelectRowTable selectedRows', this.state.selectedRows); + }) + } else { + const idx = selectedRows.indexOf(row) + selectedRows.splice(idx, 1) + this.setState({selectedRows}, () => { + console.log('onSelectRowTable selectedRows', this.state.selectedRows); + }) + } + } else { + console.log("type array") + this.setState({selectedRows: row}) + } + } + + onSelectAllRowTable = (isSelect, rows, e) => { + console.log('onSelectAllRowTable isSelect, rows, e', isSelect, rows, e); + const { selectedRows } = this.state; + + if (isSelect === false) { + // clear all this.state.selectedRows + this.setState({selectedRows: []}, () => { + console.log('onSelectAllRowTable selectedRows', this.state.selectedRows); + }); + } + else if (isSelect === true) { + let row = rows.map((item, index) => { + return item; + }); + console.log('row', row); + this.setState({selectedRows: row}, () => { + console.log('onSelectAllRowTable selectedRows', this.state.selectedRows); + }); + } + } + + + /*getTableColumns = async (tableName) => { + // let result = null; + let obj = { + tableName: tableName + } + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(obj) + } + + try { + const result = await fetch(API_GET_COLUMN_TABLE, param).then(response => response.json()).then(res => res) + console.log('result getTableColumns', result); + if(result){ + // console.log('after save',result); + // result = result; + return result; + } else { + // result = result; + return result; + } + } catch(err) { + return err.message; + // this.setState({modalAdd: false, alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + }*/ + + editTableColumn = async () => { + let reqTableColumns = await getTableColumns(this.props.mapTableName); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({editedColumnsData: reqTableColumns.result}, () => this.toggleEditTableColumn()); + } + else { + alert(reqTableColumns.result); + return; + } + } + + toggleEditTableColumn = () => { + this.setState({editTableColumnVisible: !this.state.editTableColumnVisible}) + } + + // When user select the row of table (creating vector layer named chosenLayer) + selectFeature = (selectedPopupData) => { + console.log('SearchFeatures selectFeature', selectedPopupData); + this.removeChosenLayer(); + const select = new Select(); + this.props.olmap.addInteraction(select); + let geomToDisplay = null; + + if (selectedPopupData.geometry === null) { + alert('Could not find the geometry'); + return; + } + + let geomType = selectedPopupData.geometry.type; + let coords = selectedPopupData.geometry.coordinates; + let { mapProjection } = this.state; + if (geomType === "Point") { + geomToDisplay = new Point(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPoint") { + geomToDisplay = new MultiPoint(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "LineString") { + geomToDisplay = new LineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiLineString") { + geomToDisplay = new MultiLineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "Polygon") { + geomToDisplay = new Polygon(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPolygon") { + geomToDisplay = new MultiPolygon(coords).transform('EPSG:4326', mapProjection); + } + + let styleSelected = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + // color: '#2b2d80', + color: '#1e00ff', + width: 3 + }), + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#1e00ff', + width: 3 + }) + }) + }); + + // let polygon = new MultiPolygon(country_indonesia.geometry.coordinates).transform('EPSG:4326', 'EPSG:3857') + + let chosenFeature = new Feature({ + geometry: geomToDisplay + }) + + let chosenLayer = new VectorLayer({ + name: 'ChosenLayer', + source: new VectorSource({ + features: [chosenFeature] + }), + style: styleSelected, + type: 'vector' + }); + + this.props.olmap.addLayer(chosenLayer); + } + + // removing existing ChosenLayer + removeChosenLayer = () => { + let layersToRemove = []; + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + // console.log('removeChosenLayer', layer); + layersToRemove.push(layer); + } + }); + + if (layersToRemove.length > 0) { + for(let i=0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + } + } + + // When user click on search list of feature, then go to feature + goToFeature = () => { + let selectedFeature = this.state.selectedRowData; + // if ("fid" in selectedFeature.properties) { + + // } + // let _selectedFeature = null; + if (selectedFeature.properties.hasOwnProperty("fid")) { + delete selectedFeature.properties["fid"]; + // console.log('_selectedFeature', _selectedFeature); + // console.log('selectedFeature', selectedFeature); + } + // console.log('selectedFeature outside', selectedFeature); + this.selectFeature(selectedFeature); + + // get chosenLayer -> get the chosenFeature -> get geometry -> get extent -> fit to map + const { fitOption } = this.state; + const proj = "EPSG:3857"; + let chosenLayer = null; + let chosenFeatures = null; + let featureExtent = null; + + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + return; + } + }) + + if (chosenLayer === null) { + alert('Could not go to feature because there is no geometry to go'); + // this.createGeomOnFeature(selectedFeature); + return; + } + + chosenFeatures = chosenLayer.getSource().getFeatures(); + console.log('SearchFeatures chosenFeatures', chosenFeatures); + featureExtent = chosenFeatures[0].getGeometry().getExtent() + + this.props.olmap.getView().fit(new transformExtent(featureExtent, proj, this.props.olmap.getView().getProjection()), fitOption); + + this.props.setPopupDataTemp(selectedFeature); + this.props.openPopupRight(); + this.props.toggleMapTable(); + } + + /*createGeomOnFeature = (selectedFeature) => { + let confirmation = window.confirm('Create the geometry anyway?'); + if (!confirmation) { + return; + } + this.props.toggleMapTable(); + }*/ + + /*updateFunction = async (oldValue, newValue, row, column) => { + console.log('------ updateFunction ------') + const { data, updatedData } = this.state; + // let newData = data; + let updatedDataArr = []; + + // cek apakah row ada di dalam updatedData + if (updatedData.length > 0) { + let updatedRow = findWhere(updatedData, {fid: row.fid}); + console.log('updatedRow', updatedRow); + + // jika tidak ada, maka push row ke updatedData + if (updatedRow == undefined) { + this.setState({updatedData: [...updatedData, row], saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + // jika ada, maka replace dengan data yg diedit + else { + let idx = updatedData.indexOf(updatedRow) + // updatedData.splice(idx, 1); + console.log('idx', idx); + updatedData[idx] = updatedRow; + this.setState({updatedData: updatedData, saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + } + // jika ada, maka update row yg ada di dalam updatedData + else { + this.setState({updatedData: [...updatedData, row], saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + + // console.log('updatedData afterrrr', this.state.updatedData); + }*/ + + updateFunction = async (oldValue, newValue, row, column) => { + console.log('oldValue', oldValue); + console.log('newValue', newValue); + console.log('row', row); + console.log('column', column); + + let value = newValue; + let _column = column.dataField; + + const { mapTableName } = this.props; + let _row = row; + console.log('_row', _row); + let fid = _row.fid; + let _prop = []; + if (_row.hasOwnProperty("fid")) { + delete _row.fid; + for(let key in _row) { + _prop.push({ + label: key, + value: _row[key] + }); + } + } + + let column_value = { + "column": _column, + "value": value + }; + + // console.log('updateFunction', mapTableName, fid, _prop); + // let response = await updateFeature2(mapTableName, fid, _prop); + + console.log('updateFunction', mapTableName, fid, column_value); + let response = await updateFeature2(mapTableName, fid, column_value); + + if (response.success) { + this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: "The data has been updated", selectedRows: []}, () => { + this.onSelectAllRowTable(false); + this.mapTableInit(); + }); + } + else { + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: "Oops! Some edited values are not saved", selectedRows: []}, () => { + this.onSelectAllRowTable(false); + this.mapTableInit(); + }); + } + + /*for (let i=0; i < updatedData.length; i++) { + // fid = updatedData[i].fid; + if (updatedData[i].hasOwnProperty("fid")) { + delete updatedData[i].fid; + // properties.push(updatedData[i]); + let _prop = []; + for(let key in updatedData[i]) { + _prop.push({ + label: key, + value: updatedData[i][key] + }); + } + properties.push(_prop); + } + console.log('confirmYes mapTableName, fid, properties', mapTableName, fid, properties); + // let res = await updateFeature(mapTableName, fid, properties); + let response = await updateFeature(mapTableName, fid, properties[0]); + + if (response.success) { + countSuccess = countSuccess+1; + properties = []; + } + }*/ + } + + /*deleteFunction = async param => { + const {selectedRows} = this.state + console.log("row selected",selectedRows) + if(param === "delete") { + + const {columns} = this.props + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(selectedRows) + } + + try { + const result = await fetch(this.props.urlParamDelete, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable(); + this.setState({alert: true, alertConfirm: false, messageAlert: result.code_message, successAlert: true, dangerAlert: false, ...val}) + } else { + this.setState({alert: true, alertConfirm: false, messageAlert: result.code_message, successAlert: false, dangerAlert: true, ...val}) + } + } catch { + this.setState({alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + } else { + this.setState({alertConfirm: false}) + } + + if (param === "delete") { + + } + }*/ + + saveUpdate = () => { + this.setState({confirmParam: 'save_update'}, () => this.showConfirm(this.state.confirmParam)); + // this.showConfirm('save_update'); + } + + deleteSelected = () => { + // this.showConfirm('delete_selected'); + this.setState({confirmParam: 'delete_selected'}, () => this.showConfirm(this.state.confirmParam)); + } + + showConfirm = (param) => { + if (param === 'save_update') { + this.setState({alertConfirm: true, alertConfirmBtnText: 'Save', alertConfirmType: 'success', alertConfirmMsg: 'Save the updates'}); + } + else if (param === 'delete_selected') { + this.setState({alertConfirm: true, alertConfirmBtnText: 'Delete', alertConfirmType: 'danger', alertConfirmMsg: 'Delete the selected rows'}); + } + } + + confirmYes = async () => { + const { data, updatedData, confirmParam } = this.state; + // console.log('updatedData', updatedData); + console.log(confirmParam); + const { mapTableName } = this.props; + let fid = null; + let properties = []; + let countSuccess = 0; + let totalUpdatedData = updatedData.length; + if (confirmParam === 'save_update') { + if (updatedData.length > 0) { + for (let i=0; i < updatedData.length; i++) { + fid = updatedData[i].fid; + if (updatedData[i].hasOwnProperty("fid")) { + delete updatedData[i].fid; + // properties.push(updatedData[i]); + let _prop = []; + for(let key in updatedData[i]) { + _prop.push({ + label: key, + value: updatedData[i][key] + }); + } + properties.push(_prop); + } + console.log('confirmYes mapTableName, fid, properties', mapTableName, fid, properties); + // let res = await updateFeature(mapTableName, fid, properties); + let response = await updateFeature(mapTableName, fid, properties[0]); + + if (response.success) { + countSuccess = countSuccess+1; + properties = []; + } + } + + if (countSuccess === totalUpdatedData) { + // alert('All edited values have been saved'); + this.confirmOnClose(); + this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: "All edited values have been saved"}); + } + else { + // alert('Oops! Some edited values are not saved'); + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: "Oops! Some edited values are not saved"}); + } + } + } + else if (confirmParam == 'delete_selected') { + const {selectedRows} = this.state + console.log('selectedRows', selectedRows); + let countDeleted = 0; + + if (selectedRows.length > 0) { + const { mapTableName } = this.props; + for (let i=0; i < selectedRows.length; i++) { + let fid = selectedRows[i].fid; + let response = await deleteFeature(mapTableName, fid); + if (response.success) { + countDeleted = countDeleted + 1; + // this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: "The data has been updated"}); + } + else { + // this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: "Oops! Some edited values are not saved"}); + } + } + + if (countDeleted == selectedRows.length) { + this.confirmOnClose(); + this.setState({ + alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: "The selected data have been deleted", + selectedRows: [] + }, () => { + this.mapTableInit(); + }); + } + else { + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: "Oops something went wrong! Some of your selected data have not been successfully deleted"}); + } + } + } + } + + confirmOnClose = () => { + this.setState({alertConfirm: false, alertConfirmBtnText: '', alertConfirmType: '', alertConfirmMsg: '', confirmParam: ''}); + } + + closeAlertResponse = () => { + this.setState({ + alertResponse: false, successAlert: false, dangerAlert: false, alertResponseMsg: '', + // saveMode: false, editCellMode: false, updatedData: [] + }); + } + + renderEditCellMode = () => { + const { editCellMode } = this.state; + // return editCellMode ? + // + // : + return + } + + render() { + const { columns, data, selectRow, editCellMode, editedColumnsData, fid, selectedRows, saveMode, + alertResponse, alertResponseMsg, successAlert, dangerAlert, + alertConfirm, alertConfirmBtnText, alertConfirmType, alertConfirmMsg, confirmParam + } = this.state; + + if (columns !== null ) { + return ( +
+ + this.closeAlertResponse()}> + {alertResponseMsg} + + + this.confirmYes()} + onCancel={() => this.confirmOnClose()} + focusCancelBtn + > + {alertConfirmMsg} + + + + + { + props => ( +
+ + +
+ +
+ + + {/*
+ + +
*/} + +
+ + { this.state.selectedRowData && !editCellMode ? +
+ + + View on Map + +
+ : null + } + + {saveMode && editCellMode && +
+ + + Save Changes + +
+ } + + {selectedRows.length > 0 && + +
+ + + Delete Selected Row + +
+ } + +
+ Edit Table
+ { + this.renderEditCellMode() + } +
+ + {/*
+ + + + + Export CSV + +
*/} + +
+ + + + + + this.editTableColumn()}> Edit Table Column + {/* + Export CSV + */} + + Export CSV + + + +
+
+
+ +
+ {/*
*/} +
+ {/**/} + + {editCellMode ? + this.updateFunction(oldValue, newValue, row, column) + }) } + /> + : + + } + +
+
+ ) + } +
+ + {this.state.editTableColumnVisible && + this.toggleEditTableColumn()} + modalTitle={`Edit ${this.props.mapTableTitle} attribute`} + editedColumnsData={editedColumnsData} + /> + } +
+ ) + } + else { + return null; + } + + } +} + +class MapTable extends Component { + constructor(props) { + super(props); + } + + componentDidMount() { + // console.log('MapTable resTableData', this.props.resTableData); + } + + render() { + return ( +
+ + {this.props.mapTableTitle} + +
+ {/*
+
*/} + { + + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + refreshMapTable={this.props.refreshMapTable} + /> + + } + {/*
+
*/} +
+
+ {/* + + */} +
+
+ ) + } +} + +export default MapTable; \ No newline at end of file diff --git a/src/components/MapTable/MapTable_backup.js b/src/components/MapTable/MapTable_backup.js new file mode 100644 index 0000000..7a4b005 --- /dev/null +++ b/src/components/MapTable/MapTable_backup.js @@ -0,0 +1,643 @@ +import React, { Component, Fragment } from 'react'; +import Draggable, { DraggableCore } from "react-draggable"; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +// import { Rnd } from 'react-rnd'; +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import cellEditFactory from 'react-bootstrap-table2-editor'; +import { Icon, InlineIcon } from '@iconify/react'; +import createOutline from '@iconify/icons-ion/create-outline'; +import earthIcon from '@iconify/icons-ion/earth'; +import archiveOutline from '@iconify/icons-ion/archive-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import { Col, Row } from 'reactstrap'; +import { AppSwitch } from '@coreui/react'; +import './MapTable.css'; +import '../../assets/css/customscroll.css' +import EditTableColumn from '../EditTableColumn'; +import { getTableColumns } from '../../const/GeoserverFunc.js'; +import { API_GET_COLUMN_TABLE } from '../../const/ApiConst.js'; +import { findWhere, without } from 'underscore'; + +// For Select feature then go to map +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Vector as VectorSource } from 'ol/source'; +import { fromLonLat, transformExtent } from 'ol/proj'; + +const { SearchBar } = Search; +const { ExportCSVButton } = CSVExport; +/*const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const products = [ +{ + "id": 0, + "name": "Item name 0", + "price": 2100 +}, +{ + "id": 1, + "name": "Item name 1", + "price": 2101 +}, +{ + "id": 2, + "name": "Item name 2", + "price": 2102 +}, +{ + "id": 3, + "name": "Item name 3", + "price": 2103 +}];*/ + +/*const TableContent = () => { + let tableData = this.props.resTableData; + if (tableData !== null) { + return ( + + { + props => ( +
+

Input something at below input field:

+ +
+ +
+ ) + } +
+ ) + } +}*/ + +// const selectRow = { +// mode: 'radio', +// clickToSelect: true, +// clickToEdit: true // Click to edit cell also +// }; + +class TableContent extends Component { + + constructor(props) { + super(props) + this.state = { + fid: 'fid', + columns: null, + data: null, + resTableData: null, + selectRow: { + mode: 'radio', + clickToSelect: true, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + clickToEdit: false, + bgColor: '#e4e5e6', + }, + editCellMode: false, + editTableColumnVisible: false, + selectedRowData: null, + editedColumnsData: null, + // For Select Feature + mapProjection: this.props.olmap.getView().getProjection(), + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + } + } + this.toggleEditCell = this.toggleEditCell.bind(this); + this.mapTableInit = this.mapTableInit.bind(this); + this.activateCellEdit = this.activateCellEdit.bind(this); + } + + componentDidMount() { + console.log('this.state', this.state); + this.mapTableInit(); + } + + componentDidUpdate(prevProps, prevState) { + // if (this.state.editCellMode) { + // this.setState({ }) + // } + } + + mapTableInit() { + let columns = []; + let data = []; + let resTableData = this.props.resTableData; + console.log('resTableData', resTableData); + let features = this.props.resTableData.features; + let newProperties = {}; + let countPropKey = 0; + let fidPosIdx = 0; + + /*the data + { + type: + id: + geometry: + geometry_name: + properties: { + gid: + nama: + .... + } + } + I want something like this: + { + type: + id: + geometry: + geometry_name: + properties: { + fid: id -> append the id to the properties, but hide on table, because table only show the properties + gid: + nama: + .... + } + } + + */ + + for (let i=0; i < features.length; i++) { + features[i].properties.fid = features[i].id; // append the key value at the end of object + // Object.assign({fid: features[i].id}, features[i].properties); + // {'fid': features[i].id, ...features[i].properties}; + + /*for(let key in features[i].properties) { + if (countPropKey == fidPosIdx) { + newProperties.fid = features[i].id; + } + newProperties[key] = features[i].properties[key]; + countPropKey++; + } + console.log('newProperties', newProperties); + data.push(newProperties);*/ + + data.push(features[i].properties); + } + + for (let key in features[0].properties) { + columns.push({ + dataField: key, + text: key, + sort: true + }); + } + + + console.log('features', features); + console.log('columns', columns); + console.log('data', data); + + this.setState({ + columns: columns, + data: data, + resTableData: resTableData + }) + } + + toggleEditCell() { + let { editCellMode } = this.state; + if (!editCellMode) { + this.setState({ + editCellMode: true, + selectRow: { + mode: 'radio', + clickToSelect: false, + clickToEdit: true, + bgColor: '#e4e5e6' + } + }); + } + else { + this.setState({ + editCellMode: false, + selectRow: { + mode: 'radio', + clickToSelect: true, + clickToEdit: false, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + bgColor: '#e4e5e6' + } + }); + } + } + + activateCellEdit() { + return cellEditFactory({ mode: 'click' }); + } + + handleOnSelect = (row, isSelect, rowIndex, e) => { + console.log('handleOnSelect row, isSelect, rowIndex, e', row, isSelect, rowIndex, e); + const { resTableData } = this.state; + let features = resTableData.features; + // let features = + // console.log('handleOnSelect', row, isSelect, rowIndex, e); + // console.log('resTableData', this.state.resTableData); + // let selectedRow = findWhere(features, {properties: row}); + let selectedRow = findWhere(features, {id: row.fid}); + // console.log('handleOnSelect', selectedRow); + // this.setState({selectedRowData: selectedRow}, () => this.selectFeature(this.state.selectedRowData)); + this.setState({selectedRowData: selectedRow}); + } + + + /*getTableColumns = async (tableName) => { + // let result = null; + let obj = { + tableName: tableName + } + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(obj) + } + + try { + const result = await fetch(API_GET_COLUMN_TABLE, param).then(response => response.json()).then(res => res) + console.log('result getTableColumns', result); + if(result){ + // console.log('after save',result); + // result = result; + return result; + } else { + // result = result; + return result; + } + } catch(err) { + return err.message; + // this.setState({modalAdd: false, alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + }*/ + + editTableColumn = async () => { + let reqTableColumns = await getTableColumns(this.props.mapTableTitle); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({editedColumnsData: reqTableColumns.result}, () => this.toggleEditTableColumn()); + } + else { + alert(reqTableColumns.result); + return; + } + } + + toggleEditTableColumn = () => { + this.setState({editTableColumnVisible: !this.state.editTableColumnVisible}) + } + + // When user select the row of table (creating vector layer named chosenLayer) + selectFeature = (selectedPopupData) => { + console.log('SearchFeatures selectFeature', selectedPopupData); + this.removeChosenLayer(); + const select = new Select(); + this.props.olmap.addInteraction(select); + let geomToDisplay = null; + + if (selectedPopupData.geometry === null) { + alert('Could not find the geometry'); + return; + } + + let geomType = selectedPopupData.geometry.type; + let coords = selectedPopupData.geometry.coordinates; + let { mapProjection } = this.state; + if (geomType === "Point") { + geomToDisplay = new Point(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPoint") { + geomToDisplay = new MultiPoint(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "LineString") { + geomToDisplay = new LineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiLineString") { + geomToDisplay = new MultiLineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "Polygon") { + geomToDisplay = new Polygon(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPolygon") { + geomToDisplay = new MultiPolygon(coords).transform('EPSG:4326', mapProjection); + } + + let styleSelected = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + // color: '#2b2d80', + color: '#1e00ff', + width: 3 + }), + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#1e00ff', + width: 3 + }) + }) + }); + + // let polygon = new MultiPolygon(country_indonesia.geometry.coordinates).transform('EPSG:4326', 'EPSG:3857') + + let chosenFeature = new Feature({ + geometry: geomToDisplay + }) + + let chosenLayer = new VectorLayer({ + name: 'ChosenLayer', + source: new VectorSource({ + features: [chosenFeature] + }), + style: styleSelected, + type: 'vector' + }); + + this.props.olmap.addLayer(chosenLayer); + } + + // removing existing ChosenLayer + removeChosenLayer = () => { + let layersToRemove = []; + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + // console.log('removeChosenLayer', layer); + layersToRemove.push(layer); + } + }); + + if (layersToRemove.length > 0) { + for(let i=0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + } + } + + // When user click on search list of feature, then go to feature + goToFeature = () => { + let selectedFeature = this.state.selectedRowData; + + this.selectFeature(selectedFeature); + + // get chosenLayer -> get the chosenFeature -> get geometry -> get extent -> fit to map + const { fitOption } = this.state; + const proj = "EPSG:3857"; + let chosenLayer = null; + let chosenFeatures = null; + let featureExtent = null; + + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + return; + } + }) + + if (chosenLayer === null) { + alert('Could not go to feature because there is no geometry to go'); + // this.createGeomOnFeature(selectedFeature); + return; + } + + chosenFeatures = chosenLayer.getSource().getFeatures(); + console.log('SearchFeatures chosenFeatures', chosenFeatures); + featureExtent = chosenFeatures[0].getGeometry().getExtent() + + this.props.olmap.getView().fit(new transformExtent(featureExtent, proj, this.props.olmap.getView().getProjection()), fitOption); + + this.props.setPopupDataTemp(selectedFeature); + this.props.openPopupRight(); + this.props.toggleMapTable(); + } + + /*createGeomOnFeature = (selectedFeature) => { + let confirmation = window.confirm('Create the geometry anyway?'); + if (!confirmation) { + return; + } + this.props.toggleMapTable(); + }*/ + + + render() { + const { columns, data, selectRow, editCellMode, editedColumnsData, fid } = this.state; + // console.log('TableContent resTableData', this.props.resTableData); + // console.log('this.state', this.state); + if (columns !== null ) { + // console.log('columns is not null', columns); + return ( +
+ + { + props => ( +
+ + +
+ +
+ + + {/*
+ + +
*/} + +
+ + { this.state.selectedRowData ? +
+ + + View on Map + +
+ : null + } +
+ Edit Table
+ { editCellMode ? + + : + } +
+ + {/*
+ + + + + Export CSV + +
*/} + +
+ + + + + + this.editTableColumn()}> Edit Table Column + {/* + Export CSV + */} + + Export CSV + + + +
+
+
+ +
+ {/*
*/} +
+ {/**/} + + {editCellMode ? + + : + + } + +
+
+ ) + } +
+ + {this.state.editTableColumnVisible && + this.toggleEditTableColumn()} + modalTitle={`Edit ${this.props.mapTableTitle} attribute`} + editedColumnsData={editedColumnsData} + /> + } +
+ ) + } + else { + return null; + } + + } +} + +class MapTable extends Component { + constructor(props) { + super(props); + } + + componentDidMount() { + // console.log('MapTable resTableData', this.props.resTableData); + } + + render() { + return ( +
+ + {this.props.mapTableTitle} + +
+ {/*
+
*/} + { + this.props.resTableData !== undefined ? + this.props.resTableData !== null ? + this.props.resTableData.features.length > 0 ? + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + /> + : 'Table Data is empty' + : 'Table Data is null' + : 'Table Data is undefined' + } + {/*
+
*/} +
+
+ {/* + + */} +
+
+ ) + } +} + +export default MapTable; \ No newline at end of file diff --git a/src/components/MapTable/MapTable_backup2.js b/src/components/MapTable/MapTable_backup2.js new file mode 100644 index 0000000..68fdfab --- /dev/null +++ b/src/components/MapTable/MapTable_backup2.js @@ -0,0 +1,949 @@ +import React, { Component, Fragment } from 'react'; +import Draggable, { DraggableCore } from "react-draggable"; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +// import { Rnd } from 'react-rnd'; +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import cellEditFactory from 'react-bootstrap-table2-editor'; +import { Icon, InlineIcon } from '@iconify/react'; +import createOutline from '@iconify/icons-ion/create-outline'; +import earthIcon from '@iconify/icons-ion/earth'; +import archiveOutline from '@iconify/icons-ion/archive-outline'; +import saveOutline from '@iconify/icons-ion/save-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import { Col, Row } from 'reactstrap'; +import { AppSwitch } from '@coreui/react'; +import './MapTable.css'; +import '../../assets/css/customscroll.css' +import EditTableColumn from '../EditTableColumn'; +import { getTableColumns, updateFeature } from '../../const/GeoserverFunc.js'; +import { API_GET_COLUMN_TABLE } from '../../const/ApiConst.js'; +import { findWhere, without } from 'underscore'; + +// For Select feature then go to map +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Vector as VectorSource } from 'ol/source'; +import { fromLonLat, transformExtent } from 'ol/proj'; +import SweetAlert from 'react-bootstrap-sweetalert'; + +const { SearchBar } = Search; +const { ExportCSVButton } = CSVExport; +/*const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const products = [ +{ + "id": 0, + "name": "Item name 0", + "price": 2100 +}, +{ + "id": 1, + "name": "Item name 1", + "price": 2101 +}, +{ + "id": 2, + "name": "Item name 2", + "price": 2102 +}, +{ + "id": 3, + "name": "Item name 3", + "price": 2103 +}];*/ + +/*const TableContent = () => { + let tableData = this.props.resTableData; + if (tableData !== null) { + return ( + + { + props => ( +
+

Input something at below input field:

+ +
+ +
+ ) + } +
+ ) + } +}*/ + +// const selectRow = { +// mode: 'radio', +// clickToSelect: true, +// clickToEdit: true // Click to edit cell also +// }; + +class TableContent extends Component { + + constructor(props) { + super(props) + this.state = { + fid: 'fid', + columns: null, + originalData: null, + data: null, + resTableData: null, + selectRow: { + mode: 'radio', + clickToSelect: true, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + clickToEdit: false, + bgColor: '#e4e5e6', + }, + editCellMode: false, + editTableColumnVisible: false, + selectedRowData: null, + editedColumnsData: null, + // For Select Feature + mapProjection: this.props.olmap.getView().getProjection(), + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + }, + selectedRows: [], + updatedData: [], + alertConfirm: false, + alertConfirmBtnText: '', // Delete, Update + alertConfirmType: '', // danger, success, warning, info + alertConfirmMsg: '', // Under are you sure message: ... ? + confirmParam: '', + alertResponse: false, + alertResponseMsg: '', + successAlert: false, + dangerAlert: false, + saveMode: false + } + this.toggleEditCell = this.toggleEditCell.bind(this); + // this.mapTableInit = this.mapTableInit.bind(this); + this.activateCellEdit = this.activateCellEdit.bind(this); + } + + componentDidMount() { + console.log('this.state', this.state); + this.mapTableInit(); + } + + componentDidUpdate(prevProps, prevState) { + // if (this.state.editCellMode) { + // this.setState({ }) + // } + } + + mapTableInit = async () => { + let columns = []; + let data = []; + // let resTableData = this.props.resTableData; + let { resTableData, mapTableTitle } = this.props; + console.log('resTableData', resTableData); + let features = this.props.resTableData.features; + let newProperties = {}; + let countPropKey = 0; + let fidPosIdx = 0; + + if (features.length > 0) { + for (let i=0; i < features.length; i++) { + features[i].properties.fid = features[i].id; // append the key value at the end of object + // Object.assign({fid: features[i].id}, features[i].properties); + // {'fid': features[i].id, ...features[i].properties}; + + /*for(let key in features[i].properties) { + if (countPropKey == fidPosIdx) { + newProperties.fid = features[i].id; + } + newProperties[key] = features[i].properties[key]; + countPropKey++; + } + console.log('newProperties', newProperties); + data.push(newProperties);*/ + + data.push(features[i].properties); + } + + for (let key in features[0].properties) { + if (key == 'fid') { + columns.push({ + dataField: key, + text: key, + sort: true, + hidden: true + }); + } + else { + columns.push({ + dataField: key, + text: key, + sort: true + }); + } + } + } + else { + let reqTableColumns = await getTableColumns(mapTableTitle); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + if (reqTableColumns.result !== null) { + if (reqTableColumns.result.length > 0) { + for(let i=0; i < reqTableColumns.result.length; i++) { + let column_name = reqTableColumns.result[i].column_name; + columns.push({ + dataField: column_name, + text: column_name, + sort: true + }); + } + } + } + } + } + + /*the data + { + type: + id: + geometry: + geometry_name: + properties: { + gid: + nama: + .... + } + } + I want something like this: + { + type: + id: + geometry: + geometry_name: + properties: { + fid: id -> append the id to the properties, but hide on table, because table only show the properties + gid: + nama: + .... + } + } + + */ + + console.log('features', features); + console.log('columns', columns); + console.log('data', data); + + this.setState({ + columns: columns, + data: data, + originalData: data, + resTableData: resTableData + }) + } + + toggleEditCell() { + const { editCellMode } = this.state; + if (!editCellMode) { + this.setState({ + editCellMode: true, + selectRow: { + mode: 'checkbox', + clickToSelect: false, + clickToEdit: true, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e) + } + }); + } + else { + this.setState({ + editCellMode: false, + selectRow: { + mode: 'radio', + clickToSelect: true, + clickToEdit: false, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + bgColor: '#e4e5e6' + } + }); + } + } + + activateCellEdit() { + return cellEditFactory({ mode: 'click' }); + } + + handleOnSelect = (row, isSelect, rowIndex, e) => { + console.log('handleOnSelect row, isSelect, rowIndex, e', row, isSelect, rowIndex, e); + const { resTableData } = this.state; + let features = resTableData.features; + let selectedRow = findWhere(features, {id: row.fid}); + this.setState({selectedRowData: selectedRow}); + } + + onSelectRowTable = ({row, isSelect, e}) => { + const {selectedRows} = this.state + console.log({row, isSelect, e}) + console.log('typeof row',typeof(row)); + if(typeof(row) === "object"){ + if(isSelect){ + this.setState({selectedRows: [...selectedRows, row]}, () => { + console.log('onSelectRowTable selectedRows', this.state.selectedRows); + }) + } else { + const idx = selectedRows.indexOf(row) + selectedRows.splice(idx, 1) + this.setState({selectedRows}, () => { + console.log('onSelectRowTable selectedRows', this.state.selectedRows); + }) + } + } else { + console.log("type array") + this.setState({selectedRows: row}) + } + } + + onSelectAllRowTable = (isSelect, rows, e) => { + console.log('onSelectAllRowTable isSelect, rows, e', isSelect, rows, e); + const { selectedRows } = this.state; + + if (isSelect === false) { + // clear all this.state.selectedRows + this.setState({selectedRows: []}, () => { + console.log('onSelectAllRowTable selectedRows', this.state.selectedRows); + }); + } + else if (isSelect === true) { + let row = rows.map((item, index) => { + return item; + }); + console.log('row', row); + this.setState({selectedRows: row}, () => { + console.log('onSelectAllRowTable selectedRows', this.state.selectedRows); + }); + } + } + + + /*getTableColumns = async (tableName) => { + // let result = null; + let obj = { + tableName: tableName + } + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(obj) + } + + try { + const result = await fetch(API_GET_COLUMN_TABLE, param).then(response => response.json()).then(res => res) + console.log('result getTableColumns', result); + if(result){ + // console.log('after save',result); + // result = result; + return result; + } else { + // result = result; + return result; + } + } catch(err) { + return err.message; + // this.setState({modalAdd: false, alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + }*/ + + editTableColumn = async () => { + let reqTableColumns = await getTableColumns(this.props.mapTableTitle); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({editedColumnsData: reqTableColumns.result}, () => this.toggleEditTableColumn()); + } + else { + alert(reqTableColumns.result); + return; + } + } + + toggleEditTableColumn = () => { + this.setState({editTableColumnVisible: !this.state.editTableColumnVisible}) + } + + // When user select the row of table (creating vector layer named chosenLayer) + selectFeature = (selectedPopupData) => { + console.log('SearchFeatures selectFeature', selectedPopupData); + this.removeChosenLayer(); + const select = new Select(); + this.props.olmap.addInteraction(select); + let geomToDisplay = null; + + if (selectedPopupData.geometry === null) { + alert('Could not find the geometry'); + return; + } + + let geomType = selectedPopupData.geometry.type; + let coords = selectedPopupData.geometry.coordinates; + let { mapProjection } = this.state; + if (geomType === "Point") { + geomToDisplay = new Point(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPoint") { + geomToDisplay = new MultiPoint(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "LineString") { + geomToDisplay = new LineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiLineString") { + geomToDisplay = new MultiLineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "Polygon") { + geomToDisplay = new Polygon(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPolygon") { + geomToDisplay = new MultiPolygon(coords).transform('EPSG:4326', mapProjection); + } + + let styleSelected = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + // color: '#2b2d80', + color: '#1e00ff', + width: 3 + }), + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#1e00ff', + width: 3 + }) + }) + }); + + // let polygon = new MultiPolygon(country_indonesia.geometry.coordinates).transform('EPSG:4326', 'EPSG:3857') + + let chosenFeature = new Feature({ + geometry: geomToDisplay + }) + + let chosenLayer = new VectorLayer({ + name: 'ChosenLayer', + source: new VectorSource({ + features: [chosenFeature] + }), + style: styleSelected, + type: 'vector' + }); + + this.props.olmap.addLayer(chosenLayer); + } + + // removing existing ChosenLayer + removeChosenLayer = () => { + let layersToRemove = []; + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + // console.log('removeChosenLayer', layer); + layersToRemove.push(layer); + } + }); + + if (layersToRemove.length > 0) { + for(let i=0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + } + } + + // When user click on search list of feature, then go to feature + goToFeature = () => { + let selectedFeature = this.state.selectedRowData; + // if ("fid" in selectedFeature.properties) { + + // } + // let _selectedFeature = null; + if (selectedFeature.properties.hasOwnProperty("fid")) { + delete selectedFeature.properties["fid"]; + // console.log('_selectedFeature', _selectedFeature); + // console.log('selectedFeature', selectedFeature); + } + // console.log('selectedFeature outside', selectedFeature); + this.selectFeature(selectedFeature); + + // get chosenLayer -> get the chosenFeature -> get geometry -> get extent -> fit to map + const { fitOption } = this.state; + const proj = "EPSG:3857"; + let chosenLayer = null; + let chosenFeatures = null; + let featureExtent = null; + + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + return; + } + }) + + if (chosenLayer === null) { + alert('Could not go to feature because there is no geometry to go'); + // this.createGeomOnFeature(selectedFeature); + return; + } + + chosenFeatures = chosenLayer.getSource().getFeatures(); + console.log('SearchFeatures chosenFeatures', chosenFeatures); + featureExtent = chosenFeatures[0].getGeometry().getExtent() + + this.props.olmap.getView().fit(new transformExtent(featureExtent, proj, this.props.olmap.getView().getProjection()), fitOption); + + this.props.setPopupDataTemp(selectedFeature); + this.props.openPopupRight(); + this.props.toggleMapTable(); + } + + /*createGeomOnFeature = (selectedFeature) => { + let confirmation = window.confirm('Create the geometry anyway?'); + if (!confirmation) { + return; + } + this.props.toggleMapTable(); + }*/ + + updateFunction = async (oldValue, newValue, row, column) => { + console.log('------ updateFunction ------') + const { data, updatedData } = this.state; + // let newData = data; + let updatedDataArr = []; + /*console.log('data before', data); + console.log('updateFunction oldValue, newValue, row, column', oldValue, newValue, row, column); + + // let updatedRow = findWhere(data, {fid: row.fid}); + let updatedRow = findWhere(data, {fid: row.fid}); + console.log('updatedRow', updatedRow); + console.log('data after', data); + + this.setState({data: data, saveMode: true});*/ + + + // cek apakah row ada di dalam updatedData + if (updatedData.length > 0) { + let updatedRow = findWhere(updatedData, {fid: row.fid}); + console.log('updatedRow', updatedRow); + + // jika tidak ada, maka push row ke updatedData + if (updatedRow == undefined) { + this.setState({updatedData: [...updatedData, row], saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + // jika ada, maka replace dengan data yg diedit + else { + let idx = updatedData.indexOf(updatedRow) + // updatedData.splice(idx, 1); + console.log('idx', idx); + updatedData[idx] = updatedRow; + this.setState({updatedData: updatedData, saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + } + // jika ada, maka update row yg ada di dalam updatedData + else { + this.setState({updatedData: [...updatedData, row], saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + + // console.log('updatedData afterrrr', this.state.updatedData); + } + + deleteFunction = async param => { + const {selectedRows} = this.state + console.log("row selected",selectedRows) + /*if(param === "delete") { + + const {columns} = this.props + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(selectedRows) + } + + try { + const result = await fetch(this.props.urlParamDelete, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable(); + this.setState({alert: true, alertConfirm: false, messageAlert: result.code_message, successAlert: true, dangerAlert: false, ...val}) + } else { + this.setState({alert: true, alertConfirm: false, messageAlert: result.code_message, successAlert: false, dangerAlert: true, ...val}) + } + } catch { + this.setState({alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + } else { + this.setState({alertConfirm: false}) + }*/ + + if (param === "delete") { + + } + } + + saveUpdate = () => { + this.setState({confirmParam: 'save_update'}, () => this.showConfirm(this.state.confirmParam)); + // this.showConfirm('save_update'); + } + + deleteSelected = () => { + // this.showConfirm('delete_selected'); + this.setState({confirmParam: 'delete_selected'}, () => this.showConfirm(this.state.confirmParam)); + } + + showConfirm = (param) => { + if (param === 'save_update') { + this.setState({alertConfirm: true, alertConfirmBtnText: 'Save', alertConfirmType: 'success', alertConfirmMsg: 'Save the updates'}); + } + else if (param === 'delete_selected') { + this.setState({alertConfirm: true, alertConfirmBtnText: 'Delete', alertConfirmType: 'danger', alertConfirmMsg: 'Delete the selected rows'}); + } + } + + confirmYes = async (confirmParam) => { + console.log(confirmParam); + const { data, updatedData } = this.state; + console.log('updatedData', updatedData); + const { mapTableTitle } = this.props; + let fid = null; + let properties = []; + let countSuccess = 0; + let totalUpdatedData = updatedData.length; + if (confirmParam === 'save_update') { + if (updatedData.length > 0) { + for (let i=0; i < updatedData.length; i++) { + fid = updatedData[i].fid; + if (updatedData[i].hasOwnProperty("fid")) { + delete updatedData[i].fid; + // properties.push(updatedData[i]); + let _prop = []; + for(let key in updatedData[i]) { + _prop.push({ + label: key, + value: updatedData[i][key] + }); + } + properties.push(_prop); + } + console.log('confirmYes mapTableTitle, fid, properties', mapTableTitle, fid, properties); + // let res = await updateFeature(mapTableTitle, fid, properties); + let response = await updateFeature(mapTableTitle, fid, properties[0]); + + if (response.success) { + countSuccess = countSuccess+1; + properties = []; + } + } + + if (countSuccess === totalUpdatedData) { + // alert('All edited values have been saved'); + this.confirmCancel(); + this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: "All edited values have been saved"}); + } + else { + // alert('Oops! Some edited values are not saved'); + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: "Oops! Some edited values are not saved"}); + } + } + } + else if (confirmParam == 'delete_selected') { + + } + } + + confirmCancel = () => { + this.setState({alertConfirm: false, alertConfirmBtnText: '', alertConfirmType: '', alertConfirmMsg: '', confirmParam: ''}); + } + + closeAlertResponse = () => { + this.setState({ + alertResponse: false, successAlert: false, dangerAlert: false, alertResponseMsg: '', + // saveMode: false, editCellMode: false, updatedData: [] + }); + } + + renderEditCellMode = () => { + const { editCellMode } = this.state; + return editCellMode ? + + : + } + + render() { + const { columns, data, selectRow, editCellMode, editedColumnsData, fid, selectedRows, saveMode, + alertResponse, alertResponseMsg, successAlert, dangerAlert, + alertConfirm, alertConfirmBtnText, alertConfirmType, alertConfirmMsg, confirmParam + } = this.state; + + if (columns !== null ) { + return ( +
+ + this.closeAlertResponse()}> + {alertResponseMsg} + + + this.confirmYes(confirmParam)} + onCancel={() => this.confirmCancel()} + focusCancelBtn + > + {alertConfirmMsg} + + + + + { + props => ( +
+ + +
+ +
+ + + {/*
+ + +
*/} + +
+ + { this.state.selectedRowData && !editCellMode ? +
+ + + View on Map + +
+ : null + } + + {saveMode && editCellMode && +
+ + + Save Changes + +
+ } + + {selectedRows.length > 0 && + +
+ + + Delete Selected Row + +
+ } + +
+ Edit Table
+ { + this.renderEditCellMode() + } +
+ + {/*
+ + + + + Export CSV + +
*/} + +
+ + + + + + this.editTableColumn()}> Edit Table Column + {/* + Export CSV + */} + + Export CSV + + + +
+
+
+ +
+ {/*
*/} +
+ {/**/} + + {editCellMode ? + this.updateFunction(oldValue, newValue, row, column) + }) } + /> + : + + } + +
+
+ ) + } +
+ + {this.state.editTableColumnVisible && + this.toggleEditTableColumn()} + modalTitle={`Edit ${this.props.mapTableTitle} attribute`} + editedColumnsData={editedColumnsData} + /> + } +
+ ) + } + else { + return null; + } + + } +} + +class MapTable extends Component { + constructor(props) { + super(props); + } + + componentDidMount() { + // console.log('MapTable resTableData', this.props.resTableData); + } + + render() { + return ( +
+ + {this.props.mapTableTitle} + +
+ {/*
+
*/} + { + this.props.resTableData !== undefined ? + this.props.resTableData !== null ? + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + /> + : 'Table Data is null' + : 'Table Data is undefined' + } + {/*
+
*/} +
+
+ {/* + + */} +
+
+ ) + } +} + +export default MapTable; \ No newline at end of file diff --git a/src/components/MapTable/package.json b/src/components/MapTable/package.json new file mode 100644 index 0000000..58f83ea --- /dev/null +++ b/src/components/MapTable/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapTable", + "version": "0.0.0", + "private": true, + "main": "./MapTable.js" +} diff --git a/src/components/MapTable2/MapTable.css b/src/components/MapTable2/MapTable.css new file mode 100644 index 0000000..ff05196 --- /dev/null +++ b/src/components/MapTable2/MapTable.css @@ -0,0 +1,159 @@ +.modal-wrapper { + margin-top: 10px; +} + +.modal-dialog-data { + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} + +.modal-content-data { + width: 100vw; + min-height: 100%; + border-radius: 0; +} + +.modal-body-wrap { + /*height: auto !important;*/ + /*padding: 10px 0 10px;*/ + /*margin-bottom: -100px;*/ + /*min-height: 100%;*/ +} + +.table-wrapper { + /*height: 60vh;*/ + /*height: 150px;*/ + /*width: 100%;*/ + /*bottom: 100px;*/ + margin-right: 50px; + margin-left: 50px; + overflow: auto; +} + +.react-bootstrap-table { + font-size: 12px; +} + +.maptable-search-bar-wrapper { + /*text-align: left;*/ + /*margin-left: 50px;*/ +} + +.map-table-button-group { + /*float: right;*/ +} + +.map-table-button { + margin-right: 5px; +} + +.text-white { + color: #FFFFFF; +} + +.table-option-button-group { + margin-right: 15px; +} + +.switch-button-upper { + margin-top: -5px; +} + +.button-option-table-wrapper { + margin-left: 15px; +} + +.button-option-table { + background-color: #ffffff; + padding: 5px 0px; + margin-top: 3px; +} + +.option-button-wrapper { + margin-right: 10px; + margin-top: 5px; +} + +.maptable-title { + font-size: 24px; + font-weight: 700; + text-align: left; + margin-left: 20px; +} + +.maptable-window-button-container { + float: right; + right: 0px; +} + +.maptable-close, .maptable-maximize, .maptable-minimize { + cursor: pointer; + padding: 4px; +} + +.maptable-close:hover, .maptable-maximize:hover, .maptable-minimize:hover { + color: #20a8d8; +} + +.maptable-button-action { + cursor: pointer; +} + +.maptable-button-action:hover { + color: #20a8d8; +} + +.maptable-container { + max-height: 400px; + overflow: auto; + margin-left: 20px; + margin-right: 20px; + background-color: #ffffff; +} + +.maptable-header { + margin-bottom: -10px; +} + +.maptable-header-tools { + text-align: left !important; + margin-left: 50px; + margin-bottom: 5px; +} + +.maptable-button-action-container { + /*margin-right: 75px;*/ + margin-left: 10px; +} + +.pull-right { + float: right; +} + +.maptable-button-action-tooltip { + z-index: 99999; +} + +.div-inline { + display: inline-block; +} + +.maptable-nodata-container { + text-align: center !important; + padding: 20px; + color: red; + font-weight: 400; +} + +.react-bootstrap-table th { + position: sticky; + top: 1px; + background-color: #fff; + padding: 0 !important; +} + +.react-bootstrap-table td { + padding: 0 !important; +} \ No newline at end of file diff --git a/src/components/MapTable2/MapTable.js b/src/components/MapTable2/MapTable.js new file mode 100644 index 0000000..1dd1059 --- /dev/null +++ b/src/components/MapTable2/MapTable.js @@ -0,0 +1,977 @@ +import { Vector as VectorSource } from 'ol/source'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Point, LineString } from 'ol/geom'; +import Feature from 'ol/Feature'; +import { Resizable } from "re-resizable"; +import React, { Component } from 'react'; +import { Col, Row, Table, Card, CardHeader, CardBody, Input} from 'reactstrap'; +import { Icon } from '@iconify/react'; +import closeCircleOutline from '@iconify/icons-ion/close-circle-outline'; +// import removeIcon from '@iconify/icons-ion/remove'; +import removeCircleOutline from '@iconify/icons-ion/remove-circle-outline'; +import windowMaximaze from '@iconify/icons-mdi/window-maximize'; +import { Pagination } from 'antd'; +import moment from 'moment'; +import { getSalesRoutingApi } from '../../const/GeohrApiFunc.js'; +import { ROUTE_MAP_STYLES } from '../../const/GeohrMapStyles.js'; +import { getRandomColor } from '../../const/GeoserverFunc.js'; +import '../../assets/css/customscroll.css'; +import './MapTable.css'; + + +const column = [ + { name: "Group Sales" }, + { name: "Name" }, + { name: "Phone Number" }, + { name: "Email" }, + { name: "Address" }, +] + +const style = { + // display: "flex", + // alignItems: "center", + // justifyContent: "center", + position: "fixed", + width: "100%", + bottom: 0, + left: 0, + right: 0, + border: "solid 1px #ddd", + background: "#ffffff", + zIndex: 9999 +}; + +const dummyData = [ + { + id: 1, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 2, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 3, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 4, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 5, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 1, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 2, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 3, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 4, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 5, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 1, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 2, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 3, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 4, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + }, + { + id: 5, + sales_group: "Kebayoran Baru", + name: "Yudi", + phone_number: "0986123173721", + email: "yudi@gmail.com", + address: "Jl.radio dalam, jakarta selatan" + } +] + +class TableContent extends Component { + + constructor(props) { + super(props) + this.state = { + primaryKey: 'id', + // columns: [], + data: dummyData, + columns: null, + // data: null + selected: [], + tableHeight: 150, + resizableWidth: "100%", + resizableHeight: 300, + maximizeTitle: "maximize" + } + } + + componentDidMount() { + + } + + componentDidUpdate(prevProps, prevState) { + if (prevState.selected !== this.state.selected) { + console.log('selected', this.state.selected); + } + } + + goToFeature = (row) => { + console.log('goToFeature', row); + } + + + handleOnSelect = (row, isSelect) => { + console.log('[MapTable2] handleOnSelect', isSelect, row); + if (isSelect) { + this.setState({selected: [...this.state.selected, row.id]}) + } + else { + this.setState({selected: this.state.selected.filter(x => x !== row.id)}) + } + } + + handleOnSelectAll = (isSelect, rows) => { + console.log('[MapTable2] handleOnSelectAll', isSelect, rows); + const ids = rows.map(r => r.id); + if (isSelect) { + this.setState({selected: ids}) + } + else { + this.setState({selected: []}) + } + } + + columnTable = () => { + + const editFormatter = (cell, row, rowIndex) => { + return ( +
+ this.goToFeature(row)}> + {/* {' '} + */} +
+ ) + } + + const column = [ + { + dataField: 'id', + text: 'Action', + formatter:editFormatter + },{ + dataField: 'employee_group', + text: 'Group Employee' + },{ + dataField: 'name', + text: 'Name' + },{ + dataField: 'phone_number', + text: 'Phone Number' + },{ + dataField: 'email', + text: 'Email' + },{ + dataField: 'address', + text: 'Address' + } + ]; + + return column + } + + showRouteSelected = async () => { + const { selected } = this.state; + console.log('showRouteSelected', selected); + + // await this.props.setMapState('isProcessing', true); + await this.props.setIsProcessing(true); + + this.props.removeLayerByName('routeLayer'); // clear previous route on map + + let vectorLayers = []; + let salesWhoHasRoute = []; + let polyline = null; + let route = null; + let salesId = null; + let startDate = moment().startOf('day').format("YYYY-MM-DD HH:mm"); + let endDate = moment().endOf('day').format("YYYY-MM-DD HH:mm"); + let rangeDate = [startDate, endDate]; + let extendedExtent = null; + + if (selected && selected.length > 0) { + + // checking each sales's route + for (let i=0; i < selected.length; i++) { + salesId = selected[i]; + let salesRoute = await getSalesRoutingApi(salesId, rangeDate); + console.log('salesRoute', salesRoute); + if (salesRoute && salesRoute.features && salesRoute.features.length > 0 && (salesRoute.features[0].geometry && salesRoute.features[0].geometry.coordinates)) { + salesWhoHasRoute.push(salesRoute.features[0]); // then push to salesWhoHasRoute + } + } + + // only show sales who has route + if (salesWhoHasRoute.length > 0) { + for (let i=0; i < salesWhoHasRoute.length; i++) { + polyline = salesWhoHasRoute[i].geometry; + route = new LineString(polyline.coordinates).transform('EPSG:4326', 'EPSG:3857'); + + let routeFeature = new Feature({ + type: 'route', + geometry: route, + geometry_name: salesWhoHasRoute[i].geometry_name, + id: salesWhoHasRoute[i].id, + properties: salesWhoHasRoute[i].properties, + routeColor: getRandomColor() + }); + let geoMarker = new Feature({ + type: 'geoMarker', + geometry: new Point(route.getCoordinateAt(0)), + id: salesWhoHasRoute[i].id, + properties: salesWhoHasRoute[i].properties + }); + let startMarker = new Feature({ + type: 'pinRouteStart', + geometry: new Point(route.getCoordinateAt(0)), + id: salesWhoHasRoute[i].id, + properties: salesWhoHasRoute[i].properties + }); + let endMarker = new Feature({ + type: 'pinRouteEnd', + geometry: new Point(route.getCoordinateAt(1)), + id: salesWhoHasRoute[i].id, + properties: salesWhoHasRoute[i].properties + }); + + let animating = false; + + // generate the route layer + let vectorLayer = new VectorLayer({ + name: 'routeLayer', + source_type: 'routeLayer', + source: new VectorSource({ + features: [routeFeature, geoMarker, startMarker, endMarker], + }), + style: (feature, resolution) => { + // hide geoMarker if animation is active + if (animating && feature.get('type') === 'geoMarker') { + return null; + } + // return ROUTE_MAP_STYLES[feature.get('type')]; + return ROUTE_MAP_STYLES(feature, resolution); + }, + }); + + vectorLayers.push(vectorLayer); + + // this.props.olmap.addLayer(vectorLayer); + // this.olmap.getView().fit(extent, { size: this.olmap.getSize(), duration: 500 }); + } + + + // then show to the map + if (vectorLayers.length > 0) { + for (let i=0; i < vectorLayers.length; i++) { + this.props.olmap.addLayer(vectorLayers[i]); + let extent_ = vectorLayers[i].getSource().getExtent(); + console.log('[MapTable2] extent vectorLayer', extent_); + // extent.extend(extent_); + this.props.olmap.getView().fit(extent_, { size: this.props.olmap.getSize(), duration: 500 }); + } + // console.log('all extent', new Extent()) + } + } + } + + await this.props.setIsProcessing(false); + } + + render() { + const { columns, data, resizableHeight} = this.state + const { mapTableColumns, mapTableData, mapTableTitle } = this.props; + + console.log('test table', mapTableData) + console.log('test table title', mapTableTitle) + + + if (!mapTableData) { + return ( +
+ No Data Found +
+ ) + } + + return ( + <> + {/* this.props.handleSearch */} + {mapTableTitle === "TOTAL KARYAWAN" && ( + + + + + {mapTableTitle} + + + this.props.handleSearch(e.target.value)} value={this.props.search} type="text" name="search" id="search" placeholder="Cari nama karyawan" /> + + + + + {mapTableData.length === 0 ? + + + + +
No Data Available
+ : + <> + + + + + + + + + + + + + {mapTableData.map((item, index) => { + return ( + + + + + + + + + ) + })} + +
#OrganisasiNama KaryawanJenis KelaminNo HPAlamat
{++index}{item.organization_name===null ? item.chairman_org || "-" : item.organization_name || "-"}{item.name}{item.gender == "P" ? "Perempuan" : "Laki-laki"}{item.phone_number}{item.address}
+ + + } +
+
+ ) + } + {mapTableTitle === "HADIR" && ( + + + + + {mapTableTitle} + + + this.props.handleSearch(e.target.value)} value={this.props.search} type="text" name="search" id="search" placeholder="Cari nama karyawan" /> + + + + + {mapTableData.length === 0 ? + + + + +
No Data Available
+ : + <> + + + + + + + + + + + {mapTableData.map((item, index) => { + return ( + + + + + + + + ) + })} + +
#Nama KaryawanJenis MasukJam Keluar
{++index}{item.employee_name}{item.clock_time ? moment(item.clock_time).format('DD-MM-YYYY HH:mm') : "-"}{item.clock_out_time ? moment(item.clock_out_time).format('DD-MM-YYYY HH:mm') : "-"}
+ + + } +
+
+ ) + } + {mapTableTitle === "TIDAK HADIR" && ( + + + + + {mapTableTitle} + + + this.props.handleSearch(e.target.value)} value={this.props.search} type="text" name="search" id="search" placeholder="Cari nama karyawan" /> + + + + + {mapTableData.length === 0 ? + + + + +
No Data Available
+ : + <> + + + + + + + + + + + + + {mapTableData.map((item, index) => { + return ( + + + + + + + + + ) + })} + +
#OrganisasiNama KaryawanJenis KelaminNo HPAlamat
{++index}{item.organization_name===null ? item.chairman_org || "-" : item.organization_name || "-"}{item.name}{item.gender == "P" ? "Perempuan" : "Laki-laki"}{item.phone_number}{item.address}
+ + + } +
+
+ ) + } + {mapTableTitle === "KARYAWAN TELAT" && ( + + + + + {mapTableTitle} + + + this.props.handleSearch(e.target.value)} value={this.props.search} type="text" name="search" id="search" placeholder="Cari nama karyawan" /> + + + + + {mapTableData.length === 0 ? + + + + +
No Data Available
+ : + <> + + + + + + + + + + + + + {mapTableData.map((item, index) => { + return ( + + + + + + + + + ) + })} + +
#OrganisasiNama KaryawanJenis KelaminNo HPAlamat
{++index}{item.organization_name===null ? item.chairman_org || "-" : item.organization_name || "-"}{item.name}{item.gender == "P" ? "Perempuan" : "Laki-laki"}{item.phone_number}{item.address}
+ + + } +
+
+ ) + } + {mapTableTitle === "KARYAWAN TANPA KETERANGAN" && ( + + + + + {mapTableTitle} + + + this.props.handleSearch(e.target.value)} value={this.props.search} type="text" name="search" id="search" placeholder="Cari nama karyawan" /> + + + + + {mapTableData.length === 0 ? + + + + +
No Data Available
+ : + <> + + + + + + + + + + + + + {mapTableData.map((item, index) => { + return ( + + + + + + + + + ) + })} + +
#OrganisasiNama KaryawanJenis KelaminNo HPAlamat
{++index}{item.organization_name===null ? item.chairman_org || "-" : item.organization_name || "-"}{item.name}{item.gender == "P" ? "Perempuan" : "Laki-laki"}{item.phone_number}{item.address}
+ + + } +
+
+ ) + } + {mapTableTitle === "PANIK BUTTON" && ( + + + + + {mapTableTitle} + + + this.props.handleSearch(e.target.value)} value={this.props.search} type="text" name="search" id="search" placeholder="Cari nama karyawan" /> + + + + + {mapTableData.length === 0 ? + + + + +
No Data Available
+ : + <> + + + + + + + + + + + + + {mapTableData.map((item, index) => { + return ( + + + + + + + + + ) + })} + +
#OrganisasiNama KaryawanJenis KelaminNo HPAlamat
{++index}{item.organization_name===null ? item.chairman_org || "-" : item.organization_name || "-"}{item.name}{item.gender == "P" ? "Perempuan" : "Laki-laki"}{item.phone_number}{item.address}
+ + + } +
+
+ ) + } + {/* */} + {/* { props => ( +
+ + +
+ +
+ { this.state.selected && this.state.selected.length > 0 && +
+ + this.showRouteSelected()} title="Show Route"> + + + +
+ } + + */} + {/*{ this.state.selected && this.state.selected.length > 0 && + +
+ + this.showRouteSelected()} title="Show Route"> + + + +
+ + }*/} + {/*
+ + +
+ +
+ +
+
+ )} */} + {/*
*/} + +) + + } +} + +class MapTable extends Component { + constructor(props) { + super(props); + this.state = { + tableHeight: 150, + resizableWidth: "100%", + resizableHeight: 300, + maximizeTitle: "maximize", + search: '', + } + } + + componentDidMount() { + // console.log('MapTable resTableData', this.props.resTableData); + } + + componentDidUpdate (prevProps, prevState) { + // if (prevState.search !== this.state.search) { + // if (this.state.search) { + // requestTableDailyInfoApi() + // } + // } + } + + handleDelete = () => { + + } + + handleEdit = () => { + + } + + showRoute = async (item) => { + console.log('[MapTable2] showRoute', item); + + let salesId = item.id; + let startDate = moment().startOf('day').format("YYYY-MM-DD HH:mm"); + let endDate = moment().endOf('day').format("YYYY-MM-DD HH:mm"); + console.log('startDate', startDate); + console.log('endDate', endDate); + let rangeDate = [startDate, endDate]; + + let route = await getSalesRoutingApi(salesId, rangeDate); + if (route) { + this.props.showRoute(route); + } + + } + + setMapTableWindow = (type) => { + console.log('setMapTableWindow', type); + if (type === 'min') { + this.setState({resizableHeight: 55, tableHeight: 150}); + } + else if (type === "max") { + if (this.state.resizableHeight === 625) { + // restore (back to default) + this.setState({resizableHeight: 300, tableHeight: 150, maximizeTitle: "maximize" }); + } + else { + // maximize + this.setState({resizableHeight: 625, tableHeight: 450, maximizeTitle: "restore" }); + } + + } + } + + handleSearch = (e) => { + console.log('handleSearch MapTable2', e.target.value); + this.setState({search: e.target.value}); + } + + render() { + return ( + { + + this.setState({ + resizableWidth: "100%", // to keep always full width + resizableHeight: this.state.resizableHeight + d.height + }); + + // if drag the table header + if (e.screenY < 100) { + this.setState({tableHeight: 450}) + } + else if (e.screenY < 150) { + this.setState({tableHeight: 400}) + } + else if (e.screenY < 200) { + this.setState({tableHeight: 350}) + } + else if (e.screenY < 250) { + this.setState({tableHeight: 300}) + } + else if (e.screenY < 300) { + this.setState({tableHeight: 250}) + } + else if (e.screenY < 400) { + this.setState({tableHeight: 200}) + } + else if (e.screenY < 600) { + this.setState({tableHeight: 150}) // default + } + else if (e.screenY >= 600) { + // this.props.toggleMapTable(); // close the MapTable + this.setMapTableWindow('min'); + } + }} + > +
+
+ this.setMapTableWindow("min")}> + + + this.setMapTableWindow("max")}> + + + this.props.toggleMapTable()}> + + +
+ {/* */} + + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + refreshMapTable={this.props.refreshMapTable} + tableHeight={this.state.tableHeight} + removeLayerByName={(layerName) => this.props.removeLayerByName(layerName)} + setIsProcessing={(data) => this.props.setIsProcessing(data)} + onShowSizeChange={this.props.onShowSizeChange} + onPagination={this.props.onPagination} + currentPage={this.props.currentPage} + rowsPerPage={this.props.rowsPerPage} + total={this.props.total} + handleSearch={this.props.handleSearch} + search={this.props.search} + /> +
+
+ ) + } +} + +export default MapTable; \ No newline at end of file diff --git a/src/components/MapTable2/MapTable_backup.js b/src/components/MapTable2/MapTable_backup.js new file mode 100644 index 0000000..7a4b005 --- /dev/null +++ b/src/components/MapTable2/MapTable_backup.js @@ -0,0 +1,643 @@ +import React, { Component, Fragment } from 'react'; +import Draggable, { DraggableCore } from "react-draggable"; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +// import { Rnd } from 'react-rnd'; +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import cellEditFactory from 'react-bootstrap-table2-editor'; +import { Icon, InlineIcon } from '@iconify/react'; +import createOutline from '@iconify/icons-ion/create-outline'; +import earthIcon from '@iconify/icons-ion/earth'; +import archiveOutline from '@iconify/icons-ion/archive-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import { Col, Row } from 'reactstrap'; +import { AppSwitch } from '@coreui/react'; +import './MapTable.css'; +import '../../assets/css/customscroll.css' +import EditTableColumn from '../EditTableColumn'; +import { getTableColumns } from '../../const/GeoserverFunc.js'; +import { API_GET_COLUMN_TABLE } from '../../const/ApiConst.js'; +import { findWhere, without } from 'underscore'; + +// For Select feature then go to map +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Vector as VectorSource } from 'ol/source'; +import { fromLonLat, transformExtent } from 'ol/proj'; + +const { SearchBar } = Search; +const { ExportCSVButton } = CSVExport; +/*const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const products = [ +{ + "id": 0, + "name": "Item name 0", + "price": 2100 +}, +{ + "id": 1, + "name": "Item name 1", + "price": 2101 +}, +{ + "id": 2, + "name": "Item name 2", + "price": 2102 +}, +{ + "id": 3, + "name": "Item name 3", + "price": 2103 +}];*/ + +/*const TableContent = () => { + let tableData = this.props.resTableData; + if (tableData !== null) { + return ( + + { + props => ( +
+

Input something at below input field:

+ +
+ +
+ ) + } +
+ ) + } +}*/ + +// const selectRow = { +// mode: 'radio', +// clickToSelect: true, +// clickToEdit: true // Click to edit cell also +// }; + +class TableContent extends Component { + + constructor(props) { + super(props) + this.state = { + fid: 'fid', + columns: null, + data: null, + resTableData: null, + selectRow: { + mode: 'radio', + clickToSelect: true, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + clickToEdit: false, + bgColor: '#e4e5e6', + }, + editCellMode: false, + editTableColumnVisible: false, + selectedRowData: null, + editedColumnsData: null, + // For Select Feature + mapProjection: this.props.olmap.getView().getProjection(), + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + } + } + this.toggleEditCell = this.toggleEditCell.bind(this); + this.mapTableInit = this.mapTableInit.bind(this); + this.activateCellEdit = this.activateCellEdit.bind(this); + } + + componentDidMount() { + console.log('this.state', this.state); + this.mapTableInit(); + } + + componentDidUpdate(prevProps, prevState) { + // if (this.state.editCellMode) { + // this.setState({ }) + // } + } + + mapTableInit() { + let columns = []; + let data = []; + let resTableData = this.props.resTableData; + console.log('resTableData', resTableData); + let features = this.props.resTableData.features; + let newProperties = {}; + let countPropKey = 0; + let fidPosIdx = 0; + + /*the data + { + type: + id: + geometry: + geometry_name: + properties: { + gid: + nama: + .... + } + } + I want something like this: + { + type: + id: + geometry: + geometry_name: + properties: { + fid: id -> append the id to the properties, but hide on table, because table only show the properties + gid: + nama: + .... + } + } + + */ + + for (let i=0; i < features.length; i++) { + features[i].properties.fid = features[i].id; // append the key value at the end of object + // Object.assign({fid: features[i].id}, features[i].properties); + // {'fid': features[i].id, ...features[i].properties}; + + /*for(let key in features[i].properties) { + if (countPropKey == fidPosIdx) { + newProperties.fid = features[i].id; + } + newProperties[key] = features[i].properties[key]; + countPropKey++; + } + console.log('newProperties', newProperties); + data.push(newProperties);*/ + + data.push(features[i].properties); + } + + for (let key in features[0].properties) { + columns.push({ + dataField: key, + text: key, + sort: true + }); + } + + + console.log('features', features); + console.log('columns', columns); + console.log('data', data); + + this.setState({ + columns: columns, + data: data, + resTableData: resTableData + }) + } + + toggleEditCell() { + let { editCellMode } = this.state; + if (!editCellMode) { + this.setState({ + editCellMode: true, + selectRow: { + mode: 'radio', + clickToSelect: false, + clickToEdit: true, + bgColor: '#e4e5e6' + } + }); + } + else { + this.setState({ + editCellMode: false, + selectRow: { + mode: 'radio', + clickToSelect: true, + clickToEdit: false, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + bgColor: '#e4e5e6' + } + }); + } + } + + activateCellEdit() { + return cellEditFactory({ mode: 'click' }); + } + + handleOnSelect = (row, isSelect, rowIndex, e) => { + console.log('handleOnSelect row, isSelect, rowIndex, e', row, isSelect, rowIndex, e); + const { resTableData } = this.state; + let features = resTableData.features; + // let features = + // console.log('handleOnSelect', row, isSelect, rowIndex, e); + // console.log('resTableData', this.state.resTableData); + // let selectedRow = findWhere(features, {properties: row}); + let selectedRow = findWhere(features, {id: row.fid}); + // console.log('handleOnSelect', selectedRow); + // this.setState({selectedRowData: selectedRow}, () => this.selectFeature(this.state.selectedRowData)); + this.setState({selectedRowData: selectedRow}); + } + + + /*getTableColumns = async (tableName) => { + // let result = null; + let obj = { + tableName: tableName + } + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(obj) + } + + try { + const result = await fetch(API_GET_COLUMN_TABLE, param).then(response => response.json()).then(res => res) + console.log('result getTableColumns', result); + if(result){ + // console.log('after save',result); + // result = result; + return result; + } else { + // result = result; + return result; + } + } catch(err) { + return err.message; + // this.setState({modalAdd: false, alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + }*/ + + editTableColumn = async () => { + let reqTableColumns = await getTableColumns(this.props.mapTableTitle); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({editedColumnsData: reqTableColumns.result}, () => this.toggleEditTableColumn()); + } + else { + alert(reqTableColumns.result); + return; + } + } + + toggleEditTableColumn = () => { + this.setState({editTableColumnVisible: !this.state.editTableColumnVisible}) + } + + // When user select the row of table (creating vector layer named chosenLayer) + selectFeature = (selectedPopupData) => { + console.log('SearchFeatures selectFeature', selectedPopupData); + this.removeChosenLayer(); + const select = new Select(); + this.props.olmap.addInteraction(select); + let geomToDisplay = null; + + if (selectedPopupData.geometry === null) { + alert('Could not find the geometry'); + return; + } + + let geomType = selectedPopupData.geometry.type; + let coords = selectedPopupData.geometry.coordinates; + let { mapProjection } = this.state; + if (geomType === "Point") { + geomToDisplay = new Point(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPoint") { + geomToDisplay = new MultiPoint(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "LineString") { + geomToDisplay = new LineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiLineString") { + geomToDisplay = new MultiLineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "Polygon") { + geomToDisplay = new Polygon(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPolygon") { + geomToDisplay = new MultiPolygon(coords).transform('EPSG:4326', mapProjection); + } + + let styleSelected = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + // color: '#2b2d80', + color: '#1e00ff', + width: 3 + }), + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#1e00ff', + width: 3 + }) + }) + }); + + // let polygon = new MultiPolygon(country_indonesia.geometry.coordinates).transform('EPSG:4326', 'EPSG:3857') + + let chosenFeature = new Feature({ + geometry: geomToDisplay + }) + + let chosenLayer = new VectorLayer({ + name: 'ChosenLayer', + source: new VectorSource({ + features: [chosenFeature] + }), + style: styleSelected, + type: 'vector' + }); + + this.props.olmap.addLayer(chosenLayer); + } + + // removing existing ChosenLayer + removeChosenLayer = () => { + let layersToRemove = []; + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + // console.log('removeChosenLayer', layer); + layersToRemove.push(layer); + } + }); + + if (layersToRemove.length > 0) { + for(let i=0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + } + } + + // When user click on search list of feature, then go to feature + goToFeature = () => { + let selectedFeature = this.state.selectedRowData; + + this.selectFeature(selectedFeature); + + // get chosenLayer -> get the chosenFeature -> get geometry -> get extent -> fit to map + const { fitOption } = this.state; + const proj = "EPSG:3857"; + let chosenLayer = null; + let chosenFeatures = null; + let featureExtent = null; + + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + return; + } + }) + + if (chosenLayer === null) { + alert('Could not go to feature because there is no geometry to go'); + // this.createGeomOnFeature(selectedFeature); + return; + } + + chosenFeatures = chosenLayer.getSource().getFeatures(); + console.log('SearchFeatures chosenFeatures', chosenFeatures); + featureExtent = chosenFeatures[0].getGeometry().getExtent() + + this.props.olmap.getView().fit(new transformExtent(featureExtent, proj, this.props.olmap.getView().getProjection()), fitOption); + + this.props.setPopupDataTemp(selectedFeature); + this.props.openPopupRight(); + this.props.toggleMapTable(); + } + + /*createGeomOnFeature = (selectedFeature) => { + let confirmation = window.confirm('Create the geometry anyway?'); + if (!confirmation) { + return; + } + this.props.toggleMapTable(); + }*/ + + + render() { + const { columns, data, selectRow, editCellMode, editedColumnsData, fid } = this.state; + // console.log('TableContent resTableData', this.props.resTableData); + // console.log('this.state', this.state); + if (columns !== null ) { + // console.log('columns is not null', columns); + return ( +
+ + { + props => ( +
+ + +
+ +
+ + + {/*
+ + +
*/} + +
+ + { this.state.selectedRowData ? +
+ + + View on Map + +
+ : null + } +
+ Edit Table
+ { editCellMode ? + + : + } +
+ + {/*
+ + + + + Export CSV + +
*/} + +
+ + + + + + this.editTableColumn()}> Edit Table Column + {/* + Export CSV + */} + + Export CSV + + + +
+
+
+ +
+ {/*
*/} +
+ {/**/} + + {editCellMode ? + + : + + } + +
+
+ ) + } +
+ + {this.state.editTableColumnVisible && + this.toggleEditTableColumn()} + modalTitle={`Edit ${this.props.mapTableTitle} attribute`} + editedColumnsData={editedColumnsData} + /> + } +
+ ) + } + else { + return null; + } + + } +} + +class MapTable extends Component { + constructor(props) { + super(props); + } + + componentDidMount() { + // console.log('MapTable resTableData', this.props.resTableData); + } + + render() { + return ( +
+ + {this.props.mapTableTitle} + +
+ {/*
+
*/} + { + this.props.resTableData !== undefined ? + this.props.resTableData !== null ? + this.props.resTableData.features.length > 0 ? + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + /> + : 'Table Data is empty' + : 'Table Data is null' + : 'Table Data is undefined' + } + {/*
+
*/} +
+
+ {/* + + */} +
+
+ ) + } +} + +export default MapTable; \ No newline at end of file diff --git a/src/components/MapTable2/MapTable_backup2.js b/src/components/MapTable2/MapTable_backup2.js new file mode 100644 index 0000000..68fdfab --- /dev/null +++ b/src/components/MapTable2/MapTable_backup2.js @@ -0,0 +1,949 @@ +import React, { Component, Fragment } from 'react'; +import Draggable, { DraggableCore } from "react-draggable"; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button, +UncontrolledTooltip, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +// import { Rnd } from 'react-rnd'; +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import cellEditFactory from 'react-bootstrap-table2-editor'; +import { Icon, InlineIcon } from '@iconify/react'; +import createOutline from '@iconify/icons-ion/create-outline'; +import earthIcon from '@iconify/icons-ion/earth'; +import archiveOutline from '@iconify/icons-ion/archive-outline'; +import saveOutline from '@iconify/icons-ion/save-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import { Col, Row } from 'reactstrap'; +import { AppSwitch } from '@coreui/react'; +import './MapTable.css'; +import '../../assets/css/customscroll.css' +import EditTableColumn from '../EditTableColumn'; +import { getTableColumns, updateFeature } from '../../const/GeoserverFunc.js'; +import { API_GET_COLUMN_TABLE } from '../../const/ApiConst.js'; +import { findWhere, without } from 'underscore'; + +// For Select feature then go to map +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Vector as VectorSource } from 'ol/source'; +import { fromLonLat, transformExtent } from 'ol/proj'; +import SweetAlert from 'react-bootstrap-sweetalert'; + +const { SearchBar } = Search; +const { ExportCSVButton } = CSVExport; +/*const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const products = [ +{ + "id": 0, + "name": "Item name 0", + "price": 2100 +}, +{ + "id": 1, + "name": "Item name 1", + "price": 2101 +}, +{ + "id": 2, + "name": "Item name 2", + "price": 2102 +}, +{ + "id": 3, + "name": "Item name 3", + "price": 2103 +}];*/ + +/*const TableContent = () => { + let tableData = this.props.resTableData; + if (tableData !== null) { + return ( + + { + props => ( +
+

Input something at below input field:

+ +
+ +
+ ) + } +
+ ) + } +}*/ + +// const selectRow = { +// mode: 'radio', +// clickToSelect: true, +// clickToEdit: true // Click to edit cell also +// }; + +class TableContent extends Component { + + constructor(props) { + super(props) + this.state = { + fid: 'fid', + columns: null, + originalData: null, + data: null, + resTableData: null, + selectRow: { + mode: 'radio', + clickToSelect: true, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + clickToEdit: false, + bgColor: '#e4e5e6', + }, + editCellMode: false, + editTableColumnVisible: false, + selectedRowData: null, + editedColumnsData: null, + // For Select Feature + mapProjection: this.props.olmap.getView().getProjection(), + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + }, + selectedRows: [], + updatedData: [], + alertConfirm: false, + alertConfirmBtnText: '', // Delete, Update + alertConfirmType: '', // danger, success, warning, info + alertConfirmMsg: '', // Under are you sure message: ... ? + confirmParam: '', + alertResponse: false, + alertResponseMsg: '', + successAlert: false, + dangerAlert: false, + saveMode: false + } + this.toggleEditCell = this.toggleEditCell.bind(this); + // this.mapTableInit = this.mapTableInit.bind(this); + this.activateCellEdit = this.activateCellEdit.bind(this); + } + + componentDidMount() { + console.log('this.state', this.state); + this.mapTableInit(); + } + + componentDidUpdate(prevProps, prevState) { + // if (this.state.editCellMode) { + // this.setState({ }) + // } + } + + mapTableInit = async () => { + let columns = []; + let data = []; + // let resTableData = this.props.resTableData; + let { resTableData, mapTableTitle } = this.props; + console.log('resTableData', resTableData); + let features = this.props.resTableData.features; + let newProperties = {}; + let countPropKey = 0; + let fidPosIdx = 0; + + if (features.length > 0) { + for (let i=0; i < features.length; i++) { + features[i].properties.fid = features[i].id; // append the key value at the end of object + // Object.assign({fid: features[i].id}, features[i].properties); + // {'fid': features[i].id, ...features[i].properties}; + + /*for(let key in features[i].properties) { + if (countPropKey == fidPosIdx) { + newProperties.fid = features[i].id; + } + newProperties[key] = features[i].properties[key]; + countPropKey++; + } + console.log('newProperties', newProperties); + data.push(newProperties);*/ + + data.push(features[i].properties); + } + + for (let key in features[0].properties) { + if (key == 'fid') { + columns.push({ + dataField: key, + text: key, + sort: true, + hidden: true + }); + } + else { + columns.push({ + dataField: key, + text: key, + sort: true + }); + } + } + } + else { + let reqTableColumns = await getTableColumns(mapTableTitle); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + if (reqTableColumns.result !== null) { + if (reqTableColumns.result.length > 0) { + for(let i=0; i < reqTableColumns.result.length; i++) { + let column_name = reqTableColumns.result[i].column_name; + columns.push({ + dataField: column_name, + text: column_name, + sort: true + }); + } + } + } + } + } + + /*the data + { + type: + id: + geometry: + geometry_name: + properties: { + gid: + nama: + .... + } + } + I want something like this: + { + type: + id: + geometry: + geometry_name: + properties: { + fid: id -> append the id to the properties, but hide on table, because table only show the properties + gid: + nama: + .... + } + } + + */ + + console.log('features', features); + console.log('columns', columns); + console.log('data', data); + + this.setState({ + columns: columns, + data: data, + originalData: data, + resTableData: resTableData + }) + } + + toggleEditCell() { + const { editCellMode } = this.state; + if (!editCellMode) { + this.setState({ + editCellMode: true, + selectRow: { + mode: 'checkbox', + clickToSelect: false, + clickToEdit: true, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e) + } + }); + } + else { + this.setState({ + editCellMode: false, + selectRow: { + mode: 'radio', + clickToSelect: true, + clickToEdit: false, + onSelect: (row, isSelect, rowIndex, e) => this.handleOnSelect(row, isSelect, rowIndex, e), + bgColor: '#e4e5e6' + } + }); + } + } + + activateCellEdit() { + return cellEditFactory({ mode: 'click' }); + } + + handleOnSelect = (row, isSelect, rowIndex, e) => { + console.log('handleOnSelect row, isSelect, rowIndex, e', row, isSelect, rowIndex, e); + const { resTableData } = this.state; + let features = resTableData.features; + let selectedRow = findWhere(features, {id: row.fid}); + this.setState({selectedRowData: selectedRow}); + } + + onSelectRowTable = ({row, isSelect, e}) => { + const {selectedRows} = this.state + console.log({row, isSelect, e}) + console.log('typeof row',typeof(row)); + if(typeof(row) === "object"){ + if(isSelect){ + this.setState({selectedRows: [...selectedRows, row]}, () => { + console.log('onSelectRowTable selectedRows', this.state.selectedRows); + }) + } else { + const idx = selectedRows.indexOf(row) + selectedRows.splice(idx, 1) + this.setState({selectedRows}, () => { + console.log('onSelectRowTable selectedRows', this.state.selectedRows); + }) + } + } else { + console.log("type array") + this.setState({selectedRows: row}) + } + } + + onSelectAllRowTable = (isSelect, rows, e) => { + console.log('onSelectAllRowTable isSelect, rows, e', isSelect, rows, e); + const { selectedRows } = this.state; + + if (isSelect === false) { + // clear all this.state.selectedRows + this.setState({selectedRows: []}, () => { + console.log('onSelectAllRowTable selectedRows', this.state.selectedRows); + }); + } + else if (isSelect === true) { + let row = rows.map((item, index) => { + return item; + }); + console.log('row', row); + this.setState({selectedRows: row}, () => { + console.log('onSelectAllRowTable selectedRows', this.state.selectedRows); + }); + } + } + + + /*getTableColumns = async (tableName) => { + // let result = null; + let obj = { + tableName: tableName + } + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(obj) + } + + try { + const result = await fetch(API_GET_COLUMN_TABLE, param).then(response => response.json()).then(res => res) + console.log('result getTableColumns', result); + if(result){ + // console.log('after save',result); + // result = result; + return result; + } else { + // result = result; + return result; + } + } catch(err) { + return err.message; + // this.setState({modalAdd: false, alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + }*/ + + editTableColumn = async () => { + let reqTableColumns = await getTableColumns(this.props.mapTableTitle); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({editedColumnsData: reqTableColumns.result}, () => this.toggleEditTableColumn()); + } + else { + alert(reqTableColumns.result); + return; + } + } + + toggleEditTableColumn = () => { + this.setState({editTableColumnVisible: !this.state.editTableColumnVisible}) + } + + // When user select the row of table (creating vector layer named chosenLayer) + selectFeature = (selectedPopupData) => { + console.log('SearchFeatures selectFeature', selectedPopupData); + this.removeChosenLayer(); + const select = new Select(); + this.props.olmap.addInteraction(select); + let geomToDisplay = null; + + if (selectedPopupData.geometry === null) { + alert('Could not find the geometry'); + return; + } + + let geomType = selectedPopupData.geometry.type; + let coords = selectedPopupData.geometry.coordinates; + let { mapProjection } = this.state; + if (geomType === "Point") { + geomToDisplay = new Point(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPoint") { + geomToDisplay = new MultiPoint(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "LineString") { + geomToDisplay = new LineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiLineString") { + geomToDisplay = new MultiLineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "Polygon") { + geomToDisplay = new Polygon(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPolygon") { + geomToDisplay = new MultiPolygon(coords).transform('EPSG:4326', mapProjection); + } + + let styleSelected = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + // color: '#2b2d80', + color: '#1e00ff', + width: 3 + }), + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#1e00ff', + width: 3 + }) + }) + }); + + // let polygon = new MultiPolygon(country_indonesia.geometry.coordinates).transform('EPSG:4326', 'EPSG:3857') + + let chosenFeature = new Feature({ + geometry: geomToDisplay + }) + + let chosenLayer = new VectorLayer({ + name: 'ChosenLayer', + source: new VectorSource({ + features: [chosenFeature] + }), + style: styleSelected, + type: 'vector' + }); + + this.props.olmap.addLayer(chosenLayer); + } + + // removing existing ChosenLayer + removeChosenLayer = () => { + let layersToRemove = []; + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + // console.log('removeChosenLayer', layer); + layersToRemove.push(layer); + } + }); + + if (layersToRemove.length > 0) { + for(let i=0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + } + } + + // When user click on search list of feature, then go to feature + goToFeature = () => { + let selectedFeature = this.state.selectedRowData; + // if ("fid" in selectedFeature.properties) { + + // } + // let _selectedFeature = null; + if (selectedFeature.properties.hasOwnProperty("fid")) { + delete selectedFeature.properties["fid"]; + // console.log('_selectedFeature', _selectedFeature); + // console.log('selectedFeature', selectedFeature); + } + // console.log('selectedFeature outside', selectedFeature); + this.selectFeature(selectedFeature); + + // get chosenLayer -> get the chosenFeature -> get geometry -> get extent -> fit to map + const { fitOption } = this.state; + const proj = "EPSG:3857"; + let chosenLayer = null; + let chosenFeatures = null; + let featureExtent = null; + + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + return; + } + }) + + if (chosenLayer === null) { + alert('Could not go to feature because there is no geometry to go'); + // this.createGeomOnFeature(selectedFeature); + return; + } + + chosenFeatures = chosenLayer.getSource().getFeatures(); + console.log('SearchFeatures chosenFeatures', chosenFeatures); + featureExtent = chosenFeatures[0].getGeometry().getExtent() + + this.props.olmap.getView().fit(new transformExtent(featureExtent, proj, this.props.olmap.getView().getProjection()), fitOption); + + this.props.setPopupDataTemp(selectedFeature); + this.props.openPopupRight(); + this.props.toggleMapTable(); + } + + /*createGeomOnFeature = (selectedFeature) => { + let confirmation = window.confirm('Create the geometry anyway?'); + if (!confirmation) { + return; + } + this.props.toggleMapTable(); + }*/ + + updateFunction = async (oldValue, newValue, row, column) => { + console.log('------ updateFunction ------') + const { data, updatedData } = this.state; + // let newData = data; + let updatedDataArr = []; + /*console.log('data before', data); + console.log('updateFunction oldValue, newValue, row, column', oldValue, newValue, row, column); + + // let updatedRow = findWhere(data, {fid: row.fid}); + let updatedRow = findWhere(data, {fid: row.fid}); + console.log('updatedRow', updatedRow); + console.log('data after', data); + + this.setState({data: data, saveMode: true});*/ + + + // cek apakah row ada di dalam updatedData + if (updatedData.length > 0) { + let updatedRow = findWhere(updatedData, {fid: row.fid}); + console.log('updatedRow', updatedRow); + + // jika tidak ada, maka push row ke updatedData + if (updatedRow == undefined) { + this.setState({updatedData: [...updatedData, row], saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + // jika ada, maka replace dengan data yg diedit + else { + let idx = updatedData.indexOf(updatedRow) + // updatedData.splice(idx, 1); + console.log('idx', idx); + updatedData[idx] = updatedRow; + this.setState({updatedData: updatedData, saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + } + // jika ada, maka update row yg ada di dalam updatedData + else { + this.setState({updatedData: [...updatedData, row], saveMode: true}, () => { + console.log('updatedData', this.state.updatedData); + }); + } + + // console.log('updatedData afterrrr', this.state.updatedData); + } + + deleteFunction = async param => { + const {selectedRows} = this.state + console.log("row selected",selectedRows) + /*if(param === "delete") { + + const {columns} = this.props + const val = columns.reduce((obj, item) => (obj[item.dataField] = item.state, obj) ,{}); + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(selectedRows) + } + + try { + const result = await fetch(this.props.urlParamDelete, param).then(response => response.json()).then(res => res) + if(result.data){ + this.getDataTable(); + this.setState({alert: true, alertConfirm: false, messageAlert: result.code_message, successAlert: true, dangerAlert: false, ...val}) + } else { + this.setState({alert: true, alertConfirm: false, messageAlert: result.code_message, successAlert: false, dangerAlert: true, ...val}) + } + } catch { + this.setState({alert: true, messageAlert: "Opss! looks like something wrong", successAlert: false, dangerAlert: true}) + } + } else { + this.setState({alertConfirm: false}) + }*/ + + if (param === "delete") { + + } + } + + saveUpdate = () => { + this.setState({confirmParam: 'save_update'}, () => this.showConfirm(this.state.confirmParam)); + // this.showConfirm('save_update'); + } + + deleteSelected = () => { + // this.showConfirm('delete_selected'); + this.setState({confirmParam: 'delete_selected'}, () => this.showConfirm(this.state.confirmParam)); + } + + showConfirm = (param) => { + if (param === 'save_update') { + this.setState({alertConfirm: true, alertConfirmBtnText: 'Save', alertConfirmType: 'success', alertConfirmMsg: 'Save the updates'}); + } + else if (param === 'delete_selected') { + this.setState({alertConfirm: true, alertConfirmBtnText: 'Delete', alertConfirmType: 'danger', alertConfirmMsg: 'Delete the selected rows'}); + } + } + + confirmYes = async (confirmParam) => { + console.log(confirmParam); + const { data, updatedData } = this.state; + console.log('updatedData', updatedData); + const { mapTableTitle } = this.props; + let fid = null; + let properties = []; + let countSuccess = 0; + let totalUpdatedData = updatedData.length; + if (confirmParam === 'save_update') { + if (updatedData.length > 0) { + for (let i=0; i < updatedData.length; i++) { + fid = updatedData[i].fid; + if (updatedData[i].hasOwnProperty("fid")) { + delete updatedData[i].fid; + // properties.push(updatedData[i]); + let _prop = []; + for(let key in updatedData[i]) { + _prop.push({ + label: key, + value: updatedData[i][key] + }); + } + properties.push(_prop); + } + console.log('confirmYes mapTableTitle, fid, properties', mapTableTitle, fid, properties); + // let res = await updateFeature(mapTableTitle, fid, properties); + let response = await updateFeature(mapTableTitle, fid, properties[0]); + + if (response.success) { + countSuccess = countSuccess+1; + properties = []; + } + } + + if (countSuccess === totalUpdatedData) { + // alert('All edited values have been saved'); + this.confirmCancel(); + this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: "All edited values have been saved"}); + } + else { + // alert('Oops! Some edited values are not saved'); + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: "Oops! Some edited values are not saved"}); + } + } + } + else if (confirmParam == 'delete_selected') { + + } + } + + confirmCancel = () => { + this.setState({alertConfirm: false, alertConfirmBtnText: '', alertConfirmType: '', alertConfirmMsg: '', confirmParam: ''}); + } + + closeAlertResponse = () => { + this.setState({ + alertResponse: false, successAlert: false, dangerAlert: false, alertResponseMsg: '', + // saveMode: false, editCellMode: false, updatedData: [] + }); + } + + renderEditCellMode = () => { + const { editCellMode } = this.state; + return editCellMode ? + + : + } + + render() { + const { columns, data, selectRow, editCellMode, editedColumnsData, fid, selectedRows, saveMode, + alertResponse, alertResponseMsg, successAlert, dangerAlert, + alertConfirm, alertConfirmBtnText, alertConfirmType, alertConfirmMsg, confirmParam + } = this.state; + + if (columns !== null ) { + return ( +
+ + this.closeAlertResponse()}> + {alertResponseMsg} + + + this.confirmYes(confirmParam)} + onCancel={() => this.confirmCancel()} + focusCancelBtn + > + {alertConfirmMsg} + + + + + { + props => ( +
+ + +
+ +
+ + + {/*
+ + +
*/} + +
+ + { this.state.selectedRowData && !editCellMode ? +
+ + + View on Map + +
+ : null + } + + {saveMode && editCellMode && +
+ + + Save Changes + +
+ } + + {selectedRows.length > 0 && + +
+ + + Delete Selected Row + +
+ } + +
+ Edit Table
+ { + this.renderEditCellMode() + } +
+ + {/*
+ + + + + Export CSV + +
*/} + +
+ + + + + + this.editTableColumn()}> Edit Table Column + {/* + Export CSV + */} + + Export CSV + + + +
+
+
+ +
+ {/*
*/} +
+ {/**/} + + {editCellMode ? + this.updateFunction(oldValue, newValue, row, column) + }) } + /> + : + + } + +
+
+ ) + } +
+ + {this.state.editTableColumnVisible && + this.toggleEditTableColumn()} + modalTitle={`Edit ${this.props.mapTableTitle} attribute`} + editedColumnsData={editedColumnsData} + /> + } +
+ ) + } + else { + return null; + } + + } +} + +class MapTable extends Component { + constructor(props) { + super(props); + } + + componentDidMount() { + // console.log('MapTable resTableData', this.props.resTableData); + } + + render() { + return ( +
+ + {this.props.mapTableTitle} + +
+ {/*
+
*/} + { + this.props.resTableData !== undefined ? + this.props.resTableData !== null ? + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + /> + : 'Table Data is null' + : 'Table Data is undefined' + } + {/*
+
*/} +
+
+ {/* + + */} +
+
+ ) + } +} + +export default MapTable; \ No newline at end of file diff --git a/src/components/MapTable2/package.json b/src/components/MapTable2/package.json new file mode 100644 index 0000000..58f83ea --- /dev/null +++ b/src/components/MapTable2/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapTable", + "version": "0.0.0", + "private": true, + "main": "./MapTable.js" +} diff --git a/src/components/MapToolbar/MapToolbar.css b/src/components/MapToolbar/MapToolbar.css new file mode 100644 index 0000000..a00a4af --- /dev/null +++ b/src/components/MapToolbar/MapToolbar.css @@ -0,0 +1,180 @@ +:root { + --position-maptool: fixed; + --left-maptool: 13px; + --button-size: 16px; + --active-left-sidebar: 350px; +} + +.maptool-custom { + background-color: rgba(200, 199, 58, 0.65); +} + +.maptool_1 { + position: var(--position-maptool); + top: 60px; + /*top: 130px;*/ + left: var(--left-maptool); +} + +.maptool_2 { + position: var(--position-maptool); + top: 95px; + /*top: 165px;*/ + left: var(--left-maptool); +} + +.maptool_3 { + position: var(--position-maptool); + top: 130px; + /*top: 200px;*/ + left: var(--left-maptool); +} + +.maptool_4 { + position: var(--position-maptool); + top: 165px; + /*top: 235px;*/ + left: var(--left-maptool); +} + +.maptool_5 { + position: var(--position-maptool); + top: 200px; + left: var(--left-maptool); +} + +.maptool_6 { + position: var(--position-maptool); + top: 235px; + left: var(--left-maptool); +} + +.maptool_7 { + position: var(--position-maptool); + top: 270px; + left: var(--left-maptool); +} + +.maptool_8 { + position: var(--position-maptool); + top: 305px; + left: var(--left-maptool); +} + +.maptool_9 { + position: var(--position-maptool); + top: 340px; + left: var(--left-maptool); +} + +.maptool_10 { + position: var(--position-maptool); + top: 375px; + left: var(--left-maptool); +} + +/*#custom-mouse-position { + position: var(--position-maptool); + bottom: 10px !important; + left: var(--left-maptool); +}*/ + +.maptool_btm_1 { + position: var(--position-maptool); + bottom: 10px; + left: var(--left-maptool); +} + +.maptool_btm_2 { + position: var(--position-maptool); + bottom: 45px; + left: var(--left-maptool); +} + +.button-layer-menu { + background-color: #ffffff; + padding: 0px; + margin-top: -10px; + margin-left: 10px; +} + +.layer-tree-wrapper { + margin-top: 10px; + margin-left: -20px; + overflow: auto; + height: 60vh; +} + +.layer-tree-title-wrapper { + /*width: 130px;*/ + display: inline-block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 150px; +} + +.layer-tree-dropdown-wrapper { + /*width: 100%; */ + right: 0; + display: inline-block; + /*z-index: 300;*/ +} + +.layer-tree-badge { + margin-top: -25px; +} + +.ow-wrapper { + /*overflow-wrap: break-word;*/ + word-wrap: break-word; + /*max-width: 200px;*/ +} + +.ow { + overflow-wrap: break-word; + word-wrap: break-word; + max-width: 200px; +} + +.layer-button-group { + +} + +.active-side-left { + margin-left: 300px; +} + +/*.ol-tooltip { + position: relative; + background: rgba(0, 0, 0, 0.5); + border-radius: 4px; + color: white; + padding: 4px 8px; + opacity: 0.7; + white-space: nowrap; + font-size: 12px; + } +.ol-tooltip-measure { + opacity: 1; + font-weight: bold; +} +.ol-tooltip-static { + background-color: #ffcc33; + color: black; + border: 1px solid white; +} +.ol-tooltip-measure:before, +.ol-tooltip-static:before { + border-top: 6px solid rgba(0, 0, 0, 0.5); + border-right: 6px solid transparent; + border-left: 6px solid transparent; + content: ""; + position: absolute; + bottom: -6px; + margin-left: -7px; + left: 50%; +} +.ol-tooltip-static:before { + border-top-color: #ffcc33; +}*/ diff --git a/src/components/MapToolbar/MapToolbar.js b/src/components/MapToolbar/MapToolbar.js new file mode 100644 index 0000000..7d2ead5 --- /dev/null +++ b/src/components/MapToolbar/MapToolbar.js @@ -0,0 +1,2171 @@ +import React, { Component, Fragment, useState } from 'react'; +import { Drawer, Tooltip } from 'antd'; +import { + mappify, + SimpleButton, + AddWmsPanel, + LayerTree, + Panel +} from '@terrestris/react-geo'; +import { + Badge, Dropdown, UncontrolledDropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, + InputGroup, Input, InputGroupAddon, InputGroupText, Button, Collapse, Card, CardBody +} from 'reactstrap'; +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import MeasureButton from '@terrestris/react-geo/dist/Button/MeasureButton/MeasureButton'; +import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import MapTable2 from '../../components/MapTable2'; +import DrawingTool from '../../components/DrawingTool'; +import MapLayerStyles from '../../components/MapLayerStyles'; +import CreateNewLayer from '../../components/CreateNewLayer'; +import SearchFeatures from '../../components/SearchFeatures'; +import AddFeature from '../../components/AddFeature'; +import MapLegend from '../../components/MapLegend'; +import MeasureContainer from '../../components/MeasureContainer'; +import DailyInfo from '../../components/DailyInfo'; +import LayerTreeAdm from '../../components/LayerTreeAdm'; +// import PopupContainer from '../../components/PopupContainer'; +import { appConfig, wfsDispatcherUrl } from '../../const/MapConst.js'; +// import { API_LAYER_SEARCH_LABEL } from '../../const/ApiConst.js'; +import './MapToolbar.css'; +import { Icon, InlineIcon } from '@iconify/react'; +// import layersIcon from '@iconify/icons-simple-line-icons/layers'; +import layersIcon from '@iconify/icons-ion/layers'; +import mapIcon from '@iconify/icons-ion/map'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import searchPlus from '@iconify/icons-fe/search-plus'; +import searchMinus from '@iconify/icons-fe/search-minus'; +import mapLegend from '@iconify/icons-mdi/map-legend'; +import rulerIcon from '@iconify/icons-mdi/ruler'; +import filterIcon from '@iconify/icons-ion/filter'; +import pieIcon from '@iconify/icons-ion/pie-chart'; +import barIcon from '@iconify/icons-ion/bar-chart'; +import informationCircleOutline from '@iconify/icons-ion/information-circle-outline'; + +import axios from 'axios'; +import OlLayerGroup from 'ol/layer/Group'; +import OlLayerTile from 'ol/layer/Tile'; +import OlLayerImage from 'ol/layer/Image'; +import ZoomToExtent from 'ol/control/ZoomToExtent'; +import { fromLonLat, transformExtent } from 'ol/proj'; +import { Draw } from 'ol/interaction'; +import { LineString, Polygon } from 'ol/geom'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Vector as VectorSource } from 'ol/source'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { unByKey } from 'ol/Observable'; +import Overlay from 'ol/Overlay'; +import { WMS_CAPABILITIES_URL, legendPicUrl } from '../../const/MapConst.js'; +import { findWhere, flatten, without } from 'underscore'; +import { reqTableData, getGeomType, getTableColumns, getLayerColor, formatLength, formatArea, lightOrDark } from '../../const/GeoserverFunc.js'; +import QueryBuilder from '../QueryBuilder/QueryBuilder.js'; +import '../../assets/css/customscroll.css'; +import { requestTableDailyInfoApi } from '../../const/GeohrApiFunc.js'; + +const MappifiedLayerTree = mappify(LayerTree); + +/* +Toggle Visibility that must be closed or switched to its visibility: + +layer: drawerLayerVisible +popup: popupRightVisible +searchFeature: drawerSearchVisible +basemap: layerSwitcherVisible +legend: legendVisible +drawing: drawingToolVisible +layerStyle: drawerLayerStylesVisible +2: mapTableModalVisible + +*/ + +class MapToolbar extends Component { + + constructor(props) { + super(props); + this.state = { + isReady: false, + wmsLayers: [], + drawerDailyInfo: false, + drawerLayerVisible: true, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + layerSwitcherVisible: false, + layerPanelVisible: false, + mapTableModalVisible: false, + queryBuilderVisible: false, + mapTableName: '', + mapTableTitle: '', + mapTableTitleSrc: '', + mapTableColumns: null, + mapTableData: null, + selectedMapLayer: null, + resTableData: null, + drawingToolVisible: false, + layerNameDraw: '', + geomTypeDraw: '', + drawerLayerStylesVisible: false, + createNewLayerVisible: false, + searchInput: '', + allFeatures: [], + popupRightVisible: false, + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + }, + addFeatureVisible: false, + addFeatureColumns: null, + addFeatureValue: null, + legendVisible: false, + activeSideLeft: false, + DrawingUtil: null, + drawingLayer: null, + chosenLayer: null, + // searchLabelData: [], + btnDropMeasureVisible: false, + measureVisible: false, + measureName: '', + measureType: '', + measureValue: '', + measureDraw: null, + sketch: null, + helpTooltipElement: null, + helpTooltip: null, + measureTooltipElement: null, + measureTooltip: null, + continuePolygonMsg: 'Click to continue drawing the polygon', + continueLineMsg: 'Click to continue drawing the line', + searchDetailField:"name", + search:"", + totalPageBottom:0, + currentPage:1, + rowsPerPage: 10, + page:1, + table_type:"", + totalRecord:0, + } + this.getWMSLayers = this.getWMSLayers.bind(this); + this.toggleDrawerLayer = this.toggleDrawerLayer.bind(this); + this.toggleDrawerSearch = this.toggleDrawerSearch.bind(this); + this.toggleLayerSwitcher = this.toggleLayerSwitcher.bind(this); + // this.toggleLayerPanel = this.toggleLayerPanel.bind(this); + this.cancelWmsLayer = this.cancelWmsLayer.bind(this); + this.customTitleLayerTree = this.customTitleLayerTree.bind(this); + this.onRightClickLayerTree = this.onRightClickLayerTree.bind(this); + this.onDropLayerTree = this.onDropLayerTree.bind(this); + // this.onClickLayerMenu = this.onClickLayerMenu.bind(this); + this.toggleMapTable = this.toggleMapTable.bind(this); + this.toggleMapAnalyzeTool = this.toggleMapAnalyzeTool.bind(this); + // this.toggleDrawingTool = this.toggleDrawingTool.bind(this); + this.cancelDraw = this.cancelDraw.bind(this); + this.toggleLayerStyles = this.toggleLayerStyles.bind(this); + this.openLayerStyles = this.openLayerStyles.bind(this); + this.closeLayerStyles = this.closeLayerStyles.bind(this); + this.toggleCreateNewLayer = this.toggleCreateNewLayer.bind(this); + + this.measureLayer = new VectorLayer({ + name: 'measure_layer', + title: 'Measure Layer', + type: 'vector', + source: new VectorSource(), + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + // lineDash: [10, 10], + width: 3 + }), + image: new CircleStyle({ + radius: 7, + fill: new Fill({ + color: '#ffcc33' + }) + }) + }) + }); + } + + componentDidMount() { + this.setState({ isReady: true, activeSideLeft: true }); + } + + componentDidUpdate(prevProps, prevState) { + if (prevState.mapTableModalVisible !== this.state.mapTableModalVisible) { + if (!this.state.mapTableModalVisible) { + // removing last routeLayer and table data + this.props.removeLayerByName('routeLayer'); + this.setState({ mapTableData: null }); + } + } + } + + getLayerType = (item) => { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_geom_type; + } + else { + return "Undefined Geom Type"; + } + } + + getLayerColor = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log('getLayerColor', layer); + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_color; + } + else { + return "#ffffff"; + } + } + + getLayerTitle = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + let output = ''; + + if (matchLayerName.length > 0) { + if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + // return matchLayerName[0].layer_title; + output = matchLayerName[0].layer_title; + return output; + } + else { + output = matchLayerName[0].layer_name; + return output; + } + } + else { + return "No layer title provided"; + // output = matchLayerName[0].layer_name; + // return matchLayerName[0].layer_name; + } + } + + getLayerTitleTextColor = (layerColor) => { + let brightness = lightOrDark(layerColor); // 'light' or 'dark' + + if (brightness == 'light') { + return 'dark-text'; + } + else { + return 'light-text'; + } + } + + getLayerCount = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log(layer) + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + // if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + // return matchLayerName[0].layer_title; + // } + // else { + // return matchLayerName[0].layer_name; + // } + return matchLayerName[0].total_features; + + } + else { + return "0"; + } + } + + zoomIn = () => { + this.props.olmap.getView().animate({ + zoom: this.props.olmap.getView().getZoom() + 1, + duration: 500 + }); + } + + zoomOut = () => { + this.props.olmap.getView().animate({ + zoom: this.props.olmap.getView().getZoom() - 1, + duration: 500 + }); + } + + getWMSLayers() { + CapabilitiesUtil.parseWmsCapabilities(WMS_CAPABILITIES_URL) + .then(CapabilitiesUtil.getLayersFromWmsCapabilities) + .then(layers => { + console.log('wmsLayers', layers); + this.setState({ + wmsLayers: layers + }); + }) + .catch(() => alert('Could not parse capabilities document.')); + } + + /*toggleDrawerLayer () { + this.closeAllDrawer(); + // this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}); + // this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}, () => this.closeAllDrawer()); + // this.closeAllDrawer(); + const { drawerLayerVisible } = this.state; + if (drawerLayerVisible) { + this.setState({ drawerLayerVisible: false }, () => this.closeAllDrawer() ); + } + else { + this.setState({ drawerLayerVisible: true }, () => this.closeAllDrawer() ); + } + }*/ + + /*toggleDrawerLayer () { + this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }) + }*/ + + toggleDrawerLayer() { + let { drawerLayerVisible } = this.state; + if (drawerLayerVisible) { + this.setState({ drawerLayerVisible: false }, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({ drawerLayerVisible: true }, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }) + } + } + + /*toggleDrawerSearch () { + this.closeAllDrawer(); + const { drawerSearchVisible } = this.state; + console.log('drawerSearchVisible', drawerSearchVisible); + if (drawerSearchVisible) { + this.setState({drawerSearchVisible: false}, () => this.closeAllDrawer() ); + } + else { + this.setState({drawerSearchVisible: true}, () => { + this.getFeaturesOnMap(); + this.closeAllDrawer(); + }); + } + }*/ + + toggleDrawerSearch() { + const { drawerSearchVisible } = this.state; + + if (drawerSearchVisible) { + this.setState({ drawerSearchVisible: false }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + // drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }) + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({ drawerSearchVisible: true }, () => { + this.getFeaturesOnMap(); + // this.getLayerSearchLabel(); + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + // drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + } + + toggleMapAnalyzeTool() { + // this.setState({drawerAnalyzeToolVisible: !this.state.drawerAnalyzeToolVisible}); + const { drawerAnalyzeToolVisible } = this.state; + + if (drawerAnalyzeToolVisible) { + this.setState({ drawerAnalyzeToolVisible: false }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + // drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }) + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({ drawerAnalyzeToolVisible: true }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + // drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true, + drawerDailyInfo: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + } + + // toggle Base Map + toggleLayerSwitcher() { + this.setState({ layerSwitcherVisible: !this.state.layerSwitcherVisible }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + // layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleMapTable() { + this.setState({ mapTableModalVisible: !this.state.mapTableModalVisible }, () => { + this.setState({ + // drawerLayerVisible: false, + drawerLayerStylesVisible: false, + // mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + // activeSideLeft: false + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleCreateNewLayer() { + this.setState({ createNewLayerVisible: !this.state.createNewLayerVisible }); + this.deactiveMeasureMap(); + } + + toggleLayerStyles() { + this.setState({ drawerLayerStylesVisible: !this.state.drawerLayerStylesVisible }, () => { + this.setState({ + drawerLayerVisible: false, + // drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleAddFeatureVisible = () => { + this.setState({ addFeatureVisible: !this.state.addFeatureVisible }); + this.deactiveMeasureMap(); + } + + toggleLegend = () => { + this.setState({ legendVisible: !this.state.legendVisible }, () => { + if (this.state.drawerLayerVisible) { + this.setState({ + // drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + // legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + // activeSideLeft: false, + drawerDailyInfo: false + }); + } + else { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + // legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }); + } + + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleMeasureVisible = () => { + // this.setState({measureVisible: !this.state.measureVisible}); + this.setState({ measureVisible: !this.state.measureVisible }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + // measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + + if (!this.state.measureVisible) { + this.deactiveMeasureMap(); + } + }); + } + + toggleDrawerDailyInfo = () => { + // this.setState({measureVisible: !this.state.measureVisible}); + this.setState({ drawerDailyInfo: !this.state.drawerDailyInfo }, () => { + if (this.state.drawerLayerVisible) { + this.setState({ + // drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + // activeSideLeft: false, + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + } + else { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + } + }); + } + + closeAllDrawer = () => { + /* + Toggle Visibility that must be closed or switched to its visibility: + + layer: drawerLayerVisible + popup: popupRightVisible + searchFeature: drawerSearchVisible + basemap: layerSwitcherVisible + legend: legendVisible + drawing: drawingToolVisible + layerStyle: drawerLayerStylesVisible + mapTable: mapTableModalVisible + + Group layers: layer, layerStyle, mapTable (that shouldn't be closed each other) + Group popup: searchFeature, popup (that shouldn't be closed each other) => searchFeature below of popup + Legend + BaseMap + Drawing + */ + + const { drawerLayerVisible, drawerLayerStylesVisible, mapTableModalVisible, + drawerSearchVisible, popupRightVisible, + legendVisible, + layerSwitcherVisible, + drawingToolVisible + } = this.state; + + let visibleArr = [drawerLayerVisible, drawerLayerStylesVisible, mapTableModalVisible, + drawerSearchVisible, popupRightVisible, + legendVisible, + layerSwitcherVisible, + drawingToolVisible + ]; + + console.log('visibleArr', visibleArr); + + if (drawerLayerVisible) { + this.setState({ + drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (drawerLayerStylesVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: true, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (mapTableModalVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: true, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (drawerSearchVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: true, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (popupRightVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: true, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (legendVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: true, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (layerSwitcherVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: true, + drawingToolVisible: false + }) + } + + if (drawingToolVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: true + }) + } + + } + + // toggleLayerPanel(layer) { + // console.log('toggleLayerPanel', layer); + // this.setState({layerPanelVisible: !this.state.layerPanelVisible}); + // } + + cancelWmsLayer() { + console.log('cancel wms'); + this.setState({ wmsLayers: [] }); + } + + onClickLayerMenu = (layer) => { + // console.log('onclick layer menu', layer); + } + + customTitleLayerTree(layer) { + // console.log('customTitleLayerTree', layer.get('title')); + // console.log('customTitleLayerTree', layer.get('type')); + // return ( + //
{layer.get('name')}
+ // ) + + // if (layer instanceof OlLayerGroup) { + + // } + + // return ( + //
+ //
+ // {layer.get('name')} + //
+ // {/*
+ // + //
*/} + //
+ // + // + // + // + // + // {layer.get('name')} + // this.showTableData(layer)}> Show Table42 + // Remove Layer + // + // + //
+ //
+ // ) + + // if (layer.get('type') == 'layer') { + return ( +
+
+ {layer.get('title') ? layer.get('title') : layer.get('name')} +
+ {/*
+ +
*/} +
+ + {/*10*/} + {localStorage.getItem('u_group') !== 'kominfo' ? + layer.get('type') == 'layer' ? + {this.getLayerCount(layer)} + : null + : layer.get('type') == 'layer' ? + + : null + } + this.onClickLayerMenu(layer)}> + + + + +
+ {layer.get('title') ? layer.get('title') : layer.get('name')} +
+
+ { + layer.get('type') === 'layer' || layer instanceof OlLayerImage ? + // layer instanceof OlLayerTile || layer instanceof OlLayerImage ? + + + this.zoomToLayer(layer)}> Zoom To Layer + this.showTableData(layer)}> Show Data + {this.props.editGeometryVisible ? + Add Feature + : this.addFeature(layer)}> Add Feature + } + {/* this.addFeature(layer)}> Add Feature*/} + this.styleLayer(layer)}> Layer Styles + + : + null} + this.removeLayer(layer)}> Remove Layer +
+
+
+
+ ) + // } + // else { + // return null; + // } + } + + onRightClickLayerTree(layer) { + console.log('right click layer tree', layer); + return ( + + + Header + Action + Another Action + + Another Action + + + ) + } + + onDropLayerTree() { + console.log('dropped'); + } + + + + /*showTableData = (layer) => { + console.log('showTableData', layer); + this.setState({ + mapTableTitle: layer.get('name'), + mapTableTitleSrc: layer.getSource().params_.LAYERS + }, async () => { + // console.log('mapTableTitle', this.state.mapTableTitle); + // console.log('mapTableTitleSrc', this.state.mapTableTitleSrc) + // let req = this.requestTableData(this.state.mapTableTitleSrc); + let res = await reqTableData(this.state.mapTableTitle); + console.log('showTableData',res); + // if (res !== undefined) { + // this.setState({resTableData: res}); + // } + + if (res.success) { + this.setState({resTableData: res.result}, () => this.toggleMapTable()); + } + else { + alert(res.result); + return; + } + }); + }*/ + + showTableData = (layer) => { + this.setState({ + mapTableTitle: layer.get('title'), + mapTableName: layer.get('name') + // mapTableTitleSrc: layer.getSource().params_.LAYERS + }, () => this.toggleMapTable()); + } + + refreshMapTable = async () => { + const { mapTableTitle, mapTableName } = this.state; + let res = await reqTableData(mapTableName); + if (res.success) { + this.setState({ resTableData: res.result }); + } + else { + alert(res.result); + return; + } + } + + styleLayer = async (layer) => { + console.log('styling layer...', layer); + + let SLD_URL = layer.getSource().params_.SLD; + console.log('SLD_URL', SLD_URL); + + let resGetLayerColor = await getLayerColor(SLD_URL); + + console.log('styleLayer resGetLayerColor', resGetLayerColor); + + // this.toggleLayerStyles(); + + if (this.state.drawerLayerStylesVisible) { + this.closeLayerStyles(); + this.openLayerStyles(); + } + else { + this.openLayerStyles(); + } + /* + axios.get(SLD_URL) + .then(res => { + console.log('axios get SLD', res); + let text = res.data; + let parser, xmlDoc; + // let self = this; + // parseString(res.data, function (err, result) { + // console.log(result); + // self.events = result; + // }); + if (window.DOMParser) { + // code for modern browsers + parser = new DOMParser(); + xmlDoc = parser.parseFromString(text,"text/xml"); + + console.log('xmlDoc', xmlDoc); + } + // else { + // // code for old IE browsers + // xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + // xmlDoc.async = false; + // xmlDoc.loadXML(text); + // console.log('xmlDoc', xmlDoc); + // } + }) + .catch(err => { + console.log('axios get SLD err', err); + }); + + if (this.state.drawerLayerStylesVisible) { + this.closeLayerStyles(); + this.openLayerStyles(); + } + else { + this.openLayerStyles(); + }*/ + } + + openLayerStyles() { + this.setState({ drawerLayerStylesVisible: true }); + } + + closeLayerStyles() { + this.setState({ drawerLayerStylesVisible: false }); + } + + addFeature = async (layer) => { + console.log('adding feature on layer...', layer); + // console.log(layer.get('name')); + + let geomType = ''; + // request Geom Type from API + let reqGeomType = await getGeomType(layer.get('name')); + console.log('addFeature reqGeomType', reqGeomType) + if (reqGeomType.success) { + geomType = reqGeomType.result; + } + else { + geomType = ''; + alert(reqGeomType.result); + return; + } + + // request the table columns from API + // let featureColumns = await getTableColumns(layer.get('name')); + // console.log('addFeature featureColumns', featureColumns); + + let reqTableColumns = await getTableColumns(layer.get('name')); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({ addFeatureColumns: reqTableColumns.result }); + } + else { + alert(reqTableColumns.result); + // return; + } + + + this.setState({ + drawingToolVisible: true, + layerNameDraw: layer.get('name'), + geomTypeDraw: geomType, + // addFeatureColumns: reqTableColumns.result + }); + this.props.toggleActiveStateAddGeometry(); + + } + + cancelDraw() { + this.setState({ + drawingToolVisible: false, + layerNameDraw: '', + geomTypeDraw: '' + }); + this.props.toggleActiveStateAddGeometry(); + } + + removeLayer = (layer) => { + console.log('removing layer', layer); + let confirmation = window.confirm('Are you sure you want to remove this layer?'); + if (!confirmation) { + return; + } + this.props.olmap.removeLayer(layer); + } + + /*zoomToLayer = (layer) => { + console.log('zoomToLayer', layer); + let layerExtent = null; + let newLayerExtent = null; + let transformLayerExtent = null; + let fitOption = { + size: this.props.olmap.getSize(), + duration: 500 + } + // layer.getSource().getExtent(); + // layer.getExtent(); + // layer.getSource().getExtent(); + if (layer instanceof OlLayerImage) { + // console.log(layer.getSource().image_.extent); + layerExtent = layer.getSource().image_.extent; + transformLayerExtent = new transformExtent(layerExtent, 'EPSG:4326', this.props.olmap.getView().getProjection()) + // transformLayerExtent = new transformExtent(layerExtent, this.props.olmap.getView().getProjection(), 'EPSG:4326') + console.log('layerExtent', layerExtent); + console.log('transformLayerExtent', transformLayerExtent); + newLayerExtent = [ + layerExtent[0], layerExtent[2], layerExtent[1], layerExtent[3] + ]; + console.log('newLayerExtent', newLayerExtent); + + // this.props.olmap.getView().fit(new transformExtent(layerExtent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + this.props.olmap.getView().fit(layerExtent, fitOption); + + // new ZoomToExtent() + } + else if (layer instanceof OlLayerTile) { + layerExtent = layer.getSource().tmpExtent_; + // console.log(layer.getSource()); + this.props.olmap.getView().fit(layerExtent, fitOption); + } + // console.log(layer.getSource().getExtent()); + }*/ + + zoomToLayer = (layer) => { + console.log('zoomToLayer getSource', layer.getSource()); + // let layerName = layer.getSource().params_.LAYERS; + let layerName = layer.get('name'); + console.log('layerName', layerName); + const { fitOption } = this.state; + + // if (layer instanceof OlLayerImage) { + // // layerName = layer.getSource(); + + // } + // else if (layer instanceof OlLayerTile) { + + // } + CapabilitiesUtil.parseWmsCapabilities(WMS_CAPABILITIES_URL).then(response => { + console.log('capabilities response', response); + if (response !== undefined) { + let layer = findWhere(response.Capability.Layer.Layer, { "Name": layerName }); + console.log('layer', layer); + + if (layer === undefined) { + alert('Layer ' + layerName + ' is not found'); + return; + } + + let proj = "CRS:84"; + let bbox = findWhere(layer.BoundingBox, { "crs": proj }); + let extent = bbox.extent; + console.log('layer', layer); + console.log('zoomToLayer extent', extent); + // map.getView().fit(new transformExtent(layerExtent, 'EPSG:4326', map.getView().getProjection()), { size: map.getSize() }); + + // console.log('layer', layer); + // this.props.olmap.getView().fit(extent, fitOption); + // console.log('getProjection', this.props.olmap.getView().getProjection()); + // var boundingExtent = ol.extent.boundingExtent(coordinates); + // boundingExtent = ol.proj.transformExtent(boundingExtent, ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857')); + + /*// other option + extent = new transformExtent(extent, "EPSG:4326", "EPSG:3857"); + console.log('extent now', extent); + this.props.olmap.getView().fit(extent, fitOption); */ + + this.props.olmap.getView().fit(new transformExtent(extent, proj, this.props.olmap.getView().getProjection()), fitOption); + + } + }) + } + + onDragEndLayerTree = (callback) => { + console.log('onDragEndLayerTree', callback); + } + + rebuildTreeNodes = (evt) => { + console.log('rebuildTreeNodes', evt); + } + + // to be populated on search feature menu + getFeaturesOnMap = async () => { + let layerSource = ''; + let layerTypeName = ''; + let layerSourceArr = []; + let layerTypeNameArr = []; + let layerNameArr = []; + let featureCollections = []; + let allFeatures = []; + this.props.olmap.getLayers().forEach((layer, i) => { + // console.log('layer', layer); + // console.log('layer type', layer.get('type')); + if (layer.get('type' !== 'base')) { + if (layer.get('type') !== 'vector') { + if (layer.get('type') !== 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + // get the features + layerSource = sublayer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(sublayer.get('name')); + } + }); + } + else { + if (layer.getVisible()) { + // get the features + layerSource = layer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(layer.get('name')); + } + } + } + } + if (layer.type === "IMAGE") { + if (layer.getVisible()) { + // get the features + layerSource = layer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(layer.get('name')); + } + + } + }) + + // console.log('layerSourceArr', layerSourceArr); + console.log('layerTypeNameArr', layerTypeNameArr); + console.log('layerNameArr', layerNameArr); + + for (let i = 0; i < layerTypeNameArr.length; i++) { + let req = await reqTableData(layerTypeNameArr[i]); + // console.log(typeof(res)); + + if (req.success) { + featureCollections.push(req.result); + if (req.result.features.length > 0) { + allFeatures.push(req.result.features); + } + } + else { + alert(req.result); + return; + } + + } + + allFeatures = flatten(allFeatures); + + this.setState({ allFeatures: allFeatures }); + + } + + /*getLayerSearchLabel = async() => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + console.log('getLayerSearchLabel result', result); + this.setState({ + searchLabelData: result.data + }) + } + } else { + + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + } + }*/ + + setAddFeatureValue = (obj) => { + this.setState({ addFeatureValue: obj }) + } + + renderToolbarButtons = () => { + const { activeSideLeft } = this.state + let olMousePosition = document.getElementsByClassName("ol-mouse-position")[0]; + let olScaleLine = document.getElementsByClassName("ol-scale-line")[0]; + + if (activeSideLeft) { + olMousePosition.classList.add("active-side-left"); + olScaleLine.classList.add("active-side-left"); + } + else { + olMousePosition.classList.remove("active-side-left"); + olScaleLine.classList.remove("active-side-left"); + } + + return ( + + this.zoomIn()} + > + + + + this.zoomOut()} + > + + + + + + + + + + {/* this.toggleDrawerDailyInfo()} + > + + */} + + {/* + + this.mapMeasurement()} + > + + + + + +
+ Map Measurement +
+
+ Remove Layer +
+
*/} + + + {/* this.toggleBtnDropMeasure()}> + + + + + + + +
Map Measurement
+
+ this.toggleMeasure('LineString')}> + Distance + + this.toggleMeasure('Polygon')}> + Area + +
+
*/} + + this.toggleMeasureVisible()} + > + + + + + {/**/} + + this.toggleLegend()} + > + + + + + + + + {/* */} + + this.toggleBarProggress()} + > + + + + this.togglePieStatus()} + > + + + + {/*
+ +
*/} +
+ ) + } + + toggleBarProggress = () => { + this.props.toggleProggresBottom() + } + + togglePieStatus = () => { + this.props.toggleStatusRight() + } + + finishDrawingAdd = () => { + // const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + + // Set Drawing type to null and active is false + DrawingUtil.setActive(null, false); + DrawingUtil.destroy(); + + // Clear the drawing drawingLayer in drawingLayer layer + if (drawingLayer) { + drawingLayer.getSource().clear(); + } + if (chosenLayer) { + chosenLayer.getSource().clear(); + } + + // Removing DrawingLayer and ChosenLayer layer + let layersToRemove = []; + this.props.olmap.getLayers().forEach(function (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'DrawingLayer') { + layersToRemove.push(layer); + } + if (layer.get('name') !== undefined && layer.get('name') == 'ChosenLayer') { + layersToRemove.push(layer); + } + }); + for (var i = 0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + + // // Set snapping mode to false + // this.setState({snapMode: false}); + + // // Calling props cancelDraw in MapToolbar.js + // this.props.cancelDraw(); + } + + mapMeasurement = () => { + // console.log() + /*return ( + + + + + + +
+ Map Measurement +
+
+ this.removeLayer(layer)}> Remove Layer +
+
+ )*/ + } + + toggleBtnDropMeasure = () => { + this.setState({ btnDropMeasureVisible: !this.state.btnDropMeasureVisible }, () => { + if (!this.state.btnDropMeasureVisible) { + this.setState({ + // measureVisible: false, + measureName: '', + measureType: '' + }); + } + }); + } + + // toggleBt + + /*toggleMeasure = (name, type) => { + this.setState({ + measureVisible: true, + measureName: name, + measureType: type + }); + }*/ + + pointerMoveHandler = (evt) => { + let { sketch, helpTooltipElement, helpTooltip, continuePolygonMsg, continueLineMsg } = this.state; + if (evt.dragging) { + return; + } + /** @type {string} */ + var helpMsg = 'Click to start drawing'; + + if (sketch) { + var geom = sketch.getGeometry(); + if (geom instanceof Polygon) { + helpMsg = continuePolygonMsg; + } else if (geom instanceof LineString) { + helpMsg = continueLineMsg; + } + } + + // helpTooltipElement.innerHTML = helpMsg; + helpTooltip.setPosition(evt.coordinate); + + helpTooltipElement.classList.remove('hidden'); + }; + + restartMeasureMap = () => { + let { olmap } = this.props; + let { measureType, measureDraw } = this.state; + + // empty measureValue + this.setState({ measureValue: '' }); + + // clear the layer source of measureLayer + this.measureLayer.getSource().clear(); + + // remove interaction of measureDraw + olmap.removeInteraction(measureDraw); + } + + deactiveMeasureMap = () => { + const { olmap } = this.props; + this.restartMeasureMap(); + this.setState({ measureType: '' }); + + // removing measure_layer + olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') == 'measure_layer') { + olmap.removeLayer(layer); + } + }) + } + + toggleMeasure = (type) => { + this.setState({ + measureType: type + }, () => { + let { olmap } = this.props; + let { measureType, measureDraw } = this.state; + + this.restartMeasureMap(); + + // olmap.on('pointermove', this.pointerMoveHandler.bind(this)); + + // removing measure_layer first + olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') == 'measure_layer') { + olmap.removeLayer(layer); + } + }) + + // then recreate new measure_layer (in case if user changing measure type) + olmap.addLayer(this.measureLayer); + + this.addInteractionMeasure(measureType); + }) + } + + addInteractionMeasure = (measureType) => { + // let type = (typeSelect.value == 'area' ? 'Polygon' : 'LineString'); + let { olmap } = this.props; + let { measureDraw, sketch } = this.state; + let measure = new Draw({ + source: this.measureLayer.getSource(), + type: measureType, + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + lineDash: [10, 10], + width: 3 + }), + image: new CircleStyle({ + radius: 5, + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.7)' + }), + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.2)' + }) + }) + }) + }); + this.setState({ measureDraw: measure }, () => { + let { measureDraw, measureTooltipElement, measureTooltip } = this.state; + let { olmap } = this.props; + olmap.addInteraction(measureDraw); + + + // createMeasureTooltip(); + // createHelpTooltip(); + + + + var listener; + measureDraw.on('drawstart', + (evt) => { + + // clear the layer source of measureLayer + this.measureLayer.getSource().clear(); + + // set sketch + sketch = evt.feature; + + /** @type {import("../src/ol/coordinate.js").Coordinate|undefined} */ + var tooltipCoord = evt.coordinate; + + listener = sketch.getGeometry().on('change', (evt) => { + var geom = evt.target; + var output; + if (geom instanceof Polygon) { + // console.log('geom Polygon',geom); + output = formatArea(geom); + tooltipCoord = geom.getInteriorPoint().getCoordinates(); + console.log('tooltipCoord', tooltipCoord); + // console.log('output Polygon', output); + this.setState({ measureValue: output }); + } else if (geom instanceof LineString) { + output = formatLength(geom); + tooltipCoord = geom.getLastCoordinate(); + // console.log('tooltipCoord', tooltipCoord); + // console.log('output LineString', output); + this.setState({ measureValue: output }); + } + // measureTooltipElement.innerHTML = output; + // measureTooltip.setPosition(tooltipCoord); + }); + }); + + measureDraw.on('drawend', + () => { + // measureTooltipElement.className = 'ol-tooltip ol-tooltip-static'; + // measureTooltip.setOffset([0, -7]); + // unset sketch + sketch = null; + // unset tooltip so that a new one can be created + measureTooltipElement = null; + // this.createMeasureTooltip(); + // olmap.removeInteraction(measureDraw); + + unByKey(listener); + }); + }); + } + + /** + * Creates a new measure tooltip + */ + createMeasureTooltip = () => { + let { measureTooltipElement, measureTooltip } = this.state; + let { olmap } = this.props; + if (measureTooltipElement) { + measureTooltipElement.parentNode.removeChild(measureTooltipElement); + } + measureTooltipElement = document.createElement('div'); + measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure'; + measureTooltip = new Overlay({ + element: measureTooltipElement, + offset: [0, -15], + positioning: 'bottom-center' + }); + olmap.addOverlay(measureTooltip); + } + + /*renderMeasureContent = () => { + const { measureVisible } = this.state; + console.log('measureVisible', measureVisible); + alert('measureVisible '+measureVisible); + return ( +
+
+ Helloooooooo +
+
+ ) + }*/ + + mappifiedLayerTree = () => { + const { olmap } = this.props; + let layers = []; + + olmap.getLayers().forEach((layer, i) => { + if (layer.get('type') !== 'base') { + if (layer.get('type') !== 'vector') { + if (layer.get('type') == 'layerGroup') { + // layer.getLayers().forEach((sublayer, i) => { + // if (sublayer.getVisible()) { + // url = sublayer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + // if (url) { + // promises.push(axios.get(url)); + // } + + // } + // }); + // layers.push(layer); + } + else { + // if (layer.getVisible()) { + // url = layer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + // if (url) { + // promises.push(axios.get(url)); + // } + // } + layers.push(layer); + } + } + } + }); + + + } + + toggleQueryBuilder = () => { + this.setState({ queryBuilderVisible: !this.state.queryBuilderVisible }) + } + + handleQbClose = (query, type, tree) => { + this.toggleQueryBuilder(); + this.props.handleQueryBuilder(query, type, tree) + } + + handleQbReset = (query, type) => { + this.setState({ queryBuilderVisible: false }) + this.props.handleQueryBuilder(query, type, '') + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataTableDetail(); + }) + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataTableDetail(); + }) + } + + handleSearchtype = (field) => { + this.setState({ searchDetailField:field }); + } + + handleSearch = (val) => { + this.setState({ search:val }); + this.getDataTableDetail(); + } + + setTableType = (type) => { + this.setState({table_type:type, search:""},()=> { + this.getDataTableDetail(); + }) + } + + getDataTableDetail = async () => { + const { + searchDetailField, + search, + currentPage, + rowsPerPage + } = this.state + + let param = { + table_type:this.state.table_type, + currentPage, + rowsPerPage, + searchDetailField, + search, + } + let dataRes = await requestTableDailyInfoApi(param); + this.setState({ mapTableData:dataRes.data, totalRecord:dataRes.total_record}); + } + + render() { + const { isReady } = this.state; + if (!isReady) { + return null; + } + return ( + + + this.props.onCheckOpt(state, checkedKeys)} + checkedKeysSales={this.props.checkedKeysSales} + checkedKeysCustomer={this.props.checkedKeysCustomer} + checkedKeysOffice={this.props.checkedKeysOffice} + checkedKeysDemografi={this.props.checkedKeysDemografi} + checkedKeysAnalisa={this.props.checkedKeysAnalisa} + checkedKeysEmployeeDivision={this.props.checkedKeysEmployeeDivision} + salesGroupTree={this.props.salesGroupTree} + employeeDivisionTree={this.props.employeeDivisionTree} + setSalesGroupTree={(data) => this.props.setSalesGroupTree(data)} + setEmployeeDivisionTree={(data) => this.props.setEmployeeDivisionTree(data)} + projectTree={this.props.projectTree} + setProjectTree={(data) => this.props.setProjectTree(data)} + checkedKeysProjectTree={this.props.checkedKeysProjectTree} + setCheckedKeysProjectTree={(data) => this.props.setCheckedKeysProjectTree(data)} + /> + + + this.toggleDrawerDailyInfo()} + visible={this.state.drawerDailyInfo} + mask={false} + width={250} + style={{ height: 'calc(100vh - (100vh / 2))', position: 'absolute' }} + > + this.setState({ mapTableTitle: name })} + setMapTableColumns={(data) => this.setState({ mapTableColumns: data })} + setMapTableData={(data) => this.setState({ mapTableData: data })} + removeLayerByName={(layerName) => this.props.removeLayerByName(layerName)} + setIsProcessing={(data) => this.props.setIsProcessing(data)} + getDataTableDetail={this.getDataTableDetail} + setTableType={this.setTableType} + /> + + + + this.props.openPopupRight()} + popupDataTemp={this.props.popupDataTemp} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + activeListFeatureId={this.props.activeListFeatureId} + searchLabelData={this.props.searchLabelData} + layerInfo={this.props.layerInfo} + /> + + + + + + {this.renderToolbarButtons()} + + + + + + + + {/*{ + this.state.layerSwitcherVisible && + + }*/} + + {/* { this.state.mapTableModalVisible && + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + refreshMapTable={() => this.refreshMapTable()} + /> + } */} + + {this.state.mapTableModalVisible && + this.setState({ mapTableData: data })} + olmap={this.props.olmap} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + refreshMapTable={() => this.refreshMapTable()} + showRoute={(route) => this.props.showRoute(route)} + removeLayerByName={(layerName) => this.props.removeLayerByName(layerName)} + setIsProcessing={(data) => this.props.setIsProcessing(data)} + onShowSizeChange={this.onShowSizeChange} + onPagination={this.onPagination} + currentPage={this.state.currentPage} + rowsPerPage={this.state.rowsPerPage} + total={this.state.totalRecord} + handleSearch={this.handleSearch} + search={this.state.search} + /> + } + + {this.state.drawingToolVisible && + this.toggleAddFeatureVisible()} + setAddFeatureValue={(obj) => this.setAddFeatureValue(obj)} + setDrawingState={(DrawingUtil, drawingLayer, chosenLayer) => this.setState({ + DrawingUtil: DrawingUtil, + drawingLayer: drawingLayer, + chosenLayer: chosenLayer + })} + /> + } + + {this.state.addFeatureVisible && + this.toggleAddFeatureVisible()} + addFeatureValue={this.state.addFeatureValue} + closeDrawing={this.cancelDraw} + finishDrawingAdd={() => this.finishDrawingAdd()} + /> + } + + + + + + {this.state.createNewLayerVisible && + + } + + {this.state.legendVisible && this.toggleLegend()} />} + + {/* this.state.measureVisible && + + */} + + {/* {this.state.queryBuilderVisible && + + } */} + + {this.state.measureVisible && + this.toggleMeasure(type)} + toggleMeasureVisible={() => this.toggleMeasureVisible()} + measureType={this.state.measureType} + measureValue={this.state.measureValue} + /> + } + + ) + } +} + +export default MapToolbar; \ No newline at end of file diff --git a/src/components/MapToolbar/MapToolbar_backup.js b/src/components/MapToolbar/MapToolbar_backup.js new file mode 100644 index 0000000..aa37549 --- /dev/null +++ b/src/components/MapToolbar/MapToolbar_backup.js @@ -0,0 +1,1869 @@ +import React, { Component, Fragment, useState } from 'react'; +import { Drawer, Tooltip } from 'antd'; +import { + mappify, + SimpleButton, + AddWmsPanel, + LayerTree, + Panel +} from '@terrestris/react-geo'; +import { Badge, Dropdown, UncontrolledDropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, + InputGroup, Input, InputGroupAddon, InputGroupText, Button, Collapse, Card, CardBody +} from 'reactstrap'; +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import MeasureButton from '@terrestris/react-geo/dist/Button/MeasureButton/MeasureButton'; +import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import MapTable from '../../components/MapTable'; +import DrawingTool from '../../components/DrawingTool'; +import MapLayerStyles from '../../components/MapLayerStyles'; +import CreateNewLayer from '../../components/CreateNewLayer'; +import SearchFeatures from '../../components/SearchFeatures'; +import AddFeature from '../../components/AddFeature'; +import MapLegend from '../../components/MapLegend'; +import MeasureContainer from '../../components/MeasureContainer'; +// import PopupContainer from '../../components/PopupContainer'; +import { appConfig, wfsDispatcherUrl } from '../../const/MapConst.js'; +// import { API_LAYER_SEARCH_LABEL } from '../../const/ApiConst.js'; +import './MapToolbar.css'; +import { Icon, InlineIcon } from '@iconify/react'; +// import layersIcon from '@iconify/icons-simple-line-icons/layers'; +import layersIcon from '@iconify/icons-ion/layers'; +import mapIcon from '@iconify/icons-ion/map'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import searchPlus from '@iconify/icons-fe/search-plus'; +import searchMinus from '@iconify/icons-fe/search-minus'; +import mapLegend from '@iconify/icons-mdi/map-legend'; +import rulerIcon from '@iconify/icons-mdi/ruler'; +import axios from 'axios'; +import OlLayerGroup from 'ol/layer/Group'; +import OlLayerTile from 'ol/layer/Tile'; +import OlLayerImage from 'ol/layer/Image'; +import ZoomToExtent from 'ol/control/ZoomToExtent'; +import {fromLonLat, transformExtent} from 'ol/proj'; +import { Draw } from 'ol/interaction'; +import {LineString, Polygon} from 'ol/geom'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Vector as VectorSource} from 'ol/source'; +import {Circle as CircleStyle, Fill, Stroke, Style, Text} from 'ol/style'; +import {unByKey} from 'ol/Observable'; +import Overlay from 'ol/Overlay'; +import { WMS_CAPABILITIES_URL, legendPicUrl } from '../../const/MapConst.js'; +import { findWhere, flatten, without } from 'underscore'; +import { reqTableData, getGeomType, getTableColumns, getLayerColor, formatLength, formatArea, lightOrDark } from '../../const/GeoserverFunc.js'; +import '../../assets/css/customscroll.css'; + +const MappifiedLayerTree = mappify(LayerTree); + +/* +Toggle Visibility that must be closed or switched to its visibility: + +layer: drawerLayerVisible +popup: popupRightVisible +searchFeature: drawerSearchVisible +basemap: layerSwitcherVisible +legend: legendVisible +drawing: drawingToolVisible +layerStyle: drawerLayerStylesVisible +mapTable: mapTableModalVisible + +*/ + +class MapToolbar extends Component { + + constructor(props) { + super(props); + this.state = { + isReady: false, + wmsLayers: [], + drawerLayerVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + layerSwitcherVisible: false, + layerPanelVisible: false, + mapTableModalVisible: false, + mapTableName: '', + mapTableTitle: '', + mapTableTitleSrc: '', + selectedMapLayer: null, + resTableData: null, + drawingToolVisible: false, + layerNameDraw: '', + geomTypeDraw: '', + drawerLayerStylesVisible: false, + createNewLayerVisible: false, + searchInput: '', + allFeatures: [], + popupRightVisible: false, + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + }, + addFeatureVisible: false, + addFeatureColumns: null, + addFeatureValue: null, + legendVisible: false, + activeSideLeft: false, + DrawingUtil: null, + drawingLayer: null, + chosenLayer: null, + // searchLabelData: [], + btnDropMeasureVisible: false, + measureVisible: false, + measureName: '', + measureType: '', + measureValue: '', + measureDraw: null, + sketch: null, + helpTooltipElement: null, + helpTooltip: null, + measureTooltipElement: null, + measureTooltip: null, + continuePolygonMsg: 'Click to continue drawing the polygon', + continueLineMsg: 'Click to continue drawing the line', + + + } + this.getWMSLayers = this.getWMSLayers.bind(this); + this.toggleDrawerLayer = this.toggleDrawerLayer.bind(this); + this.toggleDrawerSearch = this.toggleDrawerSearch.bind(this); + this.toggleLayerSwitcher = this.toggleLayerSwitcher.bind(this); + // this.toggleLayerPanel = this.toggleLayerPanel.bind(this); + this.cancelWmsLayer = this.cancelWmsLayer.bind(this); + this.customTitleLayerTree = this.customTitleLayerTree.bind(this); + this.onRightClickLayerTree = this.onRightClickLayerTree.bind(this); + this.onDropLayerTree = this.onDropLayerTree.bind(this); + // this.onClickLayerMenu = this.onClickLayerMenu.bind(this); + this.toggleMapTable = this.toggleMapTable.bind(this); + this.toggleMapAnalyzeTool = this.toggleMapAnalyzeTool.bind(this); + // this.toggleDrawingTool = this.toggleDrawingTool.bind(this); + this.cancelDraw = this.cancelDraw.bind(this); + this.toggleLayerStyles = this.toggleLayerStyles.bind(this); + this.openLayerStyles = this.openLayerStyles.bind(this); + this.closeLayerStyles = this.closeLayerStyles.bind(this); + this.toggleCreateNewLayer = this.toggleCreateNewLayer.bind(this); + + this.measureLayer = new VectorLayer({ + name: 'measure_layer', + title: 'Measure Layer', + type: 'vector', + source: new VectorSource(), + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + // lineDash: [10, 10], + width: 3 + }), + image: new CircleStyle({ + radius: 7, + fill: new Fill({ + color: '#ffcc33' + }) + }) + }) + }); + } + + componentDidMount() { + this.setState({isReady: true}); + } + + getLayerType = (item) => { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_geom_type; + } + else { + return "Undefined Geom Type"; + } + } + + getLayerColor = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log('getLayerColor', layer); + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_color; + } + else { + return "#ffffff"; + } + } + + getLayerTitle = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + let output = ''; + + if (matchLayerName.length > 0) { + if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + // return matchLayerName[0].layer_title; + output = matchLayerName[0].layer_title; + return output; + } + else { + output = matchLayerName[0].layer_name; + return output; + } + } + else { + return "No layer title provided"; + // output = matchLayerName[0].layer_name; + // return matchLayerName[0].layer_name; + } + } + + getLayerTitleTextColor = (layerColor) => { + let brightness = lightOrDark(layerColor); // 'light' or 'dark' + + if (brightness == 'light') { + return 'dark-text'; + } + else { + return 'light-text'; + } + } + + getLayerCount = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log(layer) + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + // if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + // return matchLayerName[0].layer_title; + // } + // else { + // return matchLayerName[0].layer_name; + // } + return matchLayerName[0].total_features; + + } + else { + return "0"; + } + } + + zoomIn = () => { + this.props.olmap.getView().animate({ + zoom: this.props.olmap.getView().getZoom() + 1, + duration: 500 + }); + } + + zoomOut = () => { + this.props.olmap.getView().animate({ + zoom: this.props.olmap.getView().getZoom() - 1, + duration: 500 + }); + } + + getWMSLayers() { + CapabilitiesUtil.parseWmsCapabilities(WMS_CAPABILITIES_URL) + .then(CapabilitiesUtil.getLayersFromWmsCapabilities) + .then(layers => { + console.log('wmsLayers', layers); + this.setState({ + wmsLayers: layers + }); + }) + .catch(() => alert('Could not parse capabilities document.')); + } + + /*toggleDrawerLayer () { + this.closeAllDrawer(); + // this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}); + // this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}, () => this.closeAllDrawer()); + // this.closeAllDrawer(); + const { drawerLayerVisible } = this.state; + if (drawerLayerVisible) { + this.setState({ drawerLayerVisible: false }, () => this.closeAllDrawer() ); + } + else { + this.setState({ drawerLayerVisible: true }, () => this.closeAllDrawer() ); + } + }*/ + + /*toggleDrawerLayer () { + this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }) + }*/ + + toggleDrawerLayer () { + let { drawerLayerVisible } = this.state; + if (drawerLayerVisible) { + this.setState({drawerLayerVisible: false}, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({drawerLayerVisible: true}, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }) + } + } + + /*toggleDrawerSearch () { + this.closeAllDrawer(); + const { drawerSearchVisible } = this.state; + console.log('drawerSearchVisible', drawerSearchVisible); + if (drawerSearchVisible) { + this.setState({drawerSearchVisible: false}, () => this.closeAllDrawer() ); + } + else { + this.setState({drawerSearchVisible: true}, () => { + this.getFeaturesOnMap(); + this.closeAllDrawer(); + }); + } + }*/ + + toggleDrawerSearch () { + const { drawerSearchVisible } = this.state; + + if (drawerSearchVisible) { + this.setState({drawerSearchVisible: false}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + // drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }) + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({drawerSearchVisible: true}, () => { + this.getFeaturesOnMap(); + // this.getLayerSearchLabel(); + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + // drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + } + + toggleMapAnalyzeTool () { + // this.setState({drawerAnalyzeToolVisible: !this.state.drawerAnalyzeToolVisible}); + const { drawerAnalyzeToolVisible } = this.state; + + if (drawerAnalyzeToolVisible) { + this.setState({drawerAnalyzeToolVisible: false}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + // drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }) + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({drawerAnalyzeToolVisible: true}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + // drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + } + + toggleLayerSwitcher() { + this.setState({layerSwitcherVisible: !this.state.layerSwitcherVisible}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + // layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleMapTable() { + this.setState({mapTableModalVisible: !this.state.mapTableModalVisible}, () => { + this.setState({ + // drawerLayerVisible: false, + drawerLayerStylesVisible: false, + // mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + // activeSideLeft: false + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleCreateNewLayer() { + this.setState({createNewLayerVisible: !this.state.createNewLayerVisible}); + this.deactiveMeasureMap(); + } + + toggleLayerStyles() { + this.setState({drawerLayerStylesVisible: !this.state.drawerLayerStylesVisible}, () => { + this.setState({ + drawerLayerVisible: false, + // drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleAddFeatureVisible = () => { + this.setState({addFeatureVisible: !this.state.addFeatureVisible}); + this.deactiveMeasureMap(); + } + + toggleLegend = () => { + this.setState({legendVisible: !this.state.legendVisible}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + // legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleMeasureVisible = () => { + // this.setState({measureVisible: !this.state.measureVisible}); + this.setState({measureVisible: !this.state.measureVisible}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + // measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + + if (!this.state.measureVisible) { + this.deactiveMeasureMap(); + } + }); + } + + closeAllDrawer = () => { + /* + Toggle Visibility that must be closed or switched to its visibility: + + layer: drawerLayerVisible + popup: popupRightVisible + searchFeature: drawerSearchVisible + basemap: layerSwitcherVisible + legend: legendVisible + drawing: drawingToolVisible + layerStyle: drawerLayerStylesVisible + mapTable: mapTableModalVisible + + Group layers: layer, layerStyle, mapTable (that shouldn't be closed each other) + Group popup: searchFeature, popup (that shouldn't be closed each other) => searchFeature below of popup + Legend + BaseMap + Drawing + */ + + const { drawerLayerVisible, drawerLayerStylesVisible, mapTableModalVisible, + drawerSearchVisible, popupRightVisible, + legendVisible, + layerSwitcherVisible, + drawingToolVisible + } = this.state; + + let visibleArr = [drawerLayerVisible, drawerLayerStylesVisible, mapTableModalVisible, + drawerSearchVisible, popupRightVisible, + legendVisible, + layerSwitcherVisible, + drawingToolVisible + ]; + + console.log('visibleArr', visibleArr); + + if (drawerLayerVisible) { + this.setState({ + drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (drawerLayerStylesVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: true, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (mapTableModalVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: true, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (drawerSearchVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: true, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (popupRightVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: true, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (legendVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: true, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (layerSwitcherVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: true, + drawingToolVisible: false + }) + } + + if (drawingToolVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: true + }) + } + + } + + // toggleLayerPanel(layer) { + // console.log('toggleLayerPanel', layer); + // this.setState({layerPanelVisible: !this.state.layerPanelVisible}); + // } + + cancelWmsLayer() { + console.log('cancel wms'); + this.setState({ wmsLayers: [] }); + } + + onClickLayerMenu = (layer) => { + // console.log('onclick layer menu', layer); + } + + customTitleLayerTree(layer) { + // console.log('customTitleLayerTree', layer.get('title')); + // console.log('customTitleLayerTree', layer.get('type')); + // return ( + //
{layer.get('name')}
+ // ) + + // if (layer instanceof OlLayerGroup) { + + // } + + // return ( + //
+ //
+ // {layer.get('name')} + //
+ // {/*
+ // + //
*/} + //
+ // + // + // + // + // + // {layer.get('name')} + // this.showTableData(layer)}> Show Table42 + // Remove Layer + // + // + //
+ //
+ // ) + + // if (layer.get('type') == 'layer') { + return ( +
+
+ {layer.get('title') ? layer.get('title') : layer.get('name')} +
+ {/*
+ +
*/} +
+ + {/*10*/} + { layer.get('type') == 'layer' ? + {this.getLayerCount(layer)} + : null + } + this.onClickLayerMenu(layer)}> + + + + +
+ {layer.get('title') ? layer.get('title') : layer.get('name')} +
+
+ { + layer.get('type') === 'layer' || layer instanceof OlLayerImage ? + // layer instanceof OlLayerTile || layer instanceof OlLayerImage ? + + + this.zoomToLayer(layer)}> Zoom To Layer + this.showTableData(layer)}> Show Data + {this.props.editGeometryVisible ? + Add Feature + : this.addFeature(layer)}> Add Feature + } + {/* this.addFeature(layer)}> Add Feature*/} + this.styleLayer(layer)}> Layer Styles + + : + null } + this.removeLayer(layer)}> Remove Layer +
+
+
+
+ ) + // } + // else { + // return null; + // } + } + + onRightClickLayerTree(layer) { + console.log('right click layer tree', layer); + return ( + + + Header + Action + Another Action + + Another Action + + + ) + } + + onDropLayerTree() { + console.log('dropped'); + } + + + + /*showTableData = (layer) => { + console.log('showTableData', layer); + this.setState({ + mapTableTitle: layer.get('name'), + mapTableTitleSrc: layer.getSource().params_.LAYERS + }, async () => { + // console.log('mapTableTitle', this.state.mapTableTitle); + // console.log('mapTableTitleSrc', this.state.mapTableTitleSrc) + // let req = this.requestTableData(this.state.mapTableTitleSrc); + let res = await reqTableData(this.state.mapTableTitle); + console.log('showTableData',res); + // if (res !== undefined) { + // this.setState({resTableData: res}); + // } + + if (res.success) { + this.setState({resTableData: res.result}, () => this.toggleMapTable()); + } + else { + alert(res.result); + return; + } + }); + }*/ + + showTableData = (layer) => { + this.setState({ + mapTableTitle: layer.get('title'), + mapTableName: layer.get('name') + // mapTableTitleSrc: layer.getSource().params_.LAYERS + }, () => this.toggleMapTable()); + } + + refreshMapTable = async () => { + const { mapTableTitle, mapTableName } = this.state; + let res = await reqTableData(mapTableName); + if (res.success) { + this.setState({resTableData: res.result}); + } + else { + alert(res.result); + return; + } + } + + styleLayer = async (layer) => { + console.log('styling layer...', layer); + + let SLD_URL = layer.getSource().params_.SLD; + console.log('SLD_URL', SLD_URL); + + let resGetLayerColor = await getLayerColor(SLD_URL); + + console.log('styleLayer resGetLayerColor', resGetLayerColor); + + // this.toggleLayerStyles(); + + if (this.state.drawerLayerStylesVisible) { + this.closeLayerStyles(); + this.openLayerStyles(); + } + else { + this.openLayerStyles(); + } + /* + axios.get(SLD_URL) + .then(res => { + console.log('axios get SLD', res); + let text = res.data; + let parser, xmlDoc; + // let self = this; + // parseString(res.data, function (err, result) { + // console.log(result); + // self.events = result; + // }); + if (window.DOMParser) { + // code for modern browsers + parser = new DOMParser(); + xmlDoc = parser.parseFromString(text,"text/xml"); + + console.log('xmlDoc', xmlDoc); + } + // else { + // // code for old IE browsers + // xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + // xmlDoc.async = false; + // xmlDoc.loadXML(text); + // console.log('xmlDoc', xmlDoc); + // } + }) + .catch(err => { + console.log('axios get SLD err', err); + }); + + if (this.state.drawerLayerStylesVisible) { + this.closeLayerStyles(); + this.openLayerStyles(); + } + else { + this.openLayerStyles(); + }*/ + } + + openLayerStyles() { + this.setState({drawerLayerStylesVisible: true}); + } + + closeLayerStyles() { + this.setState({drawerLayerStylesVisible: false}); + } + + addFeature = async (layer) => { + console.log('adding feature on layer...', layer); + // console.log(layer.get('name')); + + let geomType = ''; + // request Geom Type from API + let reqGeomType = await getGeomType(layer.get('name')); + console.log('addFeature reqGeomType', reqGeomType) + if (reqGeomType.success) { + geomType = reqGeomType.result; + } + else { + geomType = ''; + alert(reqGeomType.result); + return; + } + + // request the table columns from API + // let featureColumns = await getTableColumns(layer.get('name')); + // console.log('addFeature featureColumns', featureColumns); + + let reqTableColumns = await getTableColumns(layer.get('name')); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({addFeatureColumns: reqTableColumns.result}); + } + else { + alert(reqTableColumns.result); + // return; + } + + + this.setState({ + drawingToolVisible: true, + layerNameDraw: layer.get('name'), + geomTypeDraw: geomType, + // addFeatureColumns: reqTableColumns.result + }); + this.props.toggleActiveStateAddGeometry(); + + } + + cancelDraw() { + this.setState({ + drawingToolVisible: false, + layerNameDraw: '', + geomTypeDraw: '' + }); + this.props.toggleActiveStateAddGeometry(); + } + + removeLayer = (layer) => { + console.log('removing layer', layer); + let confirmation = window.confirm('Are you sure you want to remove this layer?'); + if (!confirmation) { + return; + } + this.props.olmap.removeLayer(layer); + } + + /*zoomToLayer = (layer) => { + console.log('zoomToLayer', layer); + let layerExtent = null; + let newLayerExtent = null; + let transformLayerExtent = null; + let fitOption = { + size: this.props.olmap.getSize(), + duration: 500 + } + // layer.getSource().getExtent(); + // layer.getExtent(); + // layer.getSource().getExtent(); + if (layer instanceof OlLayerImage) { + // console.log(layer.getSource().image_.extent); + layerExtent = layer.getSource().image_.extent; + transformLayerExtent = new transformExtent(layerExtent, 'EPSG:4326', this.props.olmap.getView().getProjection()) + // transformLayerExtent = new transformExtent(layerExtent, this.props.olmap.getView().getProjection(), 'EPSG:4326') + console.log('layerExtent', layerExtent); + console.log('transformLayerExtent', transformLayerExtent); + newLayerExtent = [ + layerExtent[0], layerExtent[2], layerExtent[1], layerExtent[3] + ]; + console.log('newLayerExtent', newLayerExtent); + + // this.props.olmap.getView().fit(new transformExtent(layerExtent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + this.props.olmap.getView().fit(layerExtent, fitOption); + + // new ZoomToExtent() + } + else if (layer instanceof OlLayerTile) { + layerExtent = layer.getSource().tmpExtent_; + // console.log(layer.getSource()); + this.props.olmap.getView().fit(layerExtent, fitOption); + } + // console.log(layer.getSource().getExtent()); + }*/ + + zoomToLayer = (layer) => { + console.log('zoomToLayer getSource', layer.getSource()); + // let layerName = layer.getSource().params_.LAYERS; + let layerName = layer.get('name'); + console.log('layerName', layerName); + const { fitOption } = this.state; + + // if (layer instanceof OlLayerImage) { + // // layerName = layer.getSource(); + + // } + // else if (layer instanceof OlLayerTile) { + + // } + CapabilitiesUtil.parseWmsCapabilities(WMS_CAPABILITIES_URL).then(response => { + console.log('capabilities response', response); + if (response !== undefined) { + let layer = findWhere(response.Capability.Layer.Layer, {"Name": layerName}); + console.log('layer',layer); + + if (layer === undefined) { + alert('Layer '+layerName+' is not found'); + return; + } + + let proj = "CRS:84"; + let bbox = findWhere(layer.BoundingBox, {"crs": proj}); + let extent = bbox.extent; + console.log('layer', layer); + console.log('extent', extent); + // map.getView().fit(new transformExtent(layerExtent, 'EPSG:4326', map.getView().getProjection()), { size: map.getSize() }); + + // console.log('layer', layer); + // this.props.olmap.getView().fit(extent, fitOption); + // console.log('getProjection', this.props.olmap.getView().getProjection()); + // var boundingExtent = ol.extent.boundingExtent(coordinates); + // boundingExtent = ol.proj.transformExtent(boundingExtent, ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857')); + + /*// other option + extent = new transformExtent(extent, "EPSG:4326", "EPSG:3857"); + console.log('extent now', extent); + this.props.olmap.getView().fit(extent, fitOption); */ + + this.props.olmap.getView().fit(new transformExtent(extent, proj, this.props.olmap.getView().getProjection()), fitOption); + + } + }) + } + + onDragEndLayerTree = (callback) => { + console.log('onDragEndLayerTree', callback); + } + + rebuildTreeNodes = (evt) => { + console.log('rebuildTreeNodes',evt); + } + + // to be populated on search feature menu + getFeaturesOnMap = async () => { + let layerSource = ''; + let layerTypeName = ''; + let layerSourceArr = []; + let layerTypeNameArr = []; + let layerNameArr = []; + let featureCollections = []; + let allFeatures = []; + this.props.olmap.getLayers().forEach((layer, i) => { + // console.log('layer', layer); + // console.log('layer type', layer.get('type')); + if (layer.get('type' !== 'base')) { + if (layer.get('type') !== 'vector') { + if (layer.get('type') !== 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + // get the features + layerSource = sublayer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(sublayer.get('name')); + } + }); + } + else { + if (layer.getVisible()) { + // get the features + layerSource = layer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(layer.get('name')); + } + } + } + } + if (layer.type === "IMAGE") { + if (layer.getVisible()) { + // get the features + layerSource = layer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(layer.get('name')); + } + + } + }) + + // console.log('layerSourceArr', layerSourceArr); + console.log('layerTypeNameArr', layerTypeNameArr); + console.log('layerNameArr', layerNameArr); + + for (let i = 0; i < layerTypeNameArr.length; i++) { + let req = await reqTableData(layerTypeNameArr[i]); + // console.log(typeof(res)); + + if (req.success) { + featureCollections.push(req.result); + if (req.result.features.length > 0) { + allFeatures.push(req.result.features); + } + } + else { + alert(req.result); + return; + } + + } + + allFeatures = flatten(allFeatures); + + this.setState({allFeatures: allFeatures}); + + } + + /*getLayerSearchLabel = async() => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + console.log('getLayerSearchLabel result', result); + this.setState({ + searchLabelData: result.data + }) + } + } else { + + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + } + }*/ + + setAddFeatureValue = (obj) => { + this.setState({addFeatureValue: obj}) + } + + renderToolbarButtons = () => { + const { activeSideLeft } = this.state + let olMousePosition = document.getElementsByClassName("ol-mouse-position")[0]; + let olScaleLine = document.getElementsByClassName("ol-scale-line")[0]; + + if (activeSideLeft) { + olMousePosition.classList.add("active-side-left"); + olScaleLine.classList.add("active-side-left"); + } + else { + olMousePosition.classList.remove("active-side-left"); + olScaleLine.classList.remove("active-side-left"); + } + + return ( + + this.zoomIn()} + > + + + + this.zoomOut()} + > + + + + + + + + + + {/* + + this.mapMeasurement()} + > + + + + + +
+ Map Measurement +
+
+ Remove Layer +
+
*/} + + + {/* this.toggleBtnDropMeasure()}> + + + + + + + +
Map Measurement
+
+ this.toggleMeasure('LineString')}> + Distance + + this.toggleMeasure('Polygon')}> + Area + +
+
*/} + + this.toggleMeasureVisible()} + > + + + + + + + this.toggleLegend()} + > + + + + + + + + {/*
+ +
*/} +
+ ) + } + + finishDrawingAdd = () => { + // const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + + // Set Drawing type to null and active is false + DrawingUtil.setActive(null, false); + DrawingUtil.destroy(); + + // Clear the drawing drawingLayer in drawingLayer layer + if (drawingLayer) { + drawingLayer.getSource().clear(); + } + if (chosenLayer) { + chosenLayer.getSource().clear(); + } + + // Removing DrawingLayer and ChosenLayer layer + let layersToRemove = []; + this.props.olmap.getLayers().forEach(function (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'DrawingLayer') { + layersToRemove.push(layer); + } + if (layer.get('name') !== undefined && layer.get('name') == 'ChosenLayer') { + layersToRemove.push(layer); + } + }); + for(var i = 0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + + // // Set snapping mode to false + // this.setState({snapMode: false}); + + // // Calling props cancelDraw in MapToolbar.js + // this.props.cancelDraw(); + } + + mapMeasurement = () => { + // console.log() + /*return ( + + + + + + +
+ Map Measurement +
+
+ this.removeLayer(layer)}> Remove Layer +
+
+ )*/ + } + + toggleBtnDropMeasure = () => { + this.setState({ btnDropMeasureVisible: !this.state.btnDropMeasureVisible }, () => { + if (!this.state.btnDropMeasureVisible) { + this.setState({ + // measureVisible: false, + measureName: '', + measureType: '' + }); + } + }); + } + + // toggleBt + + /*toggleMeasure = (name, type) => { + this.setState({ + measureVisible: true, + measureName: name, + measureType: type + }); + }*/ + + pointerMoveHandler = (evt) => { + let { sketch, helpTooltipElement, helpTooltip, continuePolygonMsg, continueLineMsg } = this.state; + if (evt.dragging) { + return; + } + /** @type {string} */ + var helpMsg = 'Click to start drawing'; + + if (sketch) { + var geom = sketch.getGeometry(); + if (geom instanceof Polygon) { + helpMsg = continuePolygonMsg; + } else if (geom instanceof LineString) { + helpMsg = continueLineMsg; + } + } + + // helpTooltipElement.innerHTML = helpMsg; + helpTooltip.setPosition(evt.coordinate); + + helpTooltipElement.classList.remove('hidden'); + }; + + restartMeasureMap = () => { + let { olmap } = this.props; + let { measureType, measureDraw } = this.state; + + // empty measureValue + this.setState({measureValue: ''}); + + // clear the layer source of measureLayer + this.measureLayer.getSource().clear(); + + // remove interaction of measureDraw + olmap.removeInteraction(measureDraw); + } + + deactiveMeasureMap = () => { + const { olmap } = this.props; + this.restartMeasureMap(); + this.setState({measureType: ''}); + + // removing measure_layer + olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') == 'measure_layer') { + olmap.removeLayer(layer); + } + }) + } + + toggleMeasure = (type) => { + this.setState({ + measureType: type + }, () => { + let { olmap } = this.props; + let { measureType, measureDraw } = this.state; + + this.restartMeasureMap(); + + // olmap.on('pointermove', this.pointerMoveHandler.bind(this)); + + // removing measure_layer first + olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') == 'measure_layer') { + olmap.removeLayer(layer); + } + }) + + // then recreate new measure_layer (in case if user changing measure type) + olmap.addLayer(this.measureLayer); + + this.addInteractionMeasure(measureType); + }) + } + + addInteractionMeasure = (measureType) => { + // let type = (typeSelect.value == 'area' ? 'Polygon' : 'LineString'); + let { olmap } = this.props; + let { measureDraw, sketch } = this.state; + let measure = new Draw({ + source: this.measureLayer.getSource(), + type: measureType, + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + lineDash: [10, 10], + width: 3 + }), + image: new CircleStyle({ + radius: 5, + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.7)' + }), + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.2)' + }) + }) + }) + }); + this.setState({measureDraw: measure}, () => { + let { measureDraw, measureTooltipElement, measureTooltip } = this.state; + let { olmap } = this.props; + olmap.addInteraction(measureDraw); + + + // createMeasureTooltip(); + // createHelpTooltip(); + + + + var listener; + measureDraw.on('drawstart', + (evt) => { + + // clear the layer source of measureLayer + this.measureLayer.getSource().clear(); + + // set sketch + sketch = evt.feature; + + /** @type {import("../src/ol/coordinate.js").Coordinate|undefined} */ + var tooltipCoord = evt.coordinate; + + listener = sketch.getGeometry().on('change', (evt) => { + var geom = evt.target; + var output; + if (geom instanceof Polygon) { + // console.log('geom Polygon',geom); + output = formatArea(geom); + tooltipCoord = geom.getInteriorPoint().getCoordinates(); + console.log('tooltipCoord',tooltipCoord); + // console.log('output Polygon', output); + this.setState({measureValue: output}); + } else if (geom instanceof LineString) { + output = formatLength(geom); + tooltipCoord = geom.getLastCoordinate(); + // console.log('tooltipCoord', tooltipCoord); + // console.log('output LineString', output); + this.setState({measureValue: output}); + } + // measureTooltipElement.innerHTML = output; + // measureTooltip.setPosition(tooltipCoord); + }); + }); + + measureDraw.on('drawend', + () => { + // measureTooltipElement.className = 'ol-tooltip ol-tooltip-static'; + // measureTooltip.setOffset([0, -7]); + // unset sketch + sketch = null; + // unset tooltip so that a new one can be created + measureTooltipElement = null; + // this.createMeasureTooltip(); + // olmap.removeInteraction(measureDraw); + + unByKey(listener); + }); + }); + } + + /** + * Creates a new measure tooltip + */ + createMeasureTooltip = () => { + let { measureTooltipElement, measureTooltip } = this.state; + let { olmap } = this.props; + if (measureTooltipElement) { + measureTooltipElement.parentNode.removeChild(measureTooltipElement); + } + measureTooltipElement = document.createElement('div'); + measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure'; + measureTooltip = new Overlay({ + element: measureTooltipElement, + offset: [0, -15], + positioning: 'bottom-center' + }); + olmap.addOverlay(measureTooltip); + } + + /*renderMeasureContent = () => { + const { measureVisible } = this.state; + console.log('measureVisible', measureVisible); + alert('measureVisible '+measureVisible); + return ( +
+
+ Helloooooooo +
+
+ ) + }*/ + + render() { + const {isReady} = this.state; + if (!isReady) { + return null; + } + return ( + + +
+ + +
+ {/* console.log('layer tree changed')} + onRightClick={(layer) => this.onRightClickLayerTree(layer)} + nodeTitleRenderer={(layer) => this.customTitleLayerTree(layer)} + onDragEnd={(callback) => this.onDragEndLayerTree(callback)} + onLayerChangeVisible={() => console.log('layer change visible')} + onDrop={(info) => console.log('onDrop', info)} + />*/} +
+ this.customTitleLayerTree(layer)} + /> +
+
+ + + this.props.openPopupRight()} + popupDataTemp={this.props.popupDataTemp} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + activeListFeatureId={this.props.activeListFeatureId} + searchLabelData={this.props.searchLabelData} + layerInfo={this.props.layerInfo} + /> + + + + + + { this.renderToolbarButtons() } + + + + + + + + {/*{ + this.state.layerSwitcherVisible && + + }*/} + + { this.state.mapTableModalVisible && + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + refreshMapTable={() => this.refreshMapTable()} + /> + } + + {this.state.drawingToolVisible && + this.toggleAddFeatureVisible()} + setAddFeatureValue={(obj) => this.setAddFeatureValue(obj)} + setDrawingState={(DrawingUtil, drawingLayer, chosenLayer) => this.setState({ + DrawingUtil: DrawingUtil, + drawingLayer: drawingLayer, + chosenLayer: chosenLayer + })} + /> + } + + { this.state.addFeatureVisible && + this.toggleAddFeatureVisible()} + addFeatureValue={this.state.addFeatureValue} + closeDrawing={this.cancelDraw} + finishDrawingAdd={() => this.finishDrawingAdd()} + /> + } + + + + + + {this.state.createNewLayerVisible && + + } + + { this.state.legendVisible && this.toggleLegend()} /> } + + {/* this.state.measureVisible && + + */} + + { this.state.measureVisible && + this.toggleMeasure(type)} + toggleMeasureVisible={() => this.toggleMeasureVisible()} + measureType={this.state.measureType} + measureValue={this.state.measureValue} + /> + } +
+ ) + } +} + +export default MapToolbar; \ No newline at end of file diff --git a/src/components/MapToolbar/package.json b/src/components/MapToolbar/package.json new file mode 100644 index 0000000..139474e --- /dev/null +++ b/src/components/MapToolbar/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapToolbar", + "version": "0.0.0", + "private": true, + "main": "./MapToolbar.js" +} diff --git a/src/components/MapToolbar_backup/MapToolbar.css b/src/components/MapToolbar_backup/MapToolbar.css new file mode 100644 index 0000000..a00a4af --- /dev/null +++ b/src/components/MapToolbar_backup/MapToolbar.css @@ -0,0 +1,180 @@ +:root { + --position-maptool: fixed; + --left-maptool: 13px; + --button-size: 16px; + --active-left-sidebar: 350px; +} + +.maptool-custom { + background-color: rgba(200, 199, 58, 0.65); +} + +.maptool_1 { + position: var(--position-maptool); + top: 60px; + /*top: 130px;*/ + left: var(--left-maptool); +} + +.maptool_2 { + position: var(--position-maptool); + top: 95px; + /*top: 165px;*/ + left: var(--left-maptool); +} + +.maptool_3 { + position: var(--position-maptool); + top: 130px; + /*top: 200px;*/ + left: var(--left-maptool); +} + +.maptool_4 { + position: var(--position-maptool); + top: 165px; + /*top: 235px;*/ + left: var(--left-maptool); +} + +.maptool_5 { + position: var(--position-maptool); + top: 200px; + left: var(--left-maptool); +} + +.maptool_6 { + position: var(--position-maptool); + top: 235px; + left: var(--left-maptool); +} + +.maptool_7 { + position: var(--position-maptool); + top: 270px; + left: var(--left-maptool); +} + +.maptool_8 { + position: var(--position-maptool); + top: 305px; + left: var(--left-maptool); +} + +.maptool_9 { + position: var(--position-maptool); + top: 340px; + left: var(--left-maptool); +} + +.maptool_10 { + position: var(--position-maptool); + top: 375px; + left: var(--left-maptool); +} + +/*#custom-mouse-position { + position: var(--position-maptool); + bottom: 10px !important; + left: var(--left-maptool); +}*/ + +.maptool_btm_1 { + position: var(--position-maptool); + bottom: 10px; + left: var(--left-maptool); +} + +.maptool_btm_2 { + position: var(--position-maptool); + bottom: 45px; + left: var(--left-maptool); +} + +.button-layer-menu { + background-color: #ffffff; + padding: 0px; + margin-top: -10px; + margin-left: 10px; +} + +.layer-tree-wrapper { + margin-top: 10px; + margin-left: -20px; + overflow: auto; + height: 60vh; +} + +.layer-tree-title-wrapper { + /*width: 130px;*/ + display: inline-block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 150px; +} + +.layer-tree-dropdown-wrapper { + /*width: 100%; */ + right: 0; + display: inline-block; + /*z-index: 300;*/ +} + +.layer-tree-badge { + margin-top: -25px; +} + +.ow-wrapper { + /*overflow-wrap: break-word;*/ + word-wrap: break-word; + /*max-width: 200px;*/ +} + +.ow { + overflow-wrap: break-word; + word-wrap: break-word; + max-width: 200px; +} + +.layer-button-group { + +} + +.active-side-left { + margin-left: 300px; +} + +/*.ol-tooltip { + position: relative; + background: rgba(0, 0, 0, 0.5); + border-radius: 4px; + color: white; + padding: 4px 8px; + opacity: 0.7; + white-space: nowrap; + font-size: 12px; + } +.ol-tooltip-measure { + opacity: 1; + font-weight: bold; +} +.ol-tooltip-static { + background-color: #ffcc33; + color: black; + border: 1px solid white; +} +.ol-tooltip-measure:before, +.ol-tooltip-static:before { + border-top: 6px solid rgba(0, 0, 0, 0.5); + border-right: 6px solid transparent; + border-left: 6px solid transparent; + content: ""; + position: absolute; + bottom: -6px; + margin-left: -7px; + left: 50%; +} +.ol-tooltip-static:before { + border-top-color: #ffcc33; +}*/ diff --git a/src/components/MapToolbar_backup/MapToolbar.js b/src/components/MapToolbar_backup/MapToolbar.js new file mode 100644 index 0000000..3892639 --- /dev/null +++ b/src/components/MapToolbar_backup/MapToolbar.js @@ -0,0 +1,2143 @@ +import React, { Component, Fragment, useState } from 'react'; +import { Drawer, Tooltip } from 'antd'; +import { + mappify, + SimpleButton, + AddWmsPanel, + LayerTree, + Panel +} from '@terrestris/react-geo'; +import { + Badge, Dropdown, UncontrolledDropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, + InputGroup, Input, InputGroupAddon, InputGroupText, Button, Collapse, Card, CardBody +} from 'reactstrap'; +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import MeasureButton from '@terrestris/react-geo/dist/Button/MeasureButton/MeasureButton'; +import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import MapTable2 from '../../components/MapTable2'; +import DrawingTool from '../../components/DrawingTool'; +import MapLayerStyles from '../../components/MapLayerStyles'; +import CreateNewLayer from '../../components/CreateNewLayer'; +import SearchFeatures from '../../components/SearchFeatures'; +import AddFeature from '../../components/AddFeature'; +import MapLegend from '../../components/MapLegend'; +import MeasureContainer from '../../components/MeasureContainer'; +import DailyInfo from '../../components/DailyInfo'; +import LayerTreeGeoHR from '../../components/LayerTreeGeoHR'; +// import PopupContainer from '../../components/PopupContainer'; +import { appConfig, wfsDispatcherUrl } from '../../const/MapConst.js'; +// import { API_LAYER_SEARCH_LABEL } from '../../const/ApiConst.js'; +import './MapToolbar.css'; +import { Icon, InlineIcon } from '@iconify/react'; +// import layersIcon from '@iconify/icons-simple-line-icons/layers'; +import layersIcon from '@iconify/icons-ion/layers'; +import mapIcon from '@iconify/icons-ion/map'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import searchPlus from '@iconify/icons-fe/search-plus'; +import searchMinus from '@iconify/icons-fe/search-minus'; +import mapLegend from '@iconify/icons-mdi/map-legend'; +import rulerIcon from '@iconify/icons-mdi/ruler'; +import filterIcon from '@iconify/icons-ion/filter'; +import informationCircleOutline from '@iconify/icons-ion/information-circle-outline'; + +import axios from 'axios'; +import OlLayerGroup from 'ol/layer/Group'; +import OlLayerTile from 'ol/layer/Tile'; +import OlLayerImage from 'ol/layer/Image'; +import ZoomToExtent from 'ol/control/ZoomToExtent'; +import { fromLonLat, transformExtent } from 'ol/proj'; +import { Draw } from 'ol/interaction'; +import { LineString, Polygon } from 'ol/geom'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Vector as VectorSource } from 'ol/source'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { unByKey } from 'ol/Observable'; +import Overlay from 'ol/Overlay'; +import { WMS_CAPABILITIES_URL, legendPicUrl } from '../../const/MapConst.js'; +import { findWhere, flatten, without } from 'underscore'; +import { reqTableData, getGeomType, getTableColumns, getLayerColor, formatLength, formatArea, lightOrDark } from '../../const/GeoserverFunc.js'; +import QueryBuilder from '../QueryBuilder/QueryBuilder.js'; +import '../../assets/css/customscroll.css'; +import { requestTableDailyInfoApi } from '../../const/GeohrApiFunc.js'; + +const MappifiedLayerTree = mappify(LayerTree); + +/* +Toggle Visibility that must be closed or switched to its visibility: + +layer: drawerLayerVisible +popup: popupRightVisible +searchFeature: drawerSearchVisible +basemap: layerSwitcherVisible +legend: legendVisible +drawing: drawingToolVisible +layerStyle: drawerLayerStylesVisible +2: mapTableModalVisible + +*/ + +class MapToolbar extends Component { + + constructor(props) { + super(props); + this.state = { + isReady: false, + wmsLayers: [], + drawerDailyInfo: true, + drawerLayerVisible: true, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + layerSwitcherVisible: false, + layerPanelVisible: false, + mapTableModalVisible: false, + queryBuilderVisible: false, + mapTableName: '', + mapTableTitle: '', + mapTableTitleSrc: '', + mapTableColumns: null, + mapTableData: null, + selectedMapLayer: null, + resTableData: null, + drawingToolVisible: false, + layerNameDraw: '', + geomTypeDraw: '', + drawerLayerStylesVisible: false, + createNewLayerVisible: false, + searchInput: '', + allFeatures: [], + popupRightVisible: false, + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + }, + addFeatureVisible: false, + addFeatureColumns: null, + addFeatureValue: null, + legendVisible: false, + activeSideLeft: false, + DrawingUtil: null, + drawingLayer: null, + chosenLayer: null, + // searchLabelData: [], + btnDropMeasureVisible: false, + measureVisible: false, + measureName: '', + measureType: '', + measureValue: '', + measureDraw: null, + sketch: null, + helpTooltipElement: null, + helpTooltip: null, + measureTooltipElement: null, + measureTooltip: null, + continuePolygonMsg: 'Click to continue drawing the polygon', + continueLineMsg: 'Click to continue drawing the line', + searchDetailField:"name", + search:"", + totalPageBottom:0, + currentPage:1, + rowsPerPage: 10, + page:1, + table_type:"", + totalRecord:0, + } + this.getWMSLayers = this.getWMSLayers.bind(this); + this.toggleDrawerLayer = this.toggleDrawerLayer.bind(this); + this.toggleDrawerSearch = this.toggleDrawerSearch.bind(this); + this.toggleLayerSwitcher = this.toggleLayerSwitcher.bind(this); + // this.toggleLayerPanel = this.toggleLayerPanel.bind(this); + this.cancelWmsLayer = this.cancelWmsLayer.bind(this); + this.customTitleLayerTree = this.customTitleLayerTree.bind(this); + this.onRightClickLayerTree = this.onRightClickLayerTree.bind(this); + this.onDropLayerTree = this.onDropLayerTree.bind(this); + // this.onClickLayerMenu = this.onClickLayerMenu.bind(this); + this.toggleMapTable = this.toggleMapTable.bind(this); + this.toggleMapAnalyzeTool = this.toggleMapAnalyzeTool.bind(this); + // this.toggleDrawingTool = this.toggleDrawingTool.bind(this); + this.cancelDraw = this.cancelDraw.bind(this); + this.toggleLayerStyles = this.toggleLayerStyles.bind(this); + this.openLayerStyles = this.openLayerStyles.bind(this); + this.closeLayerStyles = this.closeLayerStyles.bind(this); + this.toggleCreateNewLayer = this.toggleCreateNewLayer.bind(this); + + this.measureLayer = new VectorLayer({ + name: 'measure_layer', + title: 'Measure Layer', + type: 'vector', + source: new VectorSource(), + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + // lineDash: [10, 10], + width: 3 + }), + image: new CircleStyle({ + radius: 7, + fill: new Fill({ + color: '#ffcc33' + }) + }) + }) + }); + } + + componentDidMount() { + this.setState({ isReady: true, activeSideLeft: true }); + } + + componentDidUpdate(prevProps, prevState) { + if (prevState.mapTableModalVisible !== this.state.mapTableModalVisible) { + if (!this.state.mapTableModalVisible) { + // removing last routeLayer and table data + this.props.removeLayerByName('routeLayer'); + this.setState({ mapTableData: null }); + } + } + } + + getLayerType = (item) => { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_geom_type; + } + else { + return "Undefined Geom Type"; + } + } + + getLayerColor = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log('getLayerColor', layer); + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_color; + } + else { + return "#ffffff"; + } + } + + getLayerTitle = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + let output = ''; + + if (matchLayerName.length > 0) { + if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + // return matchLayerName[0].layer_title; + output = matchLayerName[0].layer_title; + return output; + } + else { + output = matchLayerName[0].layer_name; + return output; + } + } + else { + return "No layer title provided"; + // output = matchLayerName[0].layer_name; + // return matchLayerName[0].layer_name; + } + } + + getLayerTitleTextColor = (layerColor) => { + let brightness = lightOrDark(layerColor); // 'light' or 'dark' + + if (brightness == 'light') { + return 'dark-text'; + } + else { + return 'light-text'; + } + } + + getLayerCount = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log(layer) + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + // if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + // return matchLayerName[0].layer_title; + // } + // else { + // return matchLayerName[0].layer_name; + // } + return matchLayerName[0].total_features; + + } + else { + return "0"; + } + } + + zoomIn = () => { + this.props.olmap.getView().animate({ + zoom: this.props.olmap.getView().getZoom() + 1, + duration: 500 + }); + } + + zoomOut = () => { + this.props.olmap.getView().animate({ + zoom: this.props.olmap.getView().getZoom() - 1, + duration: 500 + }); + } + + getWMSLayers() { + CapabilitiesUtil.parseWmsCapabilities(WMS_CAPABILITIES_URL) + .then(CapabilitiesUtil.getLayersFromWmsCapabilities) + .then(layers => { + console.log('wmsLayers', layers); + this.setState({ + wmsLayers: layers + }); + }) + .catch(() => alert('Could not parse capabilities document.')); + } + + /*toggleDrawerLayer () { + this.closeAllDrawer(); + // this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}); + // this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}, () => this.closeAllDrawer()); + // this.closeAllDrawer(); + const { drawerLayerVisible } = this.state; + if (drawerLayerVisible) { + this.setState({ drawerLayerVisible: false }, () => this.closeAllDrawer() ); + } + else { + this.setState({ drawerLayerVisible: true }, () => this.closeAllDrawer() ); + } + }*/ + + /*toggleDrawerLayer () { + this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }) + }*/ + + toggleDrawerLayer() { + let { drawerLayerVisible } = this.state; + if (drawerLayerVisible) { + this.setState({ drawerLayerVisible: false }, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({ drawerLayerVisible: true }, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }) + } + } + + /*toggleDrawerSearch () { + this.closeAllDrawer(); + const { drawerSearchVisible } = this.state; + console.log('drawerSearchVisible', drawerSearchVisible); + if (drawerSearchVisible) { + this.setState({drawerSearchVisible: false}, () => this.closeAllDrawer() ); + } + else { + this.setState({drawerSearchVisible: true}, () => { + this.getFeaturesOnMap(); + this.closeAllDrawer(); + }); + } + }*/ + + toggleDrawerSearch() { + const { drawerSearchVisible } = this.state; + + if (drawerSearchVisible) { + this.setState({ drawerSearchVisible: false }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + // drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }) + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({ drawerSearchVisible: true }, () => { + this.getFeaturesOnMap(); + // this.getLayerSearchLabel(); + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + // drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + } + + toggleMapAnalyzeTool() { + // this.setState({drawerAnalyzeToolVisible: !this.state.drawerAnalyzeToolVisible}); + const { drawerAnalyzeToolVisible } = this.state; + + if (drawerAnalyzeToolVisible) { + this.setState({ drawerAnalyzeToolVisible: false }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + // drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }) + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({ drawerAnalyzeToolVisible: true }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + // drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true, + drawerDailyInfo: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + } + + // toggle Base Map + toggleLayerSwitcher() { + this.setState({ layerSwitcherVisible: !this.state.layerSwitcherVisible }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + // layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleMapTable() { + this.setState({ mapTableModalVisible: !this.state.mapTableModalVisible }, () => { + this.setState({ + // drawerLayerVisible: false, + drawerLayerStylesVisible: false, + // mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + // activeSideLeft: false + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleCreateNewLayer() { + this.setState({ createNewLayerVisible: !this.state.createNewLayerVisible }); + this.deactiveMeasureMap(); + } + + toggleLayerStyles() { + this.setState({ drawerLayerStylesVisible: !this.state.drawerLayerStylesVisible }, () => { + this.setState({ + drawerLayerVisible: false, + // drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleAddFeatureVisible = () => { + this.setState({ addFeatureVisible: !this.state.addFeatureVisible }); + this.deactiveMeasureMap(); + } + + toggleLegend = () => { + this.setState({ legendVisible: !this.state.legendVisible }, () => { + if (this.state.drawerLayerVisible) { + this.setState({ + // drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + // legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + // activeSideLeft: false, + drawerDailyInfo: false + }); + } + else { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + // legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }); + } + + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleMeasureVisible = () => { + // this.setState({measureVisible: !this.state.measureVisible}); + this.setState({ measureVisible: !this.state.measureVisible }, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + // measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + drawerDailyInfo: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + + if (!this.state.measureVisible) { + this.deactiveMeasureMap(); + } + }); + } + + toggleDrawerDailyInfo = () => { + // this.setState({measureVisible: !this.state.measureVisible}); + this.setState({ drawerDailyInfo: !this.state.drawerDailyInfo }, () => { + if (this.state.drawerLayerVisible) { + this.setState({ + // drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + // activeSideLeft: false, + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + } + else { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false, + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + } + }); + } + + closeAllDrawer = () => { + /* + Toggle Visibility that must be closed or switched to its visibility: + + layer: drawerLayerVisible + popup: popupRightVisible + searchFeature: drawerSearchVisible + basemap: layerSwitcherVisible + legend: legendVisible + drawing: drawingToolVisible + layerStyle: drawerLayerStylesVisible + mapTable: mapTableModalVisible + + Group layers: layer, layerStyle, mapTable (that shouldn't be closed each other) + Group popup: searchFeature, popup (that shouldn't be closed each other) => searchFeature below of popup + Legend + BaseMap + Drawing + */ + + const { drawerLayerVisible, drawerLayerStylesVisible, mapTableModalVisible, + drawerSearchVisible, popupRightVisible, + legendVisible, + layerSwitcherVisible, + drawingToolVisible + } = this.state; + + let visibleArr = [drawerLayerVisible, drawerLayerStylesVisible, mapTableModalVisible, + drawerSearchVisible, popupRightVisible, + legendVisible, + layerSwitcherVisible, + drawingToolVisible + ]; + + console.log('visibleArr', visibleArr); + + if (drawerLayerVisible) { + this.setState({ + drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (drawerLayerStylesVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: true, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (mapTableModalVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: true, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (drawerSearchVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: true, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (popupRightVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: true, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (legendVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: true, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (layerSwitcherVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: true, + drawingToolVisible: false + }) + } + + if (drawingToolVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: true + }) + } + + } + + // toggleLayerPanel(layer) { + // console.log('toggleLayerPanel', layer); + // this.setState({layerPanelVisible: !this.state.layerPanelVisible}); + // } + + cancelWmsLayer() { + console.log('cancel wms'); + this.setState({ wmsLayers: [] }); + } + + onClickLayerMenu = (layer) => { + // console.log('onclick layer menu', layer); + } + + customTitleLayerTree(layer) { + // console.log('customTitleLayerTree', layer.get('title')); + // console.log('customTitleLayerTree', layer.get('type')); + // return ( + //
{layer.get('name')}
+ // ) + + // if (layer instanceof OlLayerGroup) { + + // } + + // return ( + //
+ //
+ // {layer.get('name')} + //
+ // {/*
+ // + //
*/} + //
+ // + // + // + // + // + // {layer.get('name')} + // this.showTableData(layer)}> Show Table42 + // Remove Layer + // + // + //
+ //
+ // ) + + // if (layer.get('type') == 'layer') { + return ( +
+
+ {layer.get('title') ? layer.get('title') : layer.get('name')} +
+ {/*
+ +
*/} +
+ + {/*10*/} + {localStorage.getItem('u_group') !== 'kominfo' ? + layer.get('type') == 'layer' ? + {this.getLayerCount(layer)} + : null + : layer.get('type') == 'layer' ? + + : null + } + this.onClickLayerMenu(layer)}> + + + + +
+ {layer.get('title') ? layer.get('title') : layer.get('name')} +
+
+ { + layer.get('type') === 'layer' || layer instanceof OlLayerImage ? + // layer instanceof OlLayerTile || layer instanceof OlLayerImage ? + + + this.zoomToLayer(layer)}> Zoom To Layer + this.showTableData(layer)}> Show Data + {this.props.editGeometryVisible ? + Add Feature + : this.addFeature(layer)}> Add Feature + } + {/* this.addFeature(layer)}> Add Feature*/} + this.styleLayer(layer)}> Layer Styles + + : + null} + this.removeLayer(layer)}> Remove Layer +
+
+
+
+ ) + // } + // else { + // return null; + // } + } + + onRightClickLayerTree(layer) { + console.log('right click layer tree', layer); + return ( + + + Header + Action + Another Action + + Another Action + + + ) + } + + onDropLayerTree() { + console.log('dropped'); + } + + + + /*showTableData = (layer) => { + console.log('showTableData', layer); + this.setState({ + mapTableTitle: layer.get('name'), + mapTableTitleSrc: layer.getSource().params_.LAYERS + }, async () => { + // console.log('mapTableTitle', this.state.mapTableTitle); + // console.log('mapTableTitleSrc', this.state.mapTableTitleSrc) + // let req = this.requestTableData(this.state.mapTableTitleSrc); + let res = await reqTableData(this.state.mapTableTitle); + console.log('showTableData',res); + // if (res !== undefined) { + // this.setState({resTableData: res}); + // } + + if (res.success) { + this.setState({resTableData: res.result}, () => this.toggleMapTable()); + } + else { + alert(res.result); + return; + } + }); + }*/ + + showTableData = (layer) => { + this.setState({ + mapTableTitle: layer.get('title'), + mapTableName: layer.get('name') + // mapTableTitleSrc: layer.getSource().params_.LAYERS + }, () => this.toggleMapTable()); + } + + refreshMapTable = async () => { + const { mapTableTitle, mapTableName } = this.state; + let res = await reqTableData(mapTableName); + if (res.success) { + this.setState({ resTableData: res.result }); + } + else { + alert(res.result); + return; + } + } + + styleLayer = async (layer) => { + console.log('styling layer...', layer); + + let SLD_URL = layer.getSource().params_.SLD; + console.log('SLD_URL', SLD_URL); + + let resGetLayerColor = await getLayerColor(SLD_URL); + + console.log('styleLayer resGetLayerColor', resGetLayerColor); + + // this.toggleLayerStyles(); + + if (this.state.drawerLayerStylesVisible) { + this.closeLayerStyles(); + this.openLayerStyles(); + } + else { + this.openLayerStyles(); + } + /* + axios.get(SLD_URL) + .then(res => { + console.log('axios get SLD', res); + let text = res.data; + let parser, xmlDoc; + // let self = this; + // parseString(res.data, function (err, result) { + // console.log(result); + // self.events = result; + // }); + if (window.DOMParser) { + // code for modern browsers + parser = new DOMParser(); + xmlDoc = parser.parseFromString(text,"text/xml"); + + console.log('xmlDoc', xmlDoc); + } + // else { + // // code for old IE browsers + // xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + // xmlDoc.async = false; + // xmlDoc.loadXML(text); + // console.log('xmlDoc', xmlDoc); + // } + }) + .catch(err => { + console.log('axios get SLD err', err); + }); + + if (this.state.drawerLayerStylesVisible) { + this.closeLayerStyles(); + this.openLayerStyles(); + } + else { + this.openLayerStyles(); + }*/ + } + + openLayerStyles() { + this.setState({ drawerLayerStylesVisible: true }); + } + + closeLayerStyles() { + this.setState({ drawerLayerStylesVisible: false }); + } + + addFeature = async (layer) => { + console.log('adding feature on layer...', layer); + // console.log(layer.get('name')); + + let geomType = ''; + // request Geom Type from API + let reqGeomType = await getGeomType(layer.get('name')); + console.log('addFeature reqGeomType', reqGeomType) + if (reqGeomType.success) { + geomType = reqGeomType.result; + } + else { + geomType = ''; + alert(reqGeomType.result); + return; + } + + // request the table columns from API + // let featureColumns = await getTableColumns(layer.get('name')); + // console.log('addFeature featureColumns', featureColumns); + + let reqTableColumns = await getTableColumns(layer.get('name')); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({ addFeatureColumns: reqTableColumns.result }); + } + else { + alert(reqTableColumns.result); + // return; + } + + + this.setState({ + drawingToolVisible: true, + layerNameDraw: layer.get('name'), + geomTypeDraw: geomType, + // addFeatureColumns: reqTableColumns.result + }); + this.props.toggleActiveStateAddGeometry(); + + } + + cancelDraw() { + this.setState({ + drawingToolVisible: false, + layerNameDraw: '', + geomTypeDraw: '' + }); + this.props.toggleActiveStateAddGeometry(); + } + + removeLayer = (layer) => { + console.log('removing layer', layer); + let confirmation = window.confirm('Are you sure you want to remove this layer?'); + if (!confirmation) { + return; + } + this.props.olmap.removeLayer(layer); + } + + /*zoomToLayer = (layer) => { + console.log('zoomToLayer', layer); + let layerExtent = null; + let newLayerExtent = null; + let transformLayerExtent = null; + let fitOption = { + size: this.props.olmap.getSize(), + duration: 500 + } + // layer.getSource().getExtent(); + // layer.getExtent(); + // layer.getSource().getExtent(); + if (layer instanceof OlLayerImage) { + // console.log(layer.getSource().image_.extent); + layerExtent = layer.getSource().image_.extent; + transformLayerExtent = new transformExtent(layerExtent, 'EPSG:4326', this.props.olmap.getView().getProjection()) + // transformLayerExtent = new transformExtent(layerExtent, this.props.olmap.getView().getProjection(), 'EPSG:4326') + console.log('layerExtent', layerExtent); + console.log('transformLayerExtent', transformLayerExtent); + newLayerExtent = [ + layerExtent[0], layerExtent[2], layerExtent[1], layerExtent[3] + ]; + console.log('newLayerExtent', newLayerExtent); + + // this.props.olmap.getView().fit(new transformExtent(layerExtent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + this.props.olmap.getView().fit(layerExtent, fitOption); + + // new ZoomToExtent() + } + else if (layer instanceof OlLayerTile) { + layerExtent = layer.getSource().tmpExtent_; + // console.log(layer.getSource()); + this.props.olmap.getView().fit(layerExtent, fitOption); + } + // console.log(layer.getSource().getExtent()); + }*/ + + zoomToLayer = (layer) => { + console.log('zoomToLayer getSource', layer.getSource()); + // let layerName = layer.getSource().params_.LAYERS; + let layerName = layer.get('name'); + console.log('layerName', layerName); + const { fitOption } = this.state; + + // if (layer instanceof OlLayerImage) { + // // layerName = layer.getSource(); + + // } + // else if (layer instanceof OlLayerTile) { + + // } + CapabilitiesUtil.parseWmsCapabilities(WMS_CAPABILITIES_URL).then(response => { + console.log('capabilities response', response); + if (response !== undefined) { + let layer = findWhere(response.Capability.Layer.Layer, { "Name": layerName }); + console.log('layer', layer); + + if (layer === undefined) { + alert('Layer ' + layerName + ' is not found'); + return; + } + + let proj = "CRS:84"; + let bbox = findWhere(layer.BoundingBox, { "crs": proj }); + let extent = bbox.extent; + console.log('layer', layer); + console.log('zoomToLayer extent', extent); + // map.getView().fit(new transformExtent(layerExtent, 'EPSG:4326', map.getView().getProjection()), { size: map.getSize() }); + + // console.log('layer', layer); + // this.props.olmap.getView().fit(extent, fitOption); + // console.log('getProjection', this.props.olmap.getView().getProjection()); + // var boundingExtent = ol.extent.boundingExtent(coordinates); + // boundingExtent = ol.proj.transformExtent(boundingExtent, ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857')); + + /*// other option + extent = new transformExtent(extent, "EPSG:4326", "EPSG:3857"); + console.log('extent now', extent); + this.props.olmap.getView().fit(extent, fitOption); */ + + this.props.olmap.getView().fit(new transformExtent(extent, proj, this.props.olmap.getView().getProjection()), fitOption); + + } + }) + } + + onDragEndLayerTree = (callback) => { + console.log('onDragEndLayerTree', callback); + } + + rebuildTreeNodes = (evt) => { + console.log('rebuildTreeNodes', evt); + } + + // to be populated on search feature menu + getFeaturesOnMap = async () => { + let layerSource = ''; + let layerTypeName = ''; + let layerSourceArr = []; + let layerTypeNameArr = []; + let layerNameArr = []; + let featureCollections = []; + let allFeatures = []; + this.props.olmap.getLayers().forEach((layer, i) => { + // console.log('layer', layer); + // console.log('layer type', layer.get('type')); + if (layer.get('type' !== 'base')) { + if (layer.get('type') !== 'vector') { + if (layer.get('type') !== 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + // get the features + layerSource = sublayer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(sublayer.get('name')); + } + }); + } + else { + if (layer.getVisible()) { + // get the features + layerSource = layer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(layer.get('name')); + } + } + } + } + if (layer.type === "IMAGE") { + if (layer.getVisible()) { + // get the features + layerSource = layer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(layer.get('name')); + } + + } + }) + + // console.log('layerSourceArr', layerSourceArr); + console.log('layerTypeNameArr', layerTypeNameArr); + console.log('layerNameArr', layerNameArr); + + for (let i = 0; i < layerTypeNameArr.length; i++) { + let req = await reqTableData(layerTypeNameArr[i]); + // console.log(typeof(res)); + + if (req.success) { + featureCollections.push(req.result); + if (req.result.features.length > 0) { + allFeatures.push(req.result.features); + } + } + else { + alert(req.result); + return; + } + + } + + allFeatures = flatten(allFeatures); + + this.setState({ allFeatures: allFeatures }); + + } + + /*getLayerSearchLabel = async() => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + console.log('getLayerSearchLabel result', result); + this.setState({ + searchLabelData: result.data + }) + } + } else { + + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + } + }*/ + + setAddFeatureValue = (obj) => { + this.setState({ addFeatureValue: obj }) + } + + renderToolbarButtons = () => { + const { activeSideLeft } = this.state + let olMousePosition = document.getElementsByClassName("ol-mouse-position")[0]; + let olScaleLine = document.getElementsByClassName("ol-scale-line")[0]; + + if (activeSideLeft) { + olMousePosition.classList.add("active-side-left"); + olScaleLine.classList.add("active-side-left"); + } + else { + olMousePosition.classList.remove("active-side-left"); + olScaleLine.classList.remove("active-side-left"); + } + + return ( + + this.zoomIn()} + > + + + + this.zoomOut()} + > + + + + + + + + + + this.toggleDrawerDailyInfo()} + > + + + + {/* + + this.mapMeasurement()} + > + + + + + +
+ Map Measurement +
+
+ Remove Layer +
+
*/} + + + {/* this.toggleBtnDropMeasure()}> + + + + + + + +
Map Measurement
+
+ this.toggleMeasure('LineString')}> + Distance + + this.toggleMeasure('Polygon')}> + Area + +
+
*/} + + this.toggleMeasureVisible()} + > + + + + + {/**/} + + this.toggleLegend()} + > + + + + + + + + + + this.toggleQueryBuilder()} + > + + + + {/*
+ +
*/} +
+ ) + } + + finishDrawingAdd = () => { + // const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + + // Set Drawing type to null and active is false + DrawingUtil.setActive(null, false); + DrawingUtil.destroy(); + + // Clear the drawing drawingLayer in drawingLayer layer + if (drawingLayer) { + drawingLayer.getSource().clear(); + } + if (chosenLayer) { + chosenLayer.getSource().clear(); + } + + // Removing DrawingLayer and ChosenLayer layer + let layersToRemove = []; + this.props.olmap.getLayers().forEach(function (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'DrawingLayer') { + layersToRemove.push(layer); + } + if (layer.get('name') !== undefined && layer.get('name') == 'ChosenLayer') { + layersToRemove.push(layer); + } + }); + for (var i = 0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + + // // Set snapping mode to false + // this.setState({snapMode: false}); + + // // Calling props cancelDraw in MapToolbar.js + // this.props.cancelDraw(); + } + + mapMeasurement = () => { + // console.log() + /*return ( + + + + + + +
+ Map Measurement +
+
+ this.removeLayer(layer)}> Remove Layer +
+
+ )*/ + } + + toggleBtnDropMeasure = () => { + this.setState({ btnDropMeasureVisible: !this.state.btnDropMeasureVisible }, () => { + if (!this.state.btnDropMeasureVisible) { + this.setState({ + // measureVisible: false, + measureName: '', + measureType: '' + }); + } + }); + } + + // toggleBt + + /*toggleMeasure = (name, type) => { + this.setState({ + measureVisible: true, + measureName: name, + measureType: type + }); + }*/ + + pointerMoveHandler = (evt) => { + let { sketch, helpTooltipElement, helpTooltip, continuePolygonMsg, continueLineMsg } = this.state; + if (evt.dragging) { + return; + } + /** @type {string} */ + var helpMsg = 'Click to start drawing'; + + if (sketch) { + var geom = sketch.getGeometry(); + if (geom instanceof Polygon) { + helpMsg = continuePolygonMsg; + } else if (geom instanceof LineString) { + helpMsg = continueLineMsg; + } + } + + // helpTooltipElement.innerHTML = helpMsg; + helpTooltip.setPosition(evt.coordinate); + + helpTooltipElement.classList.remove('hidden'); + }; + + restartMeasureMap = () => { + let { olmap } = this.props; + let { measureType, measureDraw } = this.state; + + // empty measureValue + this.setState({ measureValue: '' }); + + // clear the layer source of measureLayer + this.measureLayer.getSource().clear(); + + // remove interaction of measureDraw + olmap.removeInteraction(measureDraw); + } + + deactiveMeasureMap = () => { + const { olmap } = this.props; + this.restartMeasureMap(); + this.setState({ measureType: '' }); + + // removing measure_layer + olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') == 'measure_layer') { + olmap.removeLayer(layer); + } + }) + } + + toggleMeasure = (type) => { + this.setState({ + measureType: type + }, () => { + let { olmap } = this.props; + let { measureType, measureDraw } = this.state; + + this.restartMeasureMap(); + + // olmap.on('pointermove', this.pointerMoveHandler.bind(this)); + + // removing measure_layer first + olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') == 'measure_layer') { + olmap.removeLayer(layer); + } + }) + + // then recreate new measure_layer (in case if user changing measure type) + olmap.addLayer(this.measureLayer); + + this.addInteractionMeasure(measureType); + }) + } + + addInteractionMeasure = (measureType) => { + // let type = (typeSelect.value == 'area' ? 'Polygon' : 'LineString'); + let { olmap } = this.props; + let { measureDraw, sketch } = this.state; + let measure = new Draw({ + source: this.measureLayer.getSource(), + type: measureType, + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + lineDash: [10, 10], + width: 3 + }), + image: new CircleStyle({ + radius: 5, + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.7)' + }), + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.2)' + }) + }) + }) + }); + this.setState({ measureDraw: measure }, () => { + let { measureDraw, measureTooltipElement, measureTooltip } = this.state; + let { olmap } = this.props; + olmap.addInteraction(measureDraw); + + + // createMeasureTooltip(); + // createHelpTooltip(); + + + + var listener; + measureDraw.on('drawstart', + (evt) => { + + // clear the layer source of measureLayer + this.measureLayer.getSource().clear(); + + // set sketch + sketch = evt.feature; + + /** @type {import("../src/ol/coordinate.js").Coordinate|undefined} */ + var tooltipCoord = evt.coordinate; + + listener = sketch.getGeometry().on('change', (evt) => { + var geom = evt.target; + var output; + if (geom instanceof Polygon) { + // console.log('geom Polygon',geom); + output = formatArea(geom); + tooltipCoord = geom.getInteriorPoint().getCoordinates(); + console.log('tooltipCoord', tooltipCoord); + // console.log('output Polygon', output); + this.setState({ measureValue: output }); + } else if (geom instanceof LineString) { + output = formatLength(geom); + tooltipCoord = geom.getLastCoordinate(); + // console.log('tooltipCoord', tooltipCoord); + // console.log('output LineString', output); + this.setState({ measureValue: output }); + } + // measureTooltipElement.innerHTML = output; + // measureTooltip.setPosition(tooltipCoord); + }); + }); + + measureDraw.on('drawend', + () => { + // measureTooltipElement.className = 'ol-tooltip ol-tooltip-static'; + // measureTooltip.setOffset([0, -7]); + // unset sketch + sketch = null; + // unset tooltip so that a new one can be created + measureTooltipElement = null; + // this.createMeasureTooltip(); + // olmap.removeInteraction(measureDraw); + + unByKey(listener); + }); + }); + } + + /** + * Creates a new measure tooltip + */ + createMeasureTooltip = () => { + let { measureTooltipElement, measureTooltip } = this.state; + let { olmap } = this.props; + if (measureTooltipElement) { + measureTooltipElement.parentNode.removeChild(measureTooltipElement); + } + measureTooltipElement = document.createElement('div'); + measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure'; + measureTooltip = new Overlay({ + element: measureTooltipElement, + offset: [0, -15], + positioning: 'bottom-center' + }); + olmap.addOverlay(measureTooltip); + } + + /*renderMeasureContent = () => { + const { measureVisible } = this.state; + console.log('measureVisible', measureVisible); + alert('measureVisible '+measureVisible); + return ( +
+
+ Helloooooooo +
+
+ ) + }*/ + + mappifiedLayerTree = () => { + const { olmap } = this.props; + let layers = []; + + olmap.getLayers().forEach((layer, i) => { + if (layer.get('type') !== 'base') { + if (layer.get('type') !== 'vector') { + if (layer.get('type') == 'layerGroup') { + // layer.getLayers().forEach((sublayer, i) => { + // if (sublayer.getVisible()) { + // url = sublayer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + // if (url) { + // promises.push(axios.get(url)); + // } + + // } + // }); + // layers.push(layer); + } + else { + // if (layer.getVisible()) { + // url = layer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + // if (url) { + // promises.push(axios.get(url)); + // } + // } + layers.push(layer); + } + } + } + }); + + + } + + toggleQueryBuilder = () => { + this.setState({ queryBuilderVisible: !this.state.queryBuilderVisible }) + } + + handleQbClose = (query, type, tree) => { + this.toggleQueryBuilder(); + this.props.handleQueryBuilder(query, type, tree) + } + + handleQbReset = (query, type) => { + this.setState({ queryBuilderVisible: false }) + this.props.handleQueryBuilder(query, type, '') + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataTableDetail(); + }) + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataTableDetail(); + }) + } + + handleSearchtype = (field) => { + this.setState({ searchDetailField:field }); + } + + handleSearch = (val) => { + this.setState({ search:val }); + this.getDataTableDetail(); + } + + setTableType = (type) => { + this.setState({table_type:type, search:""},()=> { + this.getDataTableDetail(); + }) + } + + getDataTableDetail = async () => { + const { + searchDetailField, + search, + currentPage, + rowsPerPage + } = this.state + + let param = { + table_type:this.state.table_type, + currentPage, + rowsPerPage, + searchDetailField, + search, + } + let dataRes = await requestTableDailyInfoApi(param); + this.setState({ mapTableData:dataRes.data, totalRecord:dataRes.total_record}); + } + + render() { + const { isReady } = this.state; + if (!isReady) { + return null; + } + return ( + + + this.props.onCheckOpt(state, checkedKeys)} + checkedKeysSales={this.props.checkedKeysSales} + checkedKeysCustomer={this.props.checkedKeysCustomer} + checkedKeysOffice={this.props.checkedKeysOffice} + checkedKeysDemografi={this.props.checkedKeysDemografi} + checkedKeysAnalisa={this.props.checkedKeysAnalisa} + checkedKeysEmployeeDivision={this.props.checkedKeysEmployeeDivision} + salesGroupTree={this.props.salesGroupTree} + employeeDivisionTree={this.props.employeeDivisionTree} + setSalesGroupTree={(data) => this.props.setSalesGroupTree(data)} + setEmployeeDivisionTree={(data) => this.props.setEmployeeDivisionTree(data)} + projectTree={this.props.projectTree} + setProjectTree={(data) => this.props.setProjectTree(data)} + checkedKeysProjectTree={this.props.checkedKeysProjectTree} + /> + + + this.toggleDrawerDailyInfo()} + visible={this.state.drawerDailyInfo} + mask={false} + width={250} + style={{ top: '50px', height: 'calc(100vh - 50px)' }} + > + this.setState({ mapTableTitle: name })} + setMapTableColumns={(data) => this.setState({ mapTableColumns: data })} + setMapTableData={(data) => this.setState({ mapTableData: data })} + removeLayerByName={(layerName) => this.props.removeLayerByName(layerName)} + setIsProcessing={(data) => this.props.setIsProcessing(data)} + getDataTableDetail={this.getDataTableDetail} + setTableType={this.setTableType} + /> + + + + this.props.openPopupRight()} + popupDataTemp={this.props.popupDataTemp} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + activeListFeatureId={this.props.activeListFeatureId} + searchLabelData={this.props.searchLabelData} + layerInfo={this.props.layerInfo} + /> + + + + + + {this.renderToolbarButtons()} + + + + + + + + {/*{ + this.state.layerSwitcherVisible && + + }*/} + + {/* { this.state.mapTableModalVisible && + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + refreshMapTable={() => this.refreshMapTable()} + /> + } */} + + {this.state.mapTableModalVisible && + this.setState({ mapTableData: data })} + olmap={this.props.olmap} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + refreshMapTable={() => this.refreshMapTable()} + showRoute={(route) => this.props.showRoute(route)} + removeLayerByName={(layerName) => this.props.removeLayerByName(layerName)} + setIsProcessing={(data) => this.props.setIsProcessing(data)} + onShowSizeChange={this.onShowSizeChange} + onPagination={this.onPagination} + currentPage={this.state.currentPage} + rowsPerPage={this.state.rowsPerPage} + total={this.state.totalRecord} + handleSearch={this.handleSearch} + search={this.state.search} + /> + } + + {this.state.drawingToolVisible && + this.toggleAddFeatureVisible()} + setAddFeatureValue={(obj) => this.setAddFeatureValue(obj)} + setDrawingState={(DrawingUtil, drawingLayer, chosenLayer) => this.setState({ + DrawingUtil: DrawingUtil, + drawingLayer: drawingLayer, + chosenLayer: chosenLayer + })} + /> + } + + {this.state.addFeatureVisible && + this.toggleAddFeatureVisible()} + addFeatureValue={this.state.addFeatureValue} + closeDrawing={this.cancelDraw} + finishDrawingAdd={() => this.finishDrawingAdd()} + /> + } + + + + + + {this.state.createNewLayerVisible && + + } + + {this.state.legendVisible && this.toggleLegend()} />} + + {/* this.state.measureVisible && + + */} + + {this.state.queryBuilderVisible && + + } + + {this.state.measureVisible && + this.toggleMeasure(type)} + toggleMeasureVisible={() => this.toggleMeasureVisible()} + measureType={this.state.measureType} + measureValue={this.state.measureValue} + /> + } + + ) + } +} + +export default MapToolbar; \ No newline at end of file diff --git a/src/components/MapToolbar_backup/MapToolbar_backup.js b/src/components/MapToolbar_backup/MapToolbar_backup.js new file mode 100644 index 0000000..aa37549 --- /dev/null +++ b/src/components/MapToolbar_backup/MapToolbar_backup.js @@ -0,0 +1,1869 @@ +import React, { Component, Fragment, useState } from 'react'; +import { Drawer, Tooltip } from 'antd'; +import { + mappify, + SimpleButton, + AddWmsPanel, + LayerTree, + Panel +} from '@terrestris/react-geo'; +import { Badge, Dropdown, UncontrolledDropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, + InputGroup, Input, InputGroupAddon, InputGroupText, Button, Collapse, Card, CardBody +} from 'reactstrap'; +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import MeasureButton from '@terrestris/react-geo/dist/Button/MeasureButton/MeasureButton'; +import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import MapTable from '../../components/MapTable'; +import DrawingTool from '../../components/DrawingTool'; +import MapLayerStyles from '../../components/MapLayerStyles'; +import CreateNewLayer from '../../components/CreateNewLayer'; +import SearchFeatures from '../../components/SearchFeatures'; +import AddFeature from '../../components/AddFeature'; +import MapLegend from '../../components/MapLegend'; +import MeasureContainer from '../../components/MeasureContainer'; +// import PopupContainer from '../../components/PopupContainer'; +import { appConfig, wfsDispatcherUrl } from '../../const/MapConst.js'; +// import { API_LAYER_SEARCH_LABEL } from '../../const/ApiConst.js'; +import './MapToolbar.css'; +import { Icon, InlineIcon } from '@iconify/react'; +// import layersIcon from '@iconify/icons-simple-line-icons/layers'; +import layersIcon from '@iconify/icons-ion/layers'; +import mapIcon from '@iconify/icons-ion/map'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import searchPlus from '@iconify/icons-fe/search-plus'; +import searchMinus from '@iconify/icons-fe/search-minus'; +import mapLegend from '@iconify/icons-mdi/map-legend'; +import rulerIcon from '@iconify/icons-mdi/ruler'; +import axios from 'axios'; +import OlLayerGroup from 'ol/layer/Group'; +import OlLayerTile from 'ol/layer/Tile'; +import OlLayerImage from 'ol/layer/Image'; +import ZoomToExtent from 'ol/control/ZoomToExtent'; +import {fromLonLat, transformExtent} from 'ol/proj'; +import { Draw } from 'ol/interaction'; +import {LineString, Polygon} from 'ol/geom'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Vector as VectorSource} from 'ol/source'; +import {Circle as CircleStyle, Fill, Stroke, Style, Text} from 'ol/style'; +import {unByKey} from 'ol/Observable'; +import Overlay from 'ol/Overlay'; +import { WMS_CAPABILITIES_URL, legendPicUrl } from '../../const/MapConst.js'; +import { findWhere, flatten, without } from 'underscore'; +import { reqTableData, getGeomType, getTableColumns, getLayerColor, formatLength, formatArea, lightOrDark } from '../../const/GeoserverFunc.js'; +import '../../assets/css/customscroll.css'; + +const MappifiedLayerTree = mappify(LayerTree); + +/* +Toggle Visibility that must be closed or switched to its visibility: + +layer: drawerLayerVisible +popup: popupRightVisible +searchFeature: drawerSearchVisible +basemap: layerSwitcherVisible +legend: legendVisible +drawing: drawingToolVisible +layerStyle: drawerLayerStylesVisible +mapTable: mapTableModalVisible + +*/ + +class MapToolbar extends Component { + + constructor(props) { + super(props); + this.state = { + isReady: false, + wmsLayers: [], + drawerLayerVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + layerSwitcherVisible: false, + layerPanelVisible: false, + mapTableModalVisible: false, + mapTableName: '', + mapTableTitle: '', + mapTableTitleSrc: '', + selectedMapLayer: null, + resTableData: null, + drawingToolVisible: false, + layerNameDraw: '', + geomTypeDraw: '', + drawerLayerStylesVisible: false, + createNewLayerVisible: false, + searchInput: '', + allFeatures: [], + popupRightVisible: false, + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + }, + addFeatureVisible: false, + addFeatureColumns: null, + addFeatureValue: null, + legendVisible: false, + activeSideLeft: false, + DrawingUtil: null, + drawingLayer: null, + chosenLayer: null, + // searchLabelData: [], + btnDropMeasureVisible: false, + measureVisible: false, + measureName: '', + measureType: '', + measureValue: '', + measureDraw: null, + sketch: null, + helpTooltipElement: null, + helpTooltip: null, + measureTooltipElement: null, + measureTooltip: null, + continuePolygonMsg: 'Click to continue drawing the polygon', + continueLineMsg: 'Click to continue drawing the line', + + + } + this.getWMSLayers = this.getWMSLayers.bind(this); + this.toggleDrawerLayer = this.toggleDrawerLayer.bind(this); + this.toggleDrawerSearch = this.toggleDrawerSearch.bind(this); + this.toggleLayerSwitcher = this.toggleLayerSwitcher.bind(this); + // this.toggleLayerPanel = this.toggleLayerPanel.bind(this); + this.cancelWmsLayer = this.cancelWmsLayer.bind(this); + this.customTitleLayerTree = this.customTitleLayerTree.bind(this); + this.onRightClickLayerTree = this.onRightClickLayerTree.bind(this); + this.onDropLayerTree = this.onDropLayerTree.bind(this); + // this.onClickLayerMenu = this.onClickLayerMenu.bind(this); + this.toggleMapTable = this.toggleMapTable.bind(this); + this.toggleMapAnalyzeTool = this.toggleMapAnalyzeTool.bind(this); + // this.toggleDrawingTool = this.toggleDrawingTool.bind(this); + this.cancelDraw = this.cancelDraw.bind(this); + this.toggleLayerStyles = this.toggleLayerStyles.bind(this); + this.openLayerStyles = this.openLayerStyles.bind(this); + this.closeLayerStyles = this.closeLayerStyles.bind(this); + this.toggleCreateNewLayer = this.toggleCreateNewLayer.bind(this); + + this.measureLayer = new VectorLayer({ + name: 'measure_layer', + title: 'Measure Layer', + type: 'vector', + source: new VectorSource(), + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + // lineDash: [10, 10], + width: 3 + }), + image: new CircleStyle({ + radius: 7, + fill: new Fill({ + color: '#ffcc33' + }) + }) + }) + }); + } + + componentDidMount() { + this.setState({isReady: true}); + } + + getLayerType = (item) => { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_geom_type; + } + else { + return "Undefined Geom Type"; + } + } + + getLayerColor = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log('getLayerColor', layer); + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_color; + } + else { + return "#ffffff"; + } + } + + getLayerTitle = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + let output = ''; + + if (matchLayerName.length > 0) { + if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + // return matchLayerName[0].layer_title; + output = matchLayerName[0].layer_title; + return output; + } + else { + output = matchLayerName[0].layer_name; + return output; + } + } + else { + return "No layer title provided"; + // output = matchLayerName[0].layer_name; + // return matchLayerName[0].layer_name; + } + } + + getLayerTitleTextColor = (layerColor) => { + let brightness = lightOrDark(layerColor); // 'light' or 'dark' + + if (brightness == 'light') { + return 'dark-text'; + } + else { + return 'light-text'; + } + } + + getLayerCount = (layer) => { + // let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log(layer) + let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + // if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + // return matchLayerName[0].layer_title; + // } + // else { + // return matchLayerName[0].layer_name; + // } + return matchLayerName[0].total_features; + + } + else { + return "0"; + } + } + + zoomIn = () => { + this.props.olmap.getView().animate({ + zoom: this.props.olmap.getView().getZoom() + 1, + duration: 500 + }); + } + + zoomOut = () => { + this.props.olmap.getView().animate({ + zoom: this.props.olmap.getView().getZoom() - 1, + duration: 500 + }); + } + + getWMSLayers() { + CapabilitiesUtil.parseWmsCapabilities(WMS_CAPABILITIES_URL) + .then(CapabilitiesUtil.getLayersFromWmsCapabilities) + .then(layers => { + console.log('wmsLayers', layers); + this.setState({ + wmsLayers: layers + }); + }) + .catch(() => alert('Could not parse capabilities document.')); + } + + /*toggleDrawerLayer () { + this.closeAllDrawer(); + // this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}); + // this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}, () => this.closeAllDrawer()); + // this.closeAllDrawer(); + const { drawerLayerVisible } = this.state; + if (drawerLayerVisible) { + this.setState({ drawerLayerVisible: false }, () => this.closeAllDrawer() ); + } + else { + this.setState({ drawerLayerVisible: true }, () => this.closeAllDrawer() ); + } + }*/ + + /*toggleDrawerLayer () { + this.setState({drawerLayerVisible: !this.state.drawerLayerVisible}, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }) + }*/ + + toggleDrawerLayer () { + let { drawerLayerVisible } = this.state; + if (drawerLayerVisible) { + this.setState({drawerLayerVisible: false}, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({drawerLayerVisible: true}, () => { + this.setState({ + // drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }) + } + } + + /*toggleDrawerSearch () { + this.closeAllDrawer(); + const { drawerSearchVisible } = this.state; + console.log('drawerSearchVisible', drawerSearchVisible); + if (drawerSearchVisible) { + this.setState({drawerSearchVisible: false}, () => this.closeAllDrawer() ); + } + else { + this.setState({drawerSearchVisible: true}, () => { + this.getFeaturesOnMap(); + this.closeAllDrawer(); + }); + } + }*/ + + toggleDrawerSearch () { + const { drawerSearchVisible } = this.state; + + if (drawerSearchVisible) { + this.setState({drawerSearchVisible: false}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + // drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }) + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({drawerSearchVisible: true}, () => { + this.getFeaturesOnMap(); + // this.getLayerSearchLabel(); + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + // drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + } + + toggleMapAnalyzeTool () { + // this.setState({drawerAnalyzeToolVisible: !this.state.drawerAnalyzeToolVisible}); + const { drawerAnalyzeToolVisible } = this.state; + + if (drawerAnalyzeToolVisible) { + this.setState({drawerAnalyzeToolVisible: false}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + // drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }) + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + else { + this.setState({drawerAnalyzeToolVisible: true}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + // drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: true + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + } + + toggleLayerSwitcher() { + this.setState({layerSwitcherVisible: !this.state.layerSwitcherVisible}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + // layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleMapTable() { + this.setState({mapTableModalVisible: !this.state.mapTableModalVisible}, () => { + this.setState({ + // drawerLayerVisible: false, + drawerLayerStylesVisible: false, + // mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + // activeSideLeft: false + }); + // this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleCreateNewLayer() { + this.setState({createNewLayerVisible: !this.state.createNewLayerVisible}); + this.deactiveMeasureMap(); + } + + toggleLayerStyles() { + this.setState({drawerLayerStylesVisible: !this.state.drawerLayerStylesVisible}, () => { + this.setState({ + drawerLayerVisible: false, + // drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleAddFeatureVisible = () => { + this.setState({addFeatureVisible: !this.state.addFeatureVisible}); + this.deactiveMeasureMap(); + } + + toggleLegend = () => { + this.setState({legendVisible: !this.state.legendVisible}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + measureVisible: false, + // legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + this.deactiveMeasureMap(); + }); + } + + toggleMeasureVisible = () => { + // this.setState({measureVisible: !this.state.measureVisible}); + this.setState({measureVisible: !this.state.measureVisible}, () => { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + drawerAnalyzeToolVisible: false, + popupRightVisible: false, + // measureVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false, + activeSideLeft: false + }); + this.props.closePopupRight(); + this.renderToolbarButtons(); + + if (!this.state.measureVisible) { + this.deactiveMeasureMap(); + } + }); + } + + closeAllDrawer = () => { + /* + Toggle Visibility that must be closed or switched to its visibility: + + layer: drawerLayerVisible + popup: popupRightVisible + searchFeature: drawerSearchVisible + basemap: layerSwitcherVisible + legend: legendVisible + drawing: drawingToolVisible + layerStyle: drawerLayerStylesVisible + mapTable: mapTableModalVisible + + Group layers: layer, layerStyle, mapTable (that shouldn't be closed each other) + Group popup: searchFeature, popup (that shouldn't be closed each other) => searchFeature below of popup + Legend + BaseMap + Drawing + */ + + const { drawerLayerVisible, drawerLayerStylesVisible, mapTableModalVisible, + drawerSearchVisible, popupRightVisible, + legendVisible, + layerSwitcherVisible, + drawingToolVisible + } = this.state; + + let visibleArr = [drawerLayerVisible, drawerLayerStylesVisible, mapTableModalVisible, + drawerSearchVisible, popupRightVisible, + legendVisible, + layerSwitcherVisible, + drawingToolVisible + ]; + + console.log('visibleArr', visibleArr); + + if (drawerLayerVisible) { + this.setState({ + drawerLayerVisible: true, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (drawerLayerStylesVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: true, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (mapTableModalVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: true, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (drawerSearchVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: true, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (popupRightVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: true, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (legendVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: true, + layerSwitcherVisible: false, + drawingToolVisible: false + }) + } + + if (layerSwitcherVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: true, + drawingToolVisible: false + }) + } + + if (drawingToolVisible) { + this.setState({ + drawerLayerVisible: false, + drawerLayerStylesVisible: false, + mapTableModalVisible: false, + drawerSearchVisible: false, + popupRightVisible: false, + legendVisible: false, + layerSwitcherVisible: false, + drawingToolVisible: true + }) + } + + } + + // toggleLayerPanel(layer) { + // console.log('toggleLayerPanel', layer); + // this.setState({layerPanelVisible: !this.state.layerPanelVisible}); + // } + + cancelWmsLayer() { + console.log('cancel wms'); + this.setState({ wmsLayers: [] }); + } + + onClickLayerMenu = (layer) => { + // console.log('onclick layer menu', layer); + } + + customTitleLayerTree(layer) { + // console.log('customTitleLayerTree', layer.get('title')); + // console.log('customTitleLayerTree', layer.get('type')); + // return ( + //
{layer.get('name')}
+ // ) + + // if (layer instanceof OlLayerGroup) { + + // } + + // return ( + //
+ //
+ // {layer.get('name')} + //
+ // {/*
+ // + //
*/} + //
+ // + // + // + // + // + // {layer.get('name')} + // this.showTableData(layer)}> Show Table42 + // Remove Layer + // + // + //
+ //
+ // ) + + // if (layer.get('type') == 'layer') { + return ( +
+
+ {layer.get('title') ? layer.get('title') : layer.get('name')} +
+ {/*
+ +
*/} +
+ + {/*10*/} + { layer.get('type') == 'layer' ? + {this.getLayerCount(layer)} + : null + } + this.onClickLayerMenu(layer)}> + + + + +
+ {layer.get('title') ? layer.get('title') : layer.get('name')} +
+
+ { + layer.get('type') === 'layer' || layer instanceof OlLayerImage ? + // layer instanceof OlLayerTile || layer instanceof OlLayerImage ? + + + this.zoomToLayer(layer)}> Zoom To Layer + this.showTableData(layer)}> Show Data + {this.props.editGeometryVisible ? + Add Feature + : this.addFeature(layer)}> Add Feature + } + {/* this.addFeature(layer)}> Add Feature*/} + this.styleLayer(layer)}> Layer Styles + + : + null } + this.removeLayer(layer)}> Remove Layer +
+
+
+
+ ) + // } + // else { + // return null; + // } + } + + onRightClickLayerTree(layer) { + console.log('right click layer tree', layer); + return ( + + + Header + Action + Another Action + + Another Action + + + ) + } + + onDropLayerTree() { + console.log('dropped'); + } + + + + /*showTableData = (layer) => { + console.log('showTableData', layer); + this.setState({ + mapTableTitle: layer.get('name'), + mapTableTitleSrc: layer.getSource().params_.LAYERS + }, async () => { + // console.log('mapTableTitle', this.state.mapTableTitle); + // console.log('mapTableTitleSrc', this.state.mapTableTitleSrc) + // let req = this.requestTableData(this.state.mapTableTitleSrc); + let res = await reqTableData(this.state.mapTableTitle); + console.log('showTableData',res); + // if (res !== undefined) { + // this.setState({resTableData: res}); + // } + + if (res.success) { + this.setState({resTableData: res.result}, () => this.toggleMapTable()); + } + else { + alert(res.result); + return; + } + }); + }*/ + + showTableData = (layer) => { + this.setState({ + mapTableTitle: layer.get('title'), + mapTableName: layer.get('name') + // mapTableTitleSrc: layer.getSource().params_.LAYERS + }, () => this.toggleMapTable()); + } + + refreshMapTable = async () => { + const { mapTableTitle, mapTableName } = this.state; + let res = await reqTableData(mapTableName); + if (res.success) { + this.setState({resTableData: res.result}); + } + else { + alert(res.result); + return; + } + } + + styleLayer = async (layer) => { + console.log('styling layer...', layer); + + let SLD_URL = layer.getSource().params_.SLD; + console.log('SLD_URL', SLD_URL); + + let resGetLayerColor = await getLayerColor(SLD_URL); + + console.log('styleLayer resGetLayerColor', resGetLayerColor); + + // this.toggleLayerStyles(); + + if (this.state.drawerLayerStylesVisible) { + this.closeLayerStyles(); + this.openLayerStyles(); + } + else { + this.openLayerStyles(); + } + /* + axios.get(SLD_URL) + .then(res => { + console.log('axios get SLD', res); + let text = res.data; + let parser, xmlDoc; + // let self = this; + // parseString(res.data, function (err, result) { + // console.log(result); + // self.events = result; + // }); + if (window.DOMParser) { + // code for modern browsers + parser = new DOMParser(); + xmlDoc = parser.parseFromString(text,"text/xml"); + + console.log('xmlDoc', xmlDoc); + } + // else { + // // code for old IE browsers + // xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + // xmlDoc.async = false; + // xmlDoc.loadXML(text); + // console.log('xmlDoc', xmlDoc); + // } + }) + .catch(err => { + console.log('axios get SLD err', err); + }); + + if (this.state.drawerLayerStylesVisible) { + this.closeLayerStyles(); + this.openLayerStyles(); + } + else { + this.openLayerStyles(); + }*/ + } + + openLayerStyles() { + this.setState({drawerLayerStylesVisible: true}); + } + + closeLayerStyles() { + this.setState({drawerLayerStylesVisible: false}); + } + + addFeature = async (layer) => { + console.log('adding feature on layer...', layer); + // console.log(layer.get('name')); + + let geomType = ''; + // request Geom Type from API + let reqGeomType = await getGeomType(layer.get('name')); + console.log('addFeature reqGeomType', reqGeomType) + if (reqGeomType.success) { + geomType = reqGeomType.result; + } + else { + geomType = ''; + alert(reqGeomType.result); + return; + } + + // request the table columns from API + // let featureColumns = await getTableColumns(layer.get('name')); + // console.log('addFeature featureColumns', featureColumns); + + let reqTableColumns = await getTableColumns(layer.get('name')); + console.log('reqTableColumns', reqTableColumns); + if (reqTableColumns.success) { + this.setState({addFeatureColumns: reqTableColumns.result}); + } + else { + alert(reqTableColumns.result); + // return; + } + + + this.setState({ + drawingToolVisible: true, + layerNameDraw: layer.get('name'), + geomTypeDraw: geomType, + // addFeatureColumns: reqTableColumns.result + }); + this.props.toggleActiveStateAddGeometry(); + + } + + cancelDraw() { + this.setState({ + drawingToolVisible: false, + layerNameDraw: '', + geomTypeDraw: '' + }); + this.props.toggleActiveStateAddGeometry(); + } + + removeLayer = (layer) => { + console.log('removing layer', layer); + let confirmation = window.confirm('Are you sure you want to remove this layer?'); + if (!confirmation) { + return; + } + this.props.olmap.removeLayer(layer); + } + + /*zoomToLayer = (layer) => { + console.log('zoomToLayer', layer); + let layerExtent = null; + let newLayerExtent = null; + let transformLayerExtent = null; + let fitOption = { + size: this.props.olmap.getSize(), + duration: 500 + } + // layer.getSource().getExtent(); + // layer.getExtent(); + // layer.getSource().getExtent(); + if (layer instanceof OlLayerImage) { + // console.log(layer.getSource().image_.extent); + layerExtent = layer.getSource().image_.extent; + transformLayerExtent = new transformExtent(layerExtent, 'EPSG:4326', this.props.olmap.getView().getProjection()) + // transformLayerExtent = new transformExtent(layerExtent, this.props.olmap.getView().getProjection(), 'EPSG:4326') + console.log('layerExtent', layerExtent); + console.log('transformLayerExtent', transformLayerExtent); + newLayerExtent = [ + layerExtent[0], layerExtent[2], layerExtent[1], layerExtent[3] + ]; + console.log('newLayerExtent', newLayerExtent); + + // this.props.olmap.getView().fit(new transformExtent(layerExtent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + this.props.olmap.getView().fit(layerExtent, fitOption); + + // new ZoomToExtent() + } + else if (layer instanceof OlLayerTile) { + layerExtent = layer.getSource().tmpExtent_; + // console.log(layer.getSource()); + this.props.olmap.getView().fit(layerExtent, fitOption); + } + // console.log(layer.getSource().getExtent()); + }*/ + + zoomToLayer = (layer) => { + console.log('zoomToLayer getSource', layer.getSource()); + // let layerName = layer.getSource().params_.LAYERS; + let layerName = layer.get('name'); + console.log('layerName', layerName); + const { fitOption } = this.state; + + // if (layer instanceof OlLayerImage) { + // // layerName = layer.getSource(); + + // } + // else if (layer instanceof OlLayerTile) { + + // } + CapabilitiesUtil.parseWmsCapabilities(WMS_CAPABILITIES_URL).then(response => { + console.log('capabilities response', response); + if (response !== undefined) { + let layer = findWhere(response.Capability.Layer.Layer, {"Name": layerName}); + console.log('layer',layer); + + if (layer === undefined) { + alert('Layer '+layerName+' is not found'); + return; + } + + let proj = "CRS:84"; + let bbox = findWhere(layer.BoundingBox, {"crs": proj}); + let extent = bbox.extent; + console.log('layer', layer); + console.log('extent', extent); + // map.getView().fit(new transformExtent(layerExtent, 'EPSG:4326', map.getView().getProjection()), { size: map.getSize() }); + + // console.log('layer', layer); + // this.props.olmap.getView().fit(extent, fitOption); + // console.log('getProjection', this.props.olmap.getView().getProjection()); + // var boundingExtent = ol.extent.boundingExtent(coordinates); + // boundingExtent = ol.proj.transformExtent(boundingExtent, ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857')); + + /*// other option + extent = new transformExtent(extent, "EPSG:4326", "EPSG:3857"); + console.log('extent now', extent); + this.props.olmap.getView().fit(extent, fitOption); */ + + this.props.olmap.getView().fit(new transformExtent(extent, proj, this.props.olmap.getView().getProjection()), fitOption); + + } + }) + } + + onDragEndLayerTree = (callback) => { + console.log('onDragEndLayerTree', callback); + } + + rebuildTreeNodes = (evt) => { + console.log('rebuildTreeNodes',evt); + } + + // to be populated on search feature menu + getFeaturesOnMap = async () => { + let layerSource = ''; + let layerTypeName = ''; + let layerSourceArr = []; + let layerTypeNameArr = []; + let layerNameArr = []; + let featureCollections = []; + let allFeatures = []; + this.props.olmap.getLayers().forEach((layer, i) => { + // console.log('layer', layer); + // console.log('layer type', layer.get('type')); + if (layer.get('type' !== 'base')) { + if (layer.get('type') !== 'vector') { + if (layer.get('type') !== 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + // get the features + layerSource = sublayer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(sublayer.get('name')); + } + }); + } + else { + if (layer.getVisible()) { + // get the features + layerSource = layer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(layer.get('name')); + } + } + } + } + if (layer.type === "IMAGE") { + if (layer.getVisible()) { + // get the features + layerSource = layer.getSource(); + layerTypeName = layerSource.params_.LAYERS; + layerTypeNameArr.push(layerTypeName); + layerNameArr.push(layer.get('name')); + } + + } + }) + + // console.log('layerSourceArr', layerSourceArr); + console.log('layerTypeNameArr', layerTypeNameArr); + console.log('layerNameArr', layerNameArr); + + for (let i = 0; i < layerTypeNameArr.length; i++) { + let req = await reqTableData(layerTypeNameArr[i]); + // console.log(typeof(res)); + + if (req.success) { + featureCollections.push(req.result); + if (req.result.features.length > 0) { + allFeatures.push(req.result.features); + } + } + else { + alert(req.result); + return; + } + + } + + allFeatures = flatten(allFeatures); + + this.setState({allFeatures: allFeatures}); + + } + + /*getLayerSearchLabel = async() => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + console.log('getLayerSearchLabel result', result); + this.setState({ + searchLabelData: result.data + }) + } + } else { + + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + } + }*/ + + setAddFeatureValue = (obj) => { + this.setState({addFeatureValue: obj}) + } + + renderToolbarButtons = () => { + const { activeSideLeft } = this.state + let olMousePosition = document.getElementsByClassName("ol-mouse-position")[0]; + let olScaleLine = document.getElementsByClassName("ol-scale-line")[0]; + + if (activeSideLeft) { + olMousePosition.classList.add("active-side-left"); + olScaleLine.classList.add("active-side-left"); + } + else { + olMousePosition.classList.remove("active-side-left"); + olScaleLine.classList.remove("active-side-left"); + } + + return ( + + this.zoomIn()} + > + + + + this.zoomOut()} + > + + + + + + + + + + {/* + + this.mapMeasurement()} + > + + + + + +
+ Map Measurement +
+
+ Remove Layer +
+
*/} + + + {/* this.toggleBtnDropMeasure()}> + + + + + + + +
Map Measurement
+
+ this.toggleMeasure('LineString')}> + Distance + + this.toggleMeasure('Polygon')}> + Area + +
+
*/} + + this.toggleMeasureVisible()} + > + + + + + + + this.toggleLegend()} + > + + + + + + + + {/*
+ +
*/} +
+ ) + } + + finishDrawingAdd = () => { + // const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + const { DrawingUtil, drawingLayer, chosenLayer } = this.state; + + // Set Drawing type to null and active is false + DrawingUtil.setActive(null, false); + DrawingUtil.destroy(); + + // Clear the drawing drawingLayer in drawingLayer layer + if (drawingLayer) { + drawingLayer.getSource().clear(); + } + if (chosenLayer) { + chosenLayer.getSource().clear(); + } + + // Removing DrawingLayer and ChosenLayer layer + let layersToRemove = []; + this.props.olmap.getLayers().forEach(function (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'DrawingLayer') { + layersToRemove.push(layer); + } + if (layer.get('name') !== undefined && layer.get('name') == 'ChosenLayer') { + layersToRemove.push(layer); + } + }); + for(var i = 0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + + // // Set snapping mode to false + // this.setState({snapMode: false}); + + // // Calling props cancelDraw in MapToolbar.js + // this.props.cancelDraw(); + } + + mapMeasurement = () => { + // console.log() + /*return ( + + + + + + +
+ Map Measurement +
+
+ this.removeLayer(layer)}> Remove Layer +
+
+ )*/ + } + + toggleBtnDropMeasure = () => { + this.setState({ btnDropMeasureVisible: !this.state.btnDropMeasureVisible }, () => { + if (!this.state.btnDropMeasureVisible) { + this.setState({ + // measureVisible: false, + measureName: '', + measureType: '' + }); + } + }); + } + + // toggleBt + + /*toggleMeasure = (name, type) => { + this.setState({ + measureVisible: true, + measureName: name, + measureType: type + }); + }*/ + + pointerMoveHandler = (evt) => { + let { sketch, helpTooltipElement, helpTooltip, continuePolygonMsg, continueLineMsg } = this.state; + if (evt.dragging) { + return; + } + /** @type {string} */ + var helpMsg = 'Click to start drawing'; + + if (sketch) { + var geom = sketch.getGeometry(); + if (geom instanceof Polygon) { + helpMsg = continuePolygonMsg; + } else if (geom instanceof LineString) { + helpMsg = continueLineMsg; + } + } + + // helpTooltipElement.innerHTML = helpMsg; + helpTooltip.setPosition(evt.coordinate); + + helpTooltipElement.classList.remove('hidden'); + }; + + restartMeasureMap = () => { + let { olmap } = this.props; + let { measureType, measureDraw } = this.state; + + // empty measureValue + this.setState({measureValue: ''}); + + // clear the layer source of measureLayer + this.measureLayer.getSource().clear(); + + // remove interaction of measureDraw + olmap.removeInteraction(measureDraw); + } + + deactiveMeasureMap = () => { + const { olmap } = this.props; + this.restartMeasureMap(); + this.setState({measureType: ''}); + + // removing measure_layer + olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') == 'measure_layer') { + olmap.removeLayer(layer); + } + }) + } + + toggleMeasure = (type) => { + this.setState({ + measureType: type + }, () => { + let { olmap } = this.props; + let { measureType, measureDraw } = this.state; + + this.restartMeasureMap(); + + // olmap.on('pointermove', this.pointerMoveHandler.bind(this)); + + // removing measure_layer first + olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') == 'measure_layer') { + olmap.removeLayer(layer); + } + }) + + // then recreate new measure_layer (in case if user changing measure type) + olmap.addLayer(this.measureLayer); + + this.addInteractionMeasure(measureType); + }) + } + + addInteractionMeasure = (measureType) => { + // let type = (typeSelect.value == 'area' ? 'Polygon' : 'LineString'); + let { olmap } = this.props; + let { measureDraw, sketch } = this.state; + let measure = new Draw({ + source: this.measureLayer.getSource(), + type: measureType, + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + lineDash: [10, 10], + width: 3 + }), + image: new CircleStyle({ + radius: 5, + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.7)' + }), + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.2)' + }) + }) + }) + }); + this.setState({measureDraw: measure}, () => { + let { measureDraw, measureTooltipElement, measureTooltip } = this.state; + let { olmap } = this.props; + olmap.addInteraction(measureDraw); + + + // createMeasureTooltip(); + // createHelpTooltip(); + + + + var listener; + measureDraw.on('drawstart', + (evt) => { + + // clear the layer source of measureLayer + this.measureLayer.getSource().clear(); + + // set sketch + sketch = evt.feature; + + /** @type {import("../src/ol/coordinate.js").Coordinate|undefined} */ + var tooltipCoord = evt.coordinate; + + listener = sketch.getGeometry().on('change', (evt) => { + var geom = evt.target; + var output; + if (geom instanceof Polygon) { + // console.log('geom Polygon',geom); + output = formatArea(geom); + tooltipCoord = geom.getInteriorPoint().getCoordinates(); + console.log('tooltipCoord',tooltipCoord); + // console.log('output Polygon', output); + this.setState({measureValue: output}); + } else if (geom instanceof LineString) { + output = formatLength(geom); + tooltipCoord = geom.getLastCoordinate(); + // console.log('tooltipCoord', tooltipCoord); + // console.log('output LineString', output); + this.setState({measureValue: output}); + } + // measureTooltipElement.innerHTML = output; + // measureTooltip.setPosition(tooltipCoord); + }); + }); + + measureDraw.on('drawend', + () => { + // measureTooltipElement.className = 'ol-tooltip ol-tooltip-static'; + // measureTooltip.setOffset([0, -7]); + // unset sketch + sketch = null; + // unset tooltip so that a new one can be created + measureTooltipElement = null; + // this.createMeasureTooltip(); + // olmap.removeInteraction(measureDraw); + + unByKey(listener); + }); + }); + } + + /** + * Creates a new measure tooltip + */ + createMeasureTooltip = () => { + let { measureTooltipElement, measureTooltip } = this.state; + let { olmap } = this.props; + if (measureTooltipElement) { + measureTooltipElement.parentNode.removeChild(measureTooltipElement); + } + measureTooltipElement = document.createElement('div'); + measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure'; + measureTooltip = new Overlay({ + element: measureTooltipElement, + offset: [0, -15], + positioning: 'bottom-center' + }); + olmap.addOverlay(measureTooltip); + } + + /*renderMeasureContent = () => { + const { measureVisible } = this.state; + console.log('measureVisible', measureVisible); + alert('measureVisible '+measureVisible); + return ( +
+
+ Helloooooooo +
+
+ ) + }*/ + + render() { + const {isReady} = this.state; + if (!isReady) { + return null; + } + return ( + + +
+ + +
+ {/* console.log('layer tree changed')} + onRightClick={(layer) => this.onRightClickLayerTree(layer)} + nodeTitleRenderer={(layer) => this.customTitleLayerTree(layer)} + onDragEnd={(callback) => this.onDragEndLayerTree(callback)} + onLayerChangeVisible={() => console.log('layer change visible')} + onDrop={(info) => console.log('onDrop', info)} + />*/} +
+ this.customTitleLayerTree(layer)} + /> +
+
+ + + this.props.openPopupRight()} + popupDataTemp={this.props.popupDataTemp} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + activeListFeatureId={this.props.activeListFeatureId} + searchLabelData={this.props.searchLabelData} + layerInfo={this.props.layerInfo} + /> + + + + + + { this.renderToolbarButtons() } + + + + + + + + {/*{ + this.state.layerSwitcherVisible && + + }*/} + + { this.state.mapTableModalVisible && + this.props.setPopupDataTemp(feature)} + openPopupRight={() => this.props.openPopupRight()} + refreshMapTable={() => this.refreshMapTable()} + /> + } + + {this.state.drawingToolVisible && + this.toggleAddFeatureVisible()} + setAddFeatureValue={(obj) => this.setAddFeatureValue(obj)} + setDrawingState={(DrawingUtil, drawingLayer, chosenLayer) => this.setState({ + DrawingUtil: DrawingUtil, + drawingLayer: drawingLayer, + chosenLayer: chosenLayer + })} + /> + } + + { this.state.addFeatureVisible && + this.toggleAddFeatureVisible()} + addFeatureValue={this.state.addFeatureValue} + closeDrawing={this.cancelDraw} + finishDrawingAdd={() => this.finishDrawingAdd()} + /> + } + + + + + + {this.state.createNewLayerVisible && + + } + + { this.state.legendVisible && this.toggleLegend()} /> } + + {/* this.state.measureVisible && + + */} + + { this.state.measureVisible && + this.toggleMeasure(type)} + toggleMeasureVisible={() => this.toggleMeasureVisible()} + measureType={this.state.measureType} + measureValue={this.state.measureValue} + /> + } +
+ ) + } +} + +export default MapToolbar; \ No newline at end of file diff --git a/src/components/MapToolbar_backup/package.json b/src/components/MapToolbar_backup/package.json new file mode 100644 index 0000000..139474e --- /dev/null +++ b/src/components/MapToolbar_backup/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapToolbar", + "version": "0.0.0", + "private": true, + "main": "./MapToolbar.js" +} diff --git a/src/components/MeasureContainer/MeasureContainer.css b/src/components/MeasureContainer/MeasureContainer.css new file mode 100644 index 0000000..39774e9 --- /dev/null +++ b/src/components/MeasureContainer/MeasureContainer.css @@ -0,0 +1,64 @@ +/*.measureDrawContainer { + text-align: center; +} + +.measureDrawContent { + display: block; + position: absolute; + top: 50px; + left: 45%; + margin-left: -50px; + min-width: 175px; + max-width: 100%; + padding: 5px; + z-index: 50; + background-color: rgba(0,0,0,0.5); +}*/ + +.measure-container { + display: block; + z-index: 50; + top: 60px; + right: 50px; + /*left: 40%;*/ + position: absolute; + max-height: 300px; + min-height: 130px; + min-width: 250px; + border-radius: 5px; + background-color: rgba(0,0,0,0.5); + color: #ffffff; +} + +.measure-title { + font-size: 16px; + margin-top: 5px; + margin-left: 20px; + margin-bottom: 10px; + font-weight: bold; +} + +.measure-close { + float: right; + margin-right: 15px; + margin-top: -5px; + cursor: pointer; +} + +.measure-body { + max-height: 250px; + overflow: auto; + margin: 5px; + text-align: left; +} + +.measure-button-group { + text-align: center; +} + +.measure-result { + font-size: 14px; + margin-top: 10px; + margin-left: 10px; +} + diff --git a/src/components/MeasureContainer/MeasureContainer.js b/src/components/MeasureContainer/MeasureContainer.js new file mode 100644 index 0000000..35b96e6 --- /dev/null +++ b/src/components/MeasureContainer/MeasureContainer.js @@ -0,0 +1,43 @@ +import React, { Component } from 'react'; +import { Row, Col, Button } from 'reactstrap'; +import './MeasureContainer.css'; +import '../../assets/css/customscroll.css'; + + +class MeasureContainer extends Component { + + + constructor(props) { + super(props) + + } + + render() { + const { measureType, measureValue, toggleMeasure, toggleMeasureVisible } = this.props; + return ( +
+
Measure x
+
+
+ {measureType !== '' && measureType == 'LineString' ? + + : + } +   + {measureType !== '' && measureType == 'Polygon' ? + + : + } +
+
+ Measurement Result:
+ {measureValue} +
+
+
+ ) + + } +} + +export default MeasureContainer; \ No newline at end of file diff --git a/src/components/MeasureContainer/package.json b/src/components/MeasureContainer/package.json new file mode 100644 index 0000000..406192e --- /dev/null +++ b/src/components/MeasureContainer/package.json @@ -0,0 +1,6 @@ +{ + "name": "MeasureContainer", + "version": "0.0.0", + "private": true, + "main": "./MeasureContainer.js" +} \ No newline at end of file diff --git a/src/components/PopupContainer/PopupContainer.css b/src/components/PopupContainer/PopupContainer.css new file mode 100644 index 0000000..f943269 --- /dev/null +++ b/src/components/PopupContainer/PopupContainer.css @@ -0,0 +1,141 @@ +.ol-popup { + position: absolute; + background-color: white; + -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + padding: 15px; + border-radius: 10px; + border: 1px solid #cccccc; + bottom: 12px; + left: -50px; + min-width: 280px; +} +.ol-popup:after, .ol-popup:before { + top: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} +.ol-popup:after { + border-top-color: white; + border-width: 10px; + left: 48px; + margin-left: -10px; +} +.ol-popup:before { + border-top-color: #cccccc; + border-width: 11px; + left: 48px; + margin-left: -11px; +} +.ol-popup-closer { + text-decoration: none; + position: absolute; + top: 2px; + right: 8px; +} +.ol-popup-closer:after { + content: "✖"; +} +/*.popup-table { + height: 200px; + overflow-y: scroll; +}*/ +.popup-content { + max-height: 250px; + overflow: auto; +} +/*.div_popoup_table { + height: 200px; + overflow: auto; +}*/ +.div-popup-table { + height: 225px; + overflow: auto; +} +/*#popup-table { + height: 200px; + overflow: auto; +}*/ +.div-popup-table>#popup-table>thead>tr>th, .div-popup-table>#popup-table>tbody>tr>th, .div-popup-table>#popup-table>tfoot>tr>th, .div-popup-table>#popup-table>thead>tr>td, .div-popup-table>#popup-table>tbody>tr>td, .div-popup-table>#popup-table>tfoot>tr>td, .div-popup-table>#popup-table>tr>td { + padding: 3px !important; + font-size: 11px; + /*overflow-y: scroll;*/ +} + +.popup-header { + /*margin-top: -10px;*/ +} + +.popup-subtitle { + font-size: 11px; + margin-top: -5px; +} + +.popup-layername { + font-size: 12px; +} + +.popup-layerid { + font-size: 10px; + font-style: italic; +} + +.popup-body { + /* margin-top: 10px; + margin-bottom: 50px; */ + margin-top: 30px; + margin-bottom: 10px; + height: 225px; +} + +.popup-footer { + text-align: center !important; + /*padding: 0px;*/ + padding-top: 30px; + /*float: right;*/ + /*margin-right: 10px;*/ + /*z-index: 90;*/ + /*float: inherit !important;*/ + /*margin-bottom: 0px;*/ +} + +/*.image-slider-container { + height: 300px; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden +}*/ + +.image-popup-container { + /*width: 100%; + height: 150px; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden*/ + + max-width: 300px; + max-height: 150px; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + margin-bottom: 25px; +} + +.light-text { + color: #eeeeee; +} + +.dark-text { + color: #333333; +} + +.hide { + display: none; +} \ No newline at end of file diff --git a/src/components/PopupContainer/PopupContainer.js b/src/components/PopupContainer/PopupContainer.js new file mode 100644 index 0000000..f61e2fa --- /dev/null +++ b/src/components/PopupContainer/PopupContainer.js @@ -0,0 +1,863 @@ +import React, { Component, Fragment } from 'react'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Vector as VectorSource} from 'ol/source'; +import Feature from 'ol/Feature'; +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Icon as IconOl, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import { fromLonLat, transformExtent } from 'ol/proj'; +import { boundingExtent } from 'ol/extent'; +import { Polyline } from 'ol/format'; +import { getVectorContext } from 'ol/render'; +import { Button, UncontrolledTooltip, Badge } from 'reactstrap'; +import { Icon } from '@iconify/react'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import listOutline from '@iconify/icons-ion/list-outline'; +import createOutline from '@iconify/icons-ion/create-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import earthIcon from '@iconify/icons-ion/earth'; +import pencilIcon from '@iconify/icons-ion/pencil'; +import routeIcon from '@iconify/icons-fa-solid/route'; +import ImagePopup from '../../components/ImagePopup'; +import ImagePopupPreview from '../../components/ImagePopupPreview/ImagePopupPreview.js'; +import ImageSlider from '../../components/ImageSlider'; +import DefaultImageSlider from '../../components/DefaultImageSlider'; +import MapTable from '../../components/MapTable'; +import EditFeature from '../../components/EditFeature'; +import DrawingTool from '../../components/DrawingTool'; +import country_indonesia from '../../assets/json/indonesia.json'; +import './PopupContainer.css'; +import SweetAlert from 'react-bootstrap-sweetalert' +import axios from "axios" +import moment from "moment" +import { deleteFeature, getImagePopup, formatLength, formatArea, lightOrDark } from '../../const/GeoserverFunc.js'; +import { formatLabel, hideAttr, dateColumns, DATE_TIME_FORMAT } from '../../const/CustomFunc.js' +import {API_UPLOAD_IMAGE, BASE_IMAGE, BASE_SIMPRO } from '../../const/ApiConst.js' +import routeDummy from '../../dummy_data/route2.json'; + +class PopupContainer extends Component { + + constructor(props) { + super(props) + this.state = { + isReady: false, + imagePopupVisible: false, + imagePreviewVisible: false, + imagePopupTitle: '', + imagePreviewTitle: '', + mapTableVisible: false, + editFeatureVisible: false, + editFeatureTitle: '', + editGeometryVisible: false, + selectedPopupData: null, + featureExtent: null, + // zoomLevelOnFeature: 18, + // zoomDurationOnFeature: 500, + mapProjection: this.props.olmap.getView().getProjection(), + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + }, + alert: false, + alertMessage: '', + successAlert: false, + dangerAlert: false, + photo_feature: null, + image_feature: null, + geomTypeDraw: '', + // fid: '', + imageArr: [] + } + } + + componentDidMount() { + // console.log('PopupContainer fid', this.props.fid); + // this.props.getLayerAttribute(this.props.layerName); + this.setState({isReady: true}); + } + + /*componentDidMount() { + // this.setState({fid: this.props.popupDataTemp[0].id}, () => console.log('this.state.fid',this.state.fid) ); + } + + componentDidUpdate(prevState, prevProps) { + if (prevProps.popupDataTemp !== this.props.popupDataTemp) { + console.log('popupDataTemp berubahhhhh', this.props.popupDataTemp); + if (this.props.popupDataTemp.length === 1) { + // this.setState({fid: this.props.popupDataTemp[0].id}, () => console.log('fid', this.state.fid)); + } + } + }*/ + + componentDidUpdate(prevState, prevProps) { + if (prevProps.fid !== this.props.fid) { + this.fillPopupContent(); + // this.renderImageSlider(this.props.fid); + // this.props.rerenderPopupContainer(); + } + // if (prevProps.layerName !== this.props.layerName) { + // console.log('prevProps.layerName', prevProps.layerName); + // console.log('this.props.layerName', this.props.layerName); + // // this.getLayerAttribute(this.props.layerName); + // } + } + + matchLayerAttribute = (propKey) => { + // console.log('matchLayerAttribute propKey', propKey); + const { layer_attribute } = this.props; + for (let i=0; i < layer_attribute.length; i++) { + if (layer_attribute[i].attribute !== 'the_geom') { + // skip the_geom + if (layer_attribute[i].attribute_label !== null && layer_attribute[i].attribute_label !== '' ) { + if (propKey == layer_attribute[i].attribute) { + // console.log(layer_attribute[i].attribute_label); + return layer_attribute[i].attribute_label; + } + } + else { + if (propKey == layer_attribute[i].attribute) { + // console.log(layer_attribute[i].attribute); + return layer_attribute[i].attribute; + } + } + } + } + } + + + getFeaturesLabel = (item) => { + if (item.id) { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { searchLabelData } = this.props; + let matchLayerName = searchLabelData.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return item.properties[matchLayerName[0].attribute_name]; + } + else { + return item.id; + } + } + else { + return ""; + } + } + + getLayerType = (item) => { + if (item.id) { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_geom_type; + } + else { + return "Undefined Geom Type"; + } + } + else { + return ""; + } + + } + + getLayerColor = (item) => { + if (item.id) { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_color; + } + else { + return "#ffffff"; + } + } + else { + return "#ffffff"; + } + } + + getLayerTitle = (item) => { + if (item.id) { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + return matchLayerName[0].layer_title; + } + else { + return matchLayerName[0].layer_name; + } + } + else { + return "No layer title provided"; + } + } + else { + return "No layer title provided"; + } + } + + getLayerTitleTextColor = (layerColor) => { + let brightness = lightOrDark(layerColor); // 'light' or 'dark' + + if (brightness == 'light') { + return 'dark-text'; + } + else { + return 'light-text'; + } + } + + fillPopupContent = () => { + const { popupDataTemp } = this.props; + // console.log('fillPopupContent this.props.layerName', this.props.layerName); + + if (popupDataTemp.length === 1) { + return this.oneFeatureContent(popupDataTemp[0]); + } + else if (popupDataTemp.length > 1) { + return this.moreFeatureContent(); + } + } + + oneFeatureContent = (selectedPopupData) => { + // this.setState({selectedPopupData: selectedPopupData}); // set the selectedPopupData + this.selectFeature(selectedPopupData); + const { layerName } = this.props; + const popupId = selectedPopupData.id; + const popupProperties = selectedPopupData.properties; + + return ( +
+
+ {/*

{ popupId }

*/} + {/*

{ this.getFeaturesLabel(selectedPopupData) }

*/} + {/*

Info Detail

*/} + {/*

+ { layerName }
+ { popupId } +

*/} + + {/*

+ + { this.getLayerTitle(selectedPopupData) } +
+

*/} + +
+ {/*
this.showImageFeature(selectedPopupData)}>*/} +
+ { this.renderImageSlider(selectedPopupData) } +
+
+ + + { this.renderOneRowFeature(popupProperties) } + +
+
+
+
+ { this.renderButtonFooter(selectedPopupData) } +
+
+
+ ) + } + + // modified to validate if there's an object inside object, so make it flatten into one object + renderOneRowFeature = (popupProperties) => { + console.log('renderOneRowFeature popupProperties', popupProperties); + + let modPopupProperties = {}; + + const row = []; + + // modify and flatten the object + for (let key in popupProperties) { + + // console.log('popupProperties[key]', popupProperties[key], typeof(popupProperties[key])); + if (typeof(popupProperties[key]) === 'object') { + for (let prop in popupProperties[key]) { + modPopupProperties[prop] = popupProperties[key][prop]; + } + } + + modPopupProperties[key] = popupProperties[key] + } + + // remove the object + for (let key in modPopupProperties) { + if (typeof(modPopupProperties[key]) === 'object') { + delete modPopupProperties[key]; + } + } + + // generate the row table in popup + for (let key in modPopupProperties) { + let propKey = this.matchLayerAttribute(key); + if (propKey !== undefined) { + row.push({formatLabel(propKey)} : {dateColumns.includes(propKey) ? moment(modPopupProperties[propKey]).utc().format(DATE_TIME_FORMAT) : modPopupProperties[propKey]}) + } + else { + row.push({formatLabel(key)} : {dateColumns.includes(key) ? moment(modPopupProperties[key]).utc().format(DATE_TIME_FORMAT) : modPopupProperties[key]}) + } + } + + return row; + } + + renderOneRowFeatureV1 = (popupProperties) => { + const row = []; + + for (let key in popupProperties) { + + // // remove join key because it is an object; + // if (key === 'join') { + // delete popupProperties[key]; + // } + + let propKey = this.matchLayerAttribute(key); + if (propKey !== undefined) { + row.push({propKey} : {popupProperties[key]}) + } + else { + row.push({key} : {popupProperties[key]}) + } + } + + return row; + + } + + renderButtonFooter = (selectedPopupData) => { + let layer_name = selectedPopupData.id && selectedPopupData.id.substr(0, selectedPopupData.id.indexOf(".")); + return( + + + + Show Images + + {/* + + Show Table + */} + + + Go to Feature + + + + Edit Attribute + + { this.props.activeStateAddGeometry ? + () : + () + } + + Edit Geometry + + + + Delete Feature + + + { layer_name && (layer_name === "m_sales" || layer_name === "m_employee" || layer_name === "m_waspang" || layer_name === "m_presensi") && + <> + + + Show Route + + } + + ) + } + + renderImageSlider = (selectedPopupData) => { + if (this.props.popupDataTemp[0].id == selectedPopupData.id) { + return ; + } else { + return + } + } + + selectFeature = (selectedPopupData) => { + console.log('PopupContainer selectFeature', selectedPopupData); + // this.props.setPopupDataTemp(selectedPopupData); + this.props.removeChosenLayer(); + const select = new Select(); + this.props.olmap.addInteraction(select); + let geomToDisplay = null; + + let geomType = selectedPopupData.geometry.type; + let coords = selectedPopupData.geometry.coordinates; + let { mapProjection } = this.state; + if (geomType === "Point") { + // geomToDisplay = new Point(coords).transform('EPSG:4326', mapProjection); + geomToDisplay = new Point(coords).transform(mapProjection, mapProjection); + // geomToDisplay = new Point(coords); + } + else if (geomType === "MultiPoint") { + // geomToDisplay = new MultiPoint(coords).transform('EPSG:4326', mapProjection); + geomToDisplay = new MultiPoint(coords).transform(mapProjection, mapProjection); + // geomToDisplay = new MultiPoint(coords); + } + else if (geomType === "LineString") { + // geomToDisplay = new LineString(coords).transform('EPSG:4326', mapProjection); + geomToDisplay = new LineString(coords).transform(mapProjection, mapProjection); + // geomToDisplay = new LineString(coords); + } + else if (geomType === "MultiLineString") { + // geomToDisplay = new MultiLineString(coords).transform('EPSG:4326', mapProjection); + geomToDisplay = new MultiLineString(coords).transform(mapProjection, mapProjection); + // geomToDisplay = new MultiLineString(coords); + } + else if (geomType === "Polygon") { + // geomToDisplay = new Polygon(coords).transform('EPSG:4326', mapProjection); + geomToDisplay = new Polygon(coords).transform(mapProjection, mapProjection); + // geomToDisplay = new Polygon(coords); + } + else if (geomType === "MultiPolygon") { + // geomToDisplay = new MultiPolygon(coords).transform('EPSG:4326', mapProjection); + geomToDisplay = new MultiPolygon(coords).transform(mapProjection, mapProjection); + // geomToDisplay = new MultiPolygon(coords); + + } + + let styleSelected = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + // color: '#2b2d80', + color: '#1e00ff', + width: 3 + }), + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#1e00ff', + width: 3 + }) + }) + }); + + // let polygon = new MultiPolygon(country_indonesia.geometry.coordinates).transform('EPSG:4326', 'EPSG:3857') + + let chosenFeature = new Feature({ + geometry: geomToDisplay + }) + + console.log('chosenFeature', chosenFeature); + console.log('chosenFeature getGeometry', chosenFeature.getGeometry()); + console.log('chosenFeature getArea', formatArea(chosenFeature.getGeometry())); + // console.log('getExtent()', chosenFeature.getGeometry().getExtent()); + + // this.setState({featureExtent: chosenFeature.getGeometry().getExtent()}); + + let chosenLayer = new VectorLayer({ + name: 'ChosenLayer', + source_type: 'chosenLayer', + source: new VectorSource({ + features: [chosenFeature] + }), + style: styleSelected, + type: 'vector', + zIndex: 999 + }); + + this.props.olmap.addLayer(chosenLayer); + // this.setState({featureExtent: chosenFeature.getGeometry().getExtent()}); + // this.getImagePopup(selectedPopupData.id); // get image on popup + } + + moreFeatureContent = () => { + const { popupDataTemp } = this.props; + console.log('moreFeatureContent', popupDataTemp); + return ( +
+
+

Choose one feature

+
+
+ + + { popupDataTemp.map((item, index) => { + return ( this.chooseOneFeature(item)}> + + + ) + }) } + +
{this.getFeaturesLabel(item)} {this.getLayerTitle(item)}
+
+
+
+ +
+
+
+ ) + } + + chooseOneFeature = (feature) => { + this.props.setPopupDataTemp(feature); + this.fillPopupContent(); + } + + showImageFeature = (selectedPopupData) => { + console.log('show images', selectedPopupData); + this.setState({imagePopupTitle: selectedPopupData.id}); + this.toggleImagePopup(); + } + + getFrontPhotoLicense = event => { + this.setState({ + photo_feature: event.target.files[0], + image_feature: URL.createObjectURL(event.target.files[0]), + // imagePopupVisible: false, + imagePreviewVisible: true + }) + + // console.log(URL.createObjectURL(event.target.files[0])) + } + + toggleImagePopup = () => { + this.setState({imagePopupVisible: !this.state.imagePopupVisible}); + } + + handleUploadImage = async () => { + const {photo_feature, image_feature, imagePopupTitle} = this.state + // console.log("id feature",imagePopupTitle) + + const category = imagePopupTitle.split("."); + // const date = moment().format("YYYY-MM-DD HH:mm:ssZ") + let type = category[0] + let id = category[1] + let imageType = '' + + if(type==="m_office"){ + imageType="office" + }else if(type==="m_sales"){ + imageType="m_sales" + }else if(type==="m_employee"){ + imageType="employee" + }else{ + imageType="customer" + } + const formData = new FormData() + formData.append('ref_id', id) + // formData.append('last_updated', date) + formData.append('files', photo_feature) + // formData.append('created_by', 'admin') + let url = `${BASE_SIMPRO}/image/${imageType}/upload`; + const result = await axios.post(url, formData) + .then(res => res) + .catch((error) => error.response); + // const param = { + // method: 'POST', + // header: JSON.stringify({'Content-Type': 'multipart/form-data'}), + // body: formData + // } + + // const result = await fetch(API_UPLOAD_IMAGE, param).then(response => response.json()).then(res => res).catch(err => err.response) + return result + } + + toggleImagePreview = async param => { + if(param == "upload"){ + const result = await this.handleUploadImage() + console.log("response upload image",result); + if (result == undefined) { + alert('Upload Failed'); + return; + } + else { + if (result.data.code == 200) { + alert('Successfully uploaded image'); + this.toggleImagePopup(); + } + else { + console.log('err',result); + alert(result.data); + return; + } + } + + } + this.setState({imagePreviewVisible: !this.state.imagePreviewVisible}); + } + + showTable = (selectedPopupData) => { + this.toggleShowTable(); + } + + toggleShowTable = () => { + this.setState({mapTableVisible: !this.state.mapTableVisible}) + } + + editFeature = (selectedPopupData) => { + console.log('edit feature', selectedPopupData); + this.setState({selectedPopupData: selectedPopupData}, () => this.toggleEditFeature()); + } + + toggleEditFeature = () => { + this.setState({editFeatureVisible: !this.state.editFeatureVisible}) + } + + /*goToFeature = (selectedPopupData) => { + console.log('goToFeature', selectedPopupData); + const fitOption = { + size: this.props.olmap.getSize(), + duration: 500 + } + // const proj = "CRS:84"; + const proj = "EPSG:4326"; + const coords = selectedPopupData.geometry.coordinates; + const { mapProjection, zoomLevelOnFeature, zoomDurationOnFeature } = this.state; + let extent = null; + + let indoGeom = [ + [ + 120.715609, + -10.239581 + ], + [ + 120.295014, + -10.25865 + ], + [ + 118.967808, + -9.557969 + ], + [ + 119.90031, + -9.36134 + ], + [ + 120.425756, + -9.665921 + ], + [ + 120.775502, + -9.969675 + ], + [ + 120.715609, + -10.239581 + ] + ]; + // let polygon = new Polygon([indoGeom]).transform('EPSG:4326', 'EPSG:3857'); + + if (selectedPopupData.geometry.type === "Point") { + // extent = new Point([coords[0], coords[1]]).transform('EPSG:4326', 'EPSG:3857'); + // extent = new Point([coords[0], coords[1]]); + extent = [coords[0], coords[1]]; + // console.log(extent); + // return this.props.olmap.getView().setCenter(extent); + // this.props.olmap.getView().fit(indoGeom, fitOption); + // this.props.olmap.getView().centerOn(coords, this.props.olmap.getSize(), this.props.olmap.getView().getResolution()); + // return this.props.olmap.getView().fit(new transformExtent(extent, proj, this.state.mapProjection), fitOption); + this.props.olmap.getView().setCenter(fromLonLat(extent, mapProjection)); + // this.props.olmap.getView().setZoom(zoomLevelOnFeature); + this.props.olmap.getView().animate({ + zoom: zoomLevelOnFeature, + duration: zoomDurationOnFeature + }) + } + }*/ + + goToFeature = (selectedPopupData) => { + // get chosenLayer -> get the chosenFeature -> get geometry -> get extent -> fit to map + const { fitOption } = this.state; + const proj = "EPSG:3857"; + let chosenLayer = null; + let chosenFeatures = null; + let featureExtent = null; + + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + return; + } + }) + // console.log('go to feature chosenLayer', chosenLayer); + if (chosenLayer === null) { + alert('No chosen feature'); + return; + } + + chosenFeatures = chosenLayer.getSource().getFeatures(); + featureExtent = chosenFeatures[0].getGeometry().getExtent() + + // console.log(chosenFeatures); + console.log('goToFeature featureExtent', featureExtent); + + this.props.olmap.getView().fit(new transformExtent(featureExtent, proj, this.props.olmap.getView().getProjection()), fitOption); + } + + editGeometry = (selectedPopupData) => { + console.log('editGeometry', selectedPopupData); + // this.setState({geomTypeDraw: selectedPopupData.geometry.type}, () => this.props.toggleEditGeometry()); + this.props.toggleEditGeometry(selectedPopupData); + } + + toggleEditGeometry = () => { + this.setState({editGeometryVisible: !this.state.editGeometryVisible}) + } + + deleteFeature = async (selectedPopupData) => { + console.log('delete feature', selectedPopupData); + console.log('layerName', this.props.layerName); + let confirmation = window.confirm("Are you sure you want to delete this feature?"); + + if (!confirmation) { + return; + } + else { + let response = await deleteFeature(this.props.layerName, selectedPopupData.id); + console.log('PopupContainer delete feature response', response); + if (response.success) { + alert('Successfully deleted a feature.'); + window.location.reload(); + // this.setState({alert: true, alertMessage: "Successfully deleted a feature.", successAlert: true, dangerAlert: false}); + } + else { + console.log(response.result); + alert('Failed to delete a feature'); + // this.setState({alert: true, alertMessage: "Failed to delete a feature.", successAlert: false, dangerAlert: true}); + } + } + } + + /*getImagePopup = async (fid) => { + let resGetImagePopup = await getImagePopup(fid); + // console.log('imagePopup resGetImagePopup',resGetImagePopup); + let imageArr = []; + + if (resGetImagePopup.data !== undefined) { + if (resGetImagePopup.data.data.length > 0) { + let imageData = resGetImagePopup.data.data; + for (let i=0; iLoading...
+ } + return( +
+ this.setState({alert: false, successAler: false, dangerAlert: false})}> + {alertMessage} + + + {this.fillPopupContent()} + { + this.state.imagePopupVisible && + this.toggleImagePopup()} + imagePopupTitle={this.state.imagePopupTitle ? this.state.imagePopupTitle : 'Feature Image'} + id={this.props.popupDataTemp[0].id} + /> + } + { + this.state.imagePreviewVisible && + + } + { + this.state.editFeatureVisible && + this.toggleEditFeature()} + editFeatureTitle={this.state.editFeatureTitle ? this.state.editFeatureTitle : 'Edit Attribute'} + data={this.state.selectedPopupData} + layerName={this.props.layerName} + closePopupRight={this.props.closePopupRight} + layer_attribute={this.props.layer_attribute} + /> + } + {/*{ + this.state.mapTableVisible && + + }*/} + {/*{ + this.state.editGeometryVisible && + + }*/} +
+ ) + + + + } +} + + +export default PopupContainer; \ No newline at end of file diff --git a/src/components/PopupContainer/PopupContainer_backup.js b/src/components/PopupContainer/PopupContainer_backup.js new file mode 100644 index 0000000..91cad57 --- /dev/null +++ b/src/components/PopupContainer/PopupContainer_backup.js @@ -0,0 +1,445 @@ +import React, { Component, Fragment } from 'react'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Vector as VectorSource} from 'ol/source'; +import Feature from 'ol/Feature'; +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import { fromLonLat, transformExtent } from 'ol/proj'; +import { boundingExtent } from 'ol/extent'; +import { Button, UncontrolledTooltip } from 'reactstrap'; +import { Icon } from '@iconify/react'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import listOutline from '@iconify/icons-ion/list-outline'; +import createOutline from '@iconify/icons-ion/create-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import earthIcon from '@iconify/icons-ion/earth'; +import pencilIcon from '@iconify/icons-ion/pencil'; +import ImagePopup from '../../components/ImagePopup'; +import ImageSlider from '../../components/ImageSlider'; +import MapTable from '../../components/MapTable'; +import EditFeature from '../../components/EditFeature'; +import DrawingTool from '../../components/DrawingTool'; +import country_indonesia from '../../assets/json/indonesia.json'; +import { Drawer } from 'antd'; +import './PopupContainer.css'; + +class PopupContainer extends Component { + + constructor(props) { + super(props) + this.state = { + imagePopupVisible: false, + imagePopupTitle: '', + mapTableVisible: false, + editFeatureVisible: false, + editFeatureTitle: '', + editGeometryVisible: false, + selectedPopupData: null, + featureExtent: null, + // zoomLevelOnFeature: 18, + // zoomDurationOnFeature: 500, + mapProjection: this.props.olmap.getView().getProjection(), + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + } + } + } + + componentDidMount() { + // this.setState() + } + + fillPopupContent = () => { + const { popupDataTemp } = this.props; + if (popupDataTemp.length === 1) { + return this.oneFeatureContent(popupDataTemp[0]); + } + else if (popupDataTemp.length > 1) { + return this.moreFeatureContent(); + } + } + + oneFeatureContent = (selectedPopupData) => { + // this.setState({selectedPopupData: selectedPopupData}); // set the selectedPopupData + this.selectFeature(selectedPopupData); + const popupId = selectedPopupData.id; + const popupProperties = selectedPopupData.properties; + + return ( +
+
+

{ popupId }

+
+ {/*
this.showImageFeature(selectedPopupData)}>*/} +
+ +
+
+ + + { this.renderOneRowFeature(popupProperties) } + +
+
+
+
+ { this.renderButtonFooter(selectedPopupData) } +
+
+
+ ) + } + + renderOneRowFeature = (popupProperties) => { + const row = []; + for (let key in popupProperties) { + row.push({key} : {popupProperties[key]}) + } + return row; + } + + renderButtonFooter = (selectedPopupData) => { + return( + + + + Show Images + + {/* + + Show Table + */} + + + Go to Feature + + + + Edit Attribute + + + + Edit Geometry + + + + Delete Feature + + + ) + } + + selectFeature = (selectedPopupData) => { + console.log('selectFeature', selectedPopupData); + this.props.removeChosenLayer(); + const select = new Select(); + this.props.olmap.addInteraction(select); + let geomToDisplay = null; + + let geomType = selectedPopupData.geometry.type; + let coords = selectedPopupData.geometry.coordinates; + let { mapProjection } = this.state; + if (geomType === "Point") { + geomToDisplay = new Point([coords[0], coords[1]]).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPoint") { + + } + else if (geomType === "LineString") { + geomToDisplay = new LineString(coords[0]).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiLineString") { + + } + else if (geomType === "Polygon") { + geomToDisplay = new Polygon(coords[0]).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPolygon") { + geomToDisplay = new MultiPolygon(coords).transform('EPSG:4326', mapProjection); + } + + let styleSelected = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + // color: '#2b2d80', + color: '#1e00ff', + width: 3 + }), + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#1e00ff', + width: 3 + }) + }) + }); + + // let polygon = new MultiPolygon(country_indonesia.geometry.coordinates).transform('EPSG:4326', 'EPSG:3857') + + let chosenFeature = new Feature({ + geometry: geomToDisplay + }) + + console.log('chosenFeature', chosenFeature); + console.log(chosenFeature.getGeometry()); + console.log('getExtent()', chosenFeature.getGeometry().getExtent()); + + // this.setState({featureExtent: chosenFeature.getGeometry().getExtent()}); + + let chosenLayer = new VectorLayer({ + name: 'ChosenLayer', + source: new VectorSource({ + features: [chosenFeature] + }), + style: styleSelected, + type: 'vector' + }); + + this.props.olmap.addLayer(chosenLayer); + // this.setState({featureExtent: chosenFeature.getGeometry().getExtent()}); + } + + moreFeatureContent = () => { + const { popupDataTemp } = this.props; + return ( +
+
+

Choose one feature

+
+
+ + + { popupDataTemp.map((item, index) => { + return ( this.chooseOneFeature(item)}>) + }) } + +
{item.id}
+
+
+
+ +
+
+
+ ) + } + + chooseOneFeature = (feature) => { + // this.setState({popupDataTemp: [feature]}, () => { + // this.fillPopupContent(); + // }); + this.props.setPopupDataTemp([feature]); + this.fillPopupContent(); + } + + showImageFeature = (selectedPopupData) => { + console.log('show images', selectedPopupData); + this.setState({imagePopupTitle: selectedPopupData.id}); + this.toggleImagePopup(); + } + + toggleImagePopup = () => { + this.setState({imagePopupVisible: !this.state.imagePopupVisible}); + } + + showTable = (selectedPopupData) => { + this.toggleShowTable(); + } + + toggleShowTable = () => { + this.setState({mapTableVisible: !this.state.mapTableVisible}) + } + + editFeature = (selectedPopupData) => { + console.log('edit feature', selectedPopupData); + this.setState({selectedPopupData: selectedPopupData}, () => this.toggleEditFeature()); + } + + toggleEditFeature = () => { + this.setState({editFeatureVisible: !this.state.editFeatureVisible}) + } + + /*goToFeature = (selectedPopupData) => { + console.log('goToFeature', selectedPopupData); + const fitOption = { + size: this.props.olmap.getSize(), + duration: 500 + } + // const proj = "CRS:84"; + const proj = "EPSG:4326"; + const coords = selectedPopupData.geometry.coordinates; + const { mapProjection, zoomLevelOnFeature, zoomDurationOnFeature } = this.state; + let extent = null; + + let indoGeom = [ + [ + 120.715609, + -10.239581 + ], + [ + 120.295014, + -10.25865 + ], + [ + 118.967808, + -9.557969 + ], + [ + 119.90031, + -9.36134 + ], + [ + 120.425756, + -9.665921 + ], + [ + 120.775502, + -9.969675 + ], + [ + 120.715609, + -10.239581 + ] + ]; + // let polygon = new Polygon([indoGeom]).transform('EPSG:4326', 'EPSG:3857'); + + if (selectedPopupData.geometry.type === "Point") { + // extent = new Point([coords[0], coords[1]]).transform('EPSG:4326', 'EPSG:3857'); + // extent = new Point([coords[0], coords[1]]); + extent = [coords[0], coords[1]]; + // console.log(extent); + // return this.props.olmap.getView().setCenter(extent); + // this.props.olmap.getView().fit(indoGeom, fitOption); + // this.props.olmap.getView().centerOn(coords, this.props.olmap.getSize(), this.props.olmap.getView().getResolution()); + // return this.props.olmap.getView().fit(new transformExtent(extent, proj, this.state.mapProjection), fitOption); + this.props.olmap.getView().setCenter(fromLonLat(extent, mapProjection)); + // this.props.olmap.getView().setZoom(zoomLevelOnFeature); + this.props.olmap.getView().animate({ + zoom: zoomLevelOnFeature, + duration: zoomDurationOnFeature + }) + } + }*/ + + goToFeature = (selectedPopupData) => { + // get chosenLayer -> get the chosenFeature -> get geometry -> get extent -> fit to map + const { fitOption } = this.state; + const proj = "EPSG:3857"; + let chosenLayer = null; + let chosenFeatures = null; + let featureExtent = null; + + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + return; + } + }) + console.log('go to feature chosenLayer', chosenLayer); + if (chosenLayer === null) { + alert('No chosen feature'); + return; + } + + chosenFeatures = chosenLayer.getSource().getFeatures(); + featureExtent = chosenFeatures[0].getGeometry().getExtent() + + console.log(chosenFeatures); + console.log(featureExtent); + + this.props.olmap.getView().fit(new transformExtent(featureExtent, proj, this.props.olmap.getView().getProjection()), fitOption); + } + + editGeometry = (selectedPopupData) => { + console.log('editGeometry', selectedPopupData); + this.toggleEditGeometry(); + } + + toggleEditGeometry = () => { + this.setState({editGeometryVisible: !this.state.editGeometryVisible}) + } + + deleteFeature = (selectedPopupData) => { + console.log('delete feature', selectedPopupData); + } + + render() { + return( + this.props.closePopupRight()} + visible={this.props.popupRightVisible} + mask={false} + width={360} + style={{top: '50px', height: 'calc(100vh - 50px)'}} + > +
+ {this.fillPopupContent()} + { + this.state.imagePopupVisible && + this.toggleImagePopup()} + imagePopupTitle={this.state.imagePopupTitle ? this.state.imagePopupTitle : 'Feature Image'} + /> + } + { + this.state.editFeatureVisible && + this.toggleEditFeature()} + editFeatureTitle={this.state.editFeatureTitle ? this.state.editFeatureTitle : 'Edit Attribute'} + data={this.state.selectedPopupData} + /> + } + { + this.state.mapTableVisible && + + } + { + this.state.editGeometryVisible && + + } +
+
+ ) + + + + } +} + + +export default PopupContainer; \ No newline at end of file diff --git a/src/components/PopupContainer/package.json b/src/components/PopupContainer/package.json new file mode 100644 index 0000000..bec4b3a --- /dev/null +++ b/src/components/PopupContainer/package.json @@ -0,0 +1,6 @@ +{ + "name": "PopupContainer", + "version": "0.0.0", + "private": true, + "main": "./PopupContainer.js" +} diff --git a/src/components/QueryBuilder/QueryBuilder.js b/src/components/QueryBuilder/QueryBuilder.js new file mode 100644 index 0000000..16894a9 --- /dev/null +++ b/src/components/QueryBuilder/QueryBuilder.js @@ -0,0 +1,248 @@ +import React, { Component } from 'react'; +import { Button,Modal,ModalHeader,ModalBody,ModalFooter,Label,Input } from 'reactstrap'; +import {Query, Builder, BasicConfig, Utils as QbUtils} from 'react-awesome-query-builder'; +import AntdConfig from 'react-awesome-query-builder/lib/config/antd'; +import 'antd/dist/antd.css'; +import 'react-awesome-query-builder/lib/css/styles.css'; +import { QUERY_BUILDER_FIELD_SALES,QUERY_BUILDER_FIELD_OFFICE,QUERY_BUILDER_FIELD_CUSTOMER } from '../../const/CustomFunc.js' + +const InitialConfig = AntdConfig; + +// You need to provide your own config. See below 'Config format' +const config = { + ...InitialConfig, + fields: { + username: { + label: 'Username', + type: 'text', + valueError:[null], + valueSources: ['value'], + }, + } + }; + +// You can load query value from your backend storage (for saving see `Query.onChange()`) +const queryValue = { + "id": "a8aa88a9-0123-4456-b89a-b17a1e7592fc", + "type": "group", + properties:{ + conjunction: "AND" + }, +}; + + +const querySales = { + "id": "a8aa88a9-0123-4456-b89a-b17a1e7592fc", + "type": "group", + properties:{ + conjunction: "AND" + }, + children1: { + "9b88abab-0123-4456-b89a-b17a2d3926c4":{ + type:"rule", + "properties": { + "field": null, + "operator": null, + "value": [], + "valueSrc": [], + } + }, + } +}; + +const queryCustomer = { + "id": "a8aa88a9-0123-4456-b89a-b17a1e7592fc", + "type": "group", + properties:{ + conjunction: "AND" + }, + children1: { + "98999aba-cdef-4012-b456-717a2d9ed196":{ + type:"rule", + "properties": { + "field": null, + "operator": null, + "value": [], + "valueSrc": [] + } + }, + } +}; + +const queryOffice = { + "id": "a8aa88a9-0123-4456-b89a-b17a1e7592fc", + "type": "group", + properties:{ + conjunction: "AND" + }, + children1: { + "98999aba-cdef-4012-b456-717a2d9ed196":{ + type:"rule", + "properties": { + "field": null, + "operator": null, + "value": [], + "valueSrc": [] + } + }, + } +}; + +class QueryBuilder extends Component { + + constructor(props) { + + super(props); + this.state = { + tree: QbUtils.checkTree(QbUtils.loadTree(queryValue), config), + config: config, + output:'', + salesColumn:'', + customerColumn:'', + officeColumn:'', + // currentQuery:'Sales', + currentQuery:'Project', + availableColumn: [], + jsonTree:'' + } + } + + + async componentDidMount(){ + this.setUpQueryBuilder(true); + } + + handleTypeQuery = (e) => { + this.setState({ currentQuery:e.target.value },()=>{ + this.setUpQueryBuilder(false) + }); + } + + + setUpQueryBuilder = async (first) => { + const { currentQuery } = this.state + let queryType = currentQuery + + if(first){ + if(this.props.currentQbTree!==''){ + if(this.props.currentQbType!==''){ + queryType = this.props.currentQbType; + this.setState({ + currentQuery:this.props.currentQbType, + }) + } + } + } + + let fields = ''; + let querySet = ''; + if(queryType==="Sales"){ + fields = QUERY_BUILDER_FIELD_SALES + querySet = querySales; + }else if(queryType==="Customer"){ + fields = QUERY_BUILDER_FIELD_CUSTOMER + querySet = queryCustomer; + }else if(queryType==="Office"){ + fields = QUERY_BUILDER_FIELD_OFFICE + querySet = queryOffice + } + + if(first){ + if(this.props.currentQbTree!==''){ + if(this.props.currentQbType!==''){ + querySet = this.props.currentQbTree + } + } + } + + // console.log("field", fields) + const nextConfig = { + ...InitialConfig, + fields:fields + }; + + if(first!=="prev"){ + await this.setState({ tree:QbUtils.checkTree(QbUtils.loadTree(querySet), nextConfig),config:nextConfig }) + } + } + + handleClose = type => { + if(type==="submit"){ + this.props.handleQbClose(this.state.output, this.state.currentQuery, this.state.jsonTree); + }else if(type==="reset"){ + this.props.handleQbReset(this.state.currentQuery); + this.props.toggle(); + }else{ + this.props.toggle(); + } + } + + renderBuilder = (props) => { + return ( +
+
+ +
+
+ ) + } + + // renderResult = ({tree: immutableTree, config}) => { + // return ( + //
+ //
Query string:
{JSON.stringify(QbUtils.queryString(immutableTree, config))}
+ //
MongoDb query:
{JSON.stringify(QbUtils.mongodbFormat(immutableTree, config))}
+ //
SQL where:
{JSON.stringify(QbUtils.sqlFormat(immutableTree, config))}
+ //
JsonLogic:
{JSON.stringify(QbUtils.jsonLogicFormat(immutableTree, config))}
+ //
+ // ) + // } + + onChange = (immutableTree, config) => { + // Tip: for better performance you can apply `throttle` - see `examples/demo` + this.setState({tree: immutableTree, config: config}); + + let output = QbUtils.sqlFormat(immutableTree, config) + let jsonTree = QbUtils.getTree(immutableTree); + output = "SELECT * FROM ? WHERE "+output + this.setState({ output:output,jsonTree }) + // `jsonTree` can be saved to backend, and later loaded to `queryValue` + } + + + render() { + return( +
+ + Query Builder + +
+ + + {/* + + */} + + +
+ +
+ + {' '} + {' '} + + +
+
+ ) + } +} + +export default QueryBuilder; \ No newline at end of file diff --git a/src/components/RoutingBar/RoutingBar.css b/src/components/RoutingBar/RoutingBar.css new file mode 100644 index 0000000..96bf192 --- /dev/null +++ b/src/components/RoutingBar/RoutingBar.css @@ -0,0 +1,79 @@ +.routingbar-container { + /*text-align: center;*/ +} + +.routingbar-content { + text-align: center; + display: block; + position: relative; + top: 60px; + /*left: 45%;*/ + /*margin-left: -50px;*/ + /*min-width: 175px;*/ + /*max-width: 100%;*/ + margin-left: 400px; + margin-right: 400px; + padding: 5px; + z-index: 50; + background-color: rgba(0,0,0,0.5); + border-radius: 10px; +} + +.routingbar-header-content { + color: #ffffff; + line-height: 17px; + margin-top: 10px; +} + +.routingbar-title { + font-size: 18px; + font-weight: bold; +} + +.routingbar-sales-name { + font-size: 12px; +} + +.routingbar-close { + float: right; + margin-right: 5px; + margin-top: -60px; + cursor: pointer; + color: #ffffff; +} + +.routingbar-body-content { + margin-bottom: 5px; +} + +.routingbar-label-container { + text-align: left !important; + margin-left: 63px; + margin-bottom: 5px; + font-weight: 700; +} + +.routingbar-label { + color: #ffffff; + font-size: 12px; +} + +.routingbar-range-picker { + margin-bottom: 10px; + display: inline-block; +} + +.routingbar-apply-button-container { + display: inline-block; + margin-left: 10px; + margin-top: -100px; + top: -100px; +} + +.routingbar-apply-button { + margin-top: -5px !important; +} + +.text-white { + color: #FFFFFF; +} \ No newline at end of file diff --git a/src/components/RoutingBar/RoutingBar.js b/src/components/RoutingBar/RoutingBar.js new file mode 100644 index 0000000..bf0bcfa --- /dev/null +++ b/src/components/RoutingBar/RoutingBar.js @@ -0,0 +1,124 @@ +import React, { Component } from 'react'; +import { Button, ButtonGroup, UncontrolledTooltip } from 'reactstrap'; +import { Icon, InlineIcon } from '@iconify/react'; +import closeCircleOutline from '@iconify/icons-ion/close-circle-outline'; +import checkmarkCircleOutline from '@iconify/icons-ion/checkmark-circle-outline'; +import turfDestination from '@iconify/icons-geo/turf-destination'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import addCircleOutline from '@iconify/icons-ion/add-circle-outline'; +import removeCircleOutline from '@iconify/icons-ion/remove-circle-outline'; +import {Draw, Modify, Snap, Select, DragBox, DragAndDrop, Translate} from 'ol/interaction'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Vector as VectorSource} from 'ol/source'; +import {Circle as CircleStyle, Fill, Stroke, Style, Text} from 'ol/style'; +import { AppSwitch } from '@coreui/react'; +import DigitizeButton from '@terrestris/react-geo/dist/Button/DigitizeButton/DigitizeButton'; +import './RoutingBar.css'; +import { updateFeatureGeometry } from '../../const/GeoserverFunc.js'; +import { DatePicker } from 'antd'; +import moment from 'moment'; + +const { RangePicker } = DatePicker; + +class RoutingBar extends Component { + + constructor(props) { + super(props); + this.state = { + userId: null, + dateString: null + } + } + + componentDidMount() { + console.log('RoutingBar this.props.popupDataTemp', this.props.popupDataTemp); + } + + onChange = (value, dateString) => { + console.log('Selected Time: ', value); + console.log('Formatted Selected Time: ', dateString); + } + + onOk = (value) => { + + let dateString = []; + dateString[0] = moment(value[0]).format("YYYY-MM-DD HH:mm"); + dateString[1] = moment(value[1]).format("YYYY-MM-DD HH:mm"); + + let userId = null; + if (this.props.popupDataTemp && this.props.popupDataTemp.length > 0) { + // if (this.props.popupDataTemp[0].properties.id && this.props.popupDataTemp[0].properties.id.includes(".")) { + // userId = this.props.popupDataTemp[0].properties.id.split('.')[1]; + // } + // else { + // userId = this.props.popupDataTemp[0].properties.id; + // } + // if (this.props.popupDataTemp[0]) + if (this.props.popupDataTemp[0].properties.id) { + // userId = this.props.popupDataTemp[0].properties.id; + userId = this.props.popupDataTemp[0].properties.user_id; + } + } + + console.log('userId', userId); + console.log('onOk: ', value); + console.log('Formatted Selected Time: ', dateString); + + this.setState({userId: userId, dateString: dateString}); + } + + applyRouting = () => { + const { userId, dateString } = this.state; + if (userId && dateString) { + this.props.searchRouting(userId, dateString); + } + } + + render() { + + // const { snapMode, modifyMode, dragMode, isMultiGeom } = this.state; + + return ( +
+
+

+ User Route
+ {this.props.popupDataTemp && this.props.popupDataTemp.length > 0 ? this.props.popupDataTemp[0].properties.name : ""}
+

+ { + this.props.toggleRoutingBarVisible(); + this.props.setRouteType(''); + }}> + +
+ Select Start and End Time +
+ +
+
+ +
+ +
+ +
+
+
+
+ ) + + } +} + +export default RoutingBar; \ No newline at end of file diff --git a/src/components/RoutingBar/package.json b/src/components/RoutingBar/package.json new file mode 100644 index 0000000..4a09ece --- /dev/null +++ b/src/components/RoutingBar/package.json @@ -0,0 +1,6 @@ +{ + "name": "RoutingBar", + "version": "0.0.0", + "private": true, + "main": "./RoutingBar.js" +} diff --git a/src/components/SearchFeatures/SearchFeatures.css b/src/components/SearchFeatures/SearchFeatures.css new file mode 100644 index 0000000..5748b4c --- /dev/null +++ b/src/components/SearchFeatures/SearchFeatures.css @@ -0,0 +1,52 @@ +.search-feature-wrapper { + height: 100%; +} + +.input-search-feature { + margin-top: -5px; +} + +.list-feature-wrapper { + margin-top: 20px; + overflow: auto; + height: 60vh; +} + +.list-feature { + /*width: 100%;*/ + cursor: pointer; +} + +.list-feature:hover { + background-color: #e4e5e6; +} + +.list-icon { + /*width: 80%;*/ +} + +.list-name { + /*width: 20%;*/ +} + +.search-info { + margin-top: 15px; + font-size: 13px; +} + +.list-active { + background-color: #e4e5e6; +} + +.list-detail { + /*width: 100%;*/ + /*width: 100px;*/ + /*white-space: nowrap;*/ + overflow: hidden; + text-overflow: ellipsis; +} + +.list-layer-title { + word-wrap: break-word; +} + diff --git a/src/components/SearchFeatures/SearchFeatures.js b/src/components/SearchFeatures/SearchFeatures.js new file mode 100644 index 0000000..e8221a8 --- /dev/null +++ b/src/components/SearchFeatures/SearchFeatures.js @@ -0,0 +1,346 @@ +import React, { Component, Fragment } from 'react' +import './SearchFeatures.css' +import '../../assets/css/customscroll.css' +import { Input, ListGroup, ListGroupItem, Row, Col, Badge } from 'reactstrap' + +// For Select feature then go to map +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Vector as VectorSource } from 'ol/source'; +import { fromLonLat, transformExtent } from 'ol/proj'; + +import { Drawer } from 'antd'; +import PopupContainer from '../PopupContainer'; +import { Icon, InlineIcon } from '@iconify/react'; +import locationPoint from '@iconify/icons-uil/location-point'; +import lineChart from '@iconify/icons-fe/line-chart'; +import drawPolygon from '@iconify/icons-fa-solid/draw-polygon'; +import { lightOrDark } from '../../const/GeoserverFunc.js'; + +class SearchFeatures extends Component { + constructor(props) { + super(props); + this.state = { + searchInput: '', + allFeatures: [], + // searchDataFeatures: [], + // For Select Feature + mapProjection: this.props.olmap.getView().getProjection(), + fitOption: { + size: this.props.olmap.getSize(), + duration: 500, + maxZoom: 18 + } + } + } + + componentDidMount() { + if (this.props.allFeatures !== undefined) { + this.setState({allFeatures: this.props.allFeatures}) + } + // if (this.props.searchDataFeatures !== undefined) { + // this.setState({searchDataFeatures: this.props.searchDataFeatures}) + // } + } + + componentDidUpdate = (prevProps, prevState) => { + + if (prevProps.allFeatures !== this.props.allFeatures) { + this.setState({allFeatures: this.props.allFeatures}) + } + + } + + handleChangeSearchInput = (event) => { + event.preventDefault(); + let value = event.target.value.toLowerCase(); + this.setState({searchInput: value}, () => { + this.returnSearchData(this.state.searchInput); + }); + } + + returnSearchData = (value) => { + let filteredFeatures = []; + if (value === '') { + return this.state.allFeatures; + } + else { + filteredFeatures = this.state.allFeatures.filter((result) => { + if (result.id.toLowerCase().includes(this.state.searchInput)) { + return result.id.toLowerCase().includes(this.state.searchInput); + } + else { + for (let key in result.properties) { + if (result.properties[key] !== null ) { + if (result.properties[key].toString().toLowerCase().includes(this.state.searchInput)) { + return result.properties[key].toString().toLowerCase().includes(this.state.searchInput); + } + } + } + } + }); + return filteredFeatures; + } + } + + // When user select the row of table (creating vector layer named chosenLayer) + selectFeature = (selectedPopupData) => { + console.log('SearchFeatures selectFeature', selectedPopupData); + this.removeChosenLayer(); + const select = new Select(); + this.props.olmap.addInteraction(select); + let geomToDisplay = null; + + if (selectedPopupData.geometry === null) { + alert('Could not find the geometry'); + return; + } + + let geomType = selectedPopupData.geometry.type; + let coords = selectedPopupData.geometry.coordinates; + let { mapProjection } = this.state; + if (geomType === "Point") { + geomToDisplay = new Point(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPoint") { + geomToDisplay = new MultiPoint(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "LineString") { + geomToDisplay = new LineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiLineString") { + geomToDisplay = new MultiLineString(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "Polygon") { + geomToDisplay = new Polygon(coords).transform('EPSG:4326', mapProjection); + } + else if (geomType === "MultiPolygon") { + geomToDisplay = new MultiPolygon(coords).transform('EPSG:4326', mapProjection); + } + + let styleSelected = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + // color: '#2b2d80', + color: '#1e00ff', + width: 3 + }), + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.7)' + }), + stroke: new Stroke({ + color: '#1e00ff', + width: 3 + }) + }) + }); + + // let polygon = new MultiPolygon(country_indonesia.geometry.coordinates).transform('EPSG:4326', 'EPSG:3857') + + let chosenFeature = new Feature({ + geometry: geomToDisplay + }) + + let chosenLayer = new VectorLayer({ + name: 'ChosenLayer', + source: new VectorSource({ + features: [chosenFeature] + }), + style: styleSelected, + type: 'vector' + }); + + this.props.olmap.addLayer(chosenLayer); + } + + // removing existing ChosenLayer + removeChosenLayer = () => { + let layersToRemove = []; + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + // console.log('removeChosenLayer', layer); + layersToRemove.push(layer); + } + }); + + if (layersToRemove.length > 0) { + for(let i=0; i < layersToRemove.length; i++) { + this.props.olmap.removeLayer(layersToRemove[i]); + } + } + } + + // When user click on search list of feature, then go to feature + goToFeature = (selectedFeature) => { + + this.selectFeature(selectedFeature); + + // get chosenLayer -> get the chosenFeature -> get geometry -> get extent -> fit to map + const { fitOption } = this.state; + const proj = "EPSG:3857"; + let chosenLayer = null; + let chosenFeatures = null; + let featureExtent = null; + + this.props.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === 'ChosenLayer') { + chosenLayer = layer; + return; + } + }) + + if (chosenLayer === null) { + alert('Could not go to feature because there is no geometry to go'); + return; + } + + chosenFeatures = chosenLayer.getSource().getFeatures(); + console.log('SearchFeatures chosenFeatures', chosenFeatures); + featureExtent = chosenFeatures[0].getGeometry().getExtent() + + this.props.olmap.getView().fit(new transformExtent(featureExtent, proj, this.props.olmap.getView().getProjection()), fitOption); + + this.props.setPopupDataTemp(selectedFeature); + this.props.openPopupRight(); + } + + renderIcon = (item) => { + if (item.geometry) { + let geomType = item.geometry.type.toLowerCase(); + if (geomType.includes('point')) { + return + } + else if (geomType.includes('linestring')) { + return + } + else if (geomType.includes('polygon')) { + return + } + } + } + + getFeaturesLabel = (item) => { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + let { searchLabelData } = this.props; + let matchLayerName = searchLabelData.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return item.properties[matchLayerName[0].attribute_name]; + } + else { + return item.id; + } + } + + getLayerTitle = (item) => { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + // let { searchLabelData } = this.props; + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + // console.log('getLayerTitle matchLayerName', matchLayerName); + // let output = ''; + + if (matchLayerName.length > 0) { + if (matchLayerName[0].layer_title !== '' && matchLayerName[0].layer_title !== null) { + return matchLayerName[0].layer_title; + // output = matchLayerName[0].layer_title; + // return output; + } + else { + return matchLayerName[0].layer_name; + // output = matchLayerName[0].layer_title; + // return output; + } + } + else { + return "No layer title provided"; + // return matchLayerName[0].layer_name; + } + } + + getLayerColor = (item) => { + let layer_name = item.id.substr(0, item.id.indexOf(".")); + // console.log('getLayerColor', layer); + // let layer_name = layer.get('name'); + let { layerInfo } = this.props; + let matchLayerName = layerInfo.filter(data => (data.layer_name == layer_name)); + + if (matchLayerName.length > 0) { + return matchLayerName[0].layer_color; + } + else { + return "#ffffff"; + } + } + + getLayerTitleTextColor = (layerColor) => { + let brightness = lightOrDark(layerColor); // 'light' or 'dark' + + if (brightness == 'light') { + return 'dark-text'; + } + else { + return 'light-text'; + } + } + + renderList = () => { + let searchData = this.returnSearchData(this.state.searchInput); + if (searchData.length > 0) { + return searchData.map((item, index) => { + return ( this.goToFeature(item)} + > + +
{ this.renderIcon(item) }
+ {/*
{item.id}
*/} + +
+
{ this.getFeaturesLabel(item) }
+
{this.getLayerTitle(item)}
+
+ +
+
) + }) + } + else { + return No feature found + } + + } + + render() { + let { allFeatures, searchInput } = this.state; + return( +
+
+ +
+
+ + { this.renderList() } + +
+
+ { allFeatures !== undefined ? + `Filtered ${ this.returnSearchData(searchInput).length < 2 ? this.returnSearchData(searchInput).length+' feature' : this.returnSearchData(searchInput).length+' features' } + from ${ allFeatures.length < 2 ? allFeatures.length+' feature' : allFeatures.length+' features'}` + : null + } +
+
+ ) + } +} + +export default SearchFeatures; \ No newline at end of file diff --git a/src/components/SearchFeatures/package.json b/src/components/SearchFeatures/package.json new file mode 100644 index 0000000..8fc8213 --- /dev/null +++ b/src/components/SearchFeatures/package.json @@ -0,0 +1,6 @@ +{ + "name": "SearchFeatures", + "version": "0.0.0", + "private": true, + "main": "./SearchFeatures.js" +} diff --git a/src/components/TopLoadingBar/TopLoadingBar.js b/src/components/TopLoadingBar/TopLoadingBar.js new file mode 100644 index 0000000..bd215a8 --- /dev/null +++ b/src/components/TopLoadingBar/TopLoadingBar.js @@ -0,0 +1,40 @@ +import React, { Component } from 'react' + +import LoadingBar from 'react-top-loading-bar' + + +class TopLoadingBar extends Component { + state = { + loadingBarProgress: 0 + } + + add = value => { + this.setState({ + loadingBarProgress: this.state.loadingBarProgress + value + }) + } + + complete = () => { + this.setState({ loadingBarProgress: 100 }) + } + + onLoaderFinished = () => { + this.setState({ loadingBarProgress: 0 }) + } + + render() { + return ( +
+ this.onLoaderFinished()} + /> + {/* + + */} +
+ ) + } +} \ No newline at end of file diff --git a/src/components/TopLoadingBar/package.json b/src/components/TopLoadingBar/package.json new file mode 100644 index 0000000..a49c59f --- /dev/null +++ b/src/components/TopLoadingBar/package.json @@ -0,0 +1,6 @@ +{ + "name": "TopLoadingBar", + "version": "0.0.0", + "private": true, + "main": "./TopLoadingBar.js" +} diff --git a/src/const/ApiConst.js b/src/const/ApiConst.js new file mode 100644 index 0000000..06284fa --- /dev/null +++ b/src/const/ApiConst.js @@ -0,0 +1,593 @@ +import { appConfig, MAP_ID } from './MapConst'; +// import axios from 'axios' +// import { createBrowserHistory } from 'history'; +// const history = createBrowserHistory(); +const roleName = window.localStorage.getItem('role_name'); + +// const token = localStorage.getItem('apiKey') +// axios.interceptors.request.use( +// async config => { +// // const value = await redisClient.get(rediskey) +// // const keys = JSON.parse(value) +// return config; +// }, +// error => { +// Promise.reject(error) +// }); + +// // Response interceptor for API calls +// export const AXIOS = axios.interceptors.response.use((response) => { +// console.log("token not expire") +// return response +// }, async function (error) { +// // const originalRequest = error.config; +// if (error.response.status == 401) { +// console.log("error", error.response) +// console.log("token expire") +// localStorage.clear(); +// window.location = window.location.origin; +// } +// return Promise.reject(error); +// }); + +// const HOST = "http://127.0.0.1"; // local +// const HOST = "http://192.168.99.110"; // local vagrant Ardhi +const HOST = "https://osmap.id"; // online + +export const BASE_URL_GEOHR_API = "https://oslog.id/geohr-api"; +export const BASE_URL_GEOHR_API2 = "http://siopas.co.id/custom-php/api/geohr"; + +export const BASE_API = `${HOST}/osmap-php/index.php`; + +export const BASE_IMAGE = `${HOST}/osmap-php`; +export const BASE_IMAGE_EMPLOYEE = "https://oslog.id/geohr-api/assets/images/employee/" + +export const SIOPAS_API = `${BASE_API}/siopas` + +export const API_LOGIN = `${SIOPAS_API}/user/login` + +export const API_LOG = `${SIOPAS_API}/user/log` + +export const API_USER_SESSION = `${SIOPAS_API}/user/get_valid_session` + +// http://192.168.99.110/fnc/osmap-php/index.php/map/getColumnNameAndType" +export const API_GET_COLUMN_TABLE = `${BASE_API}/map/getColumnNameAndType` +export const API_CREATE_NEW_LAYER = `${BASE_API}/map/createlayer` + +// Load and Save Map View +export const API_LOAD_MAP = `${SIOPAS_API}/map/load/${MAP_ID}` +export const API_UPDATE_MAP = `${SIOPAS_API}/map/update` + +// Upload and Get Image Popup +// export const API_UPLOAD_IMAGE = `${SIOPAS_API}/uploadimage` +export const API_UPLOAD_IMAGE = `${SIOPAS_API}/feature/uploadimage` +// export const API_GET_IMAGE = `${SIOPAS_API}/uploadimage/getimage` +export const API_GET_IMAGE = `${SIOPAS_API}/feature/getimage` + +// Update Feature +export const API_UPDATE_FEATURE = `${SIOPAS_API}/feature/update` + +// Dashboard Chart API +export const API_GET_CHART_TIPE_GEOMETRY = `${SIOPAS_API}/dashboard/chart/tipe-geometry?map_id=${MAP_ID}` +export const API_GET_CHART_KATEGORI = `${SIOPAS_API}/dashboard/chart/tipe-kategori?map_id=${MAP_ID}` + +export const API_LIST_DATA_COUNTRY = `${SIOPAS_API}/country` +export const API_INSERT_DATA_COUNTRY = `${API_LIST_DATA_COUNTRY}/insertDataCountry` +export const API_UPDATE_DATA_COUNTRY = `${API_LIST_DATA_COUNTRY}/updateDataCountry` +export const API_DELETE_DATA_COUNTRY = `${API_LIST_DATA_COUNTRY}/deleteDataCountry` + +export const API_LIST_DATA_DISTRICT = `${SIOPAS_API}/district` +export const API_INSERT_DATA_DISTRICT = `${API_LIST_DATA_DISTRICT}/insertDataDistrict` +export const API_UPDATE_DATA_DISTRICT = `${API_LIST_DATA_DISTRICT}/updateDataDistrict` +export const API_DELETE_DATA_DISTRICT = `${API_LIST_DATA_DISTRICT}/deleteDataDistrict` + +export const API_LIST_DATA_SUBDISTRICT = `${SIOPAS_API}/subdistrict` +export const API_INSERT_DATA_SUBDISTRICT = `${API_LIST_DATA_SUBDISTRICT}/insertDataSubdistrict` +export const API_UPDATE_DATA_SUBDISTRICT = `${API_LIST_DATA_SUBDISTRICT}/updateDataSubdistrict` +export const API_DELETE_DATA_SUBDISTRICT = `${API_LIST_DATA_SUBDISTRICT}/deleteDataSubdistrict` + +export const API_LIST_DATA_PROVINCE = `${SIOPAS_API}/province` +export const API_UPDATE_DATA_PROVINCE = `${API_LIST_DATA_PROVINCE}/updateDataProvince` +export const API_INSERT_DATA_PROVINCE = `${API_LIST_DATA_PROVINCE}/insertDataProvince` +export const API_DELETE_DATA_PROVINCE = `${API_LIST_DATA_PROVINCE}/deleteDataProvince` + +export const API_LIST_DATA_VILLAGE = `${SIOPAS_API}/village` +export const API_UPDATE_DATA_VILLAGE = `${API_LIST_DATA_VILLAGE}/updateDataVillage` +export const API_INSERT_DATA_VILLAGE = `${API_LIST_DATA_VILLAGE}/insertDataVillage` +export const API_DELETE_DATA_VILLAGE = `${API_LIST_DATA_VILLAGE}/deleteDataVillage` + +export const API_LIST_DATA_USERS = `${SIOPAS_API}/user?group_id=${window.localStorage.getItem('group_id')}` +export const API_INSERT_DATA_USERS = `${API_LIST_DATA_USERS}/insertDataUser` +export const API_UPDATE_DATA_USERS = `${API_LIST_DATA_USERS}/updateDataUser` +export const API_DELETE_DATA_USERS = `${API_LIST_DATA_USERS}/deleteDataUser` + +// Layer Page +export const API_LAYER_LIST = `${SIOPAS_API}/layer?workspace=${appConfig.workspace_name}` +export const API_LAYER_LABEL = `${SIOPAS_API}/layer/getsearchlabel` +export const API_LAYER_DETAIL = `${SIOPAS_API}/layer/?resourcebase_ptr_id=` +export const API_LAYER_DETAIL_UPDATE = `${SIOPAS_API}/layer/updatelayerdetail` +export const API_LAYER_ATTRIBUTE = `${SIOPAS_API}/layer/getlayerattribute/` +export const API_LAYER_ATTRIBUTE_UPDATE = `${SIOPAS_API}/layer/updatelayerattribute` +export const API_LAYER_ATTRIBUTE_BY_LAYERNAME = `${SIOPAS_API}/layer/getlayerattribute?name=` +export const API_LAYER_SEARCH_LABEL = `${SIOPAS_API}/layer/getsearchlabel/` +export const API_LAYER_SEARCH_LABEL_UPDATE = `${SIOPAS_API}/layer/updatelayersearchlabel` + + + +// GEOHR API + +export const API_GEOHR_GROUP_SALES_SEARCH = `${BASE_URL_GEOHR_API}/group-sales/search`; +export const API_GEOHR_SALES_MONITORING = `${BASE_URL_GEOHR_API}/sales/monitoring`; +export const API_GEOHR_SALES_SEARCH = `${BASE_URL_GEOHR_API}/sales/search`; +export const API_GEOHR_OFFICE_MONITORING = `${BASE_URL_GEOHR_API}/office/monitoring`; +export const API_GEOHR_CUSTOMER_MONITORING = `${BASE_URL_GEOHR_API}/customer/monitoring`; +export const API_GEOHR_EMPLOYEE_MONITORING = `${BASE_URL_GEOHR_API}/employee/monitoring`; +export const API_GEOHR_WAYPOINT_SALES_MONITORING = (salesId) => `${BASE_URL_GEOHR_API}/waypoint-sales/monitoring?salesId=${salesId}`; +export const API_GEOHR_WAYPOINT_EMPLOYEE_MONITORING = (employeeId) => `${BASE_URL_GEOHR_API}/waypoint-employee/monitoring?employeeId=${employeeId}`; +// export const API_GEOHR_DAILY_INFO = (typeSales) => `${BASE_URL_GEOHR_API}/dashboard-status-sales?typeSales=${typeSales}`; +export const API_GEOHR_DAILY_INFO = `${BASE_URL_GEOHR_API}/dashboard-status-sales`; +// export const API_GEOHR_WAYPOINT_EMPLOYEE_MONITORING = (employeeId) => `${BASE_URL_GEOHR_API}/waypoint-employee/monitoring?emoloyeeId=${employeeId}`; +export const API_BROADCAST = `${BASE_URL_GEOHR_API}/broadcast` + +// API PHP +export const API_GEOHR_EMPLOYEE_DIVISION_LIST = `${BASE_URL_GEOHR_API2}/employee_devision.php?act=get_data`; +export const API_GEOHR_KARYAWAN = `${BASE_URL_GEOHR_API2}/employee.php?`; +export const API_GEOHR_ROLES = `${BASE_URL_GEOHR_API2}/roles.php?`; +export const API_GEOHR_MENU = `${BASE_URL_GEOHR_API2}/menu.php?`; +export const API_GEOHR_TASK = `${BASE_URL_GEOHR_API2}/task_report.php?`; +export const API_GEOHR_DIVISI_KARYAWAN = `${BASE_URL_GEOHR_API2}/employee_devision.php?`; +export const API_GEOHR_IZIN = `${BASE_URL_GEOHR_API2}/paid_leave.php?`; +export const API_GEOHR_PRESENSI = `${BASE_URL_GEOHR_API2}/clock_in_out.php?`; +export const API_GET_DATA_OFFICE_HOURS = `${BASE_URL_GEOHR_API2}/employee_office_hours.php?`; +export const API_GEOHR_SETTING_OFFICE_HOURS = (type) => `${BASE_URL_GEOHR_API2}/employee_office_hours.php?act=setting&type=${type}`; +export const API_GEOHR_EDIT_OFFICE_HOURS = (id) => `${BASE_URL_GEOHR_API2}/employee_office_hours.php?act=edit&id=${id}`; +export const API_DAILY_INFO = `${BASE_URL_GEOHR_API2}/employee.php?act=get_daily&role_name=${roleName}`; +export const API_DAILY_INFO_DETAIL = (start, length) => `${BASE_URL_GEOHR_API2}/employee.php?act=get_daily_detail&start=${start}&length=${length}&role_name=${roleName}`; + + + +// =========================== OSPRO ======================================// +export const APP_MODE = 'KIT' // KIT / ADW / NAWAKARA / IU +export const BASE_SIMPRO = "https://oslog.id/simpro-api/v1"; +// export const BASE_SIMPRO = "http://localhost:8300/v1"; +export const BASE_SIMPRO_V2 = "https://oslog.id/simpro-api/v2"; +export const BASE_SIMPRO_LUMENNIAGA = "https://ospro-api.odm-iu.com/api"; + +export let BASE_OSPRO = "https://ospro-api.ospro.id"; +export let BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; +// for warna host config +export let BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; +export let BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; + +switch (APP_MODE) { + case 'KIT': + // BASE_OSPRO = "https://ospro.krakatau-it.co.id" + BASE_OSPRO = "https://kit-api.oslogdev.com" + + //for KIT server + // BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; + // BASE_SIMPRO_LUMEN_IMAGE = `${BASE_SIMPRO_LUMEN}/assets/image`; + // BASE_SIMPRO_LUMEN_FILE = `${BASE_SIMPRO_LUMEN}/assets/file/project`; + + break; + case 'ADW': + BASE_OSPRO = "https://ospro-api.ospro.id" + + BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; + BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; + BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; + + break; + case 'IU': + BASE_OSPRO = "https://api-iu.ospro.id" + + BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; + BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; + BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; + + break; + default: + BASE_OSPRO = "https://ospro-api.ospro.id" + // BASE_OSPRO = "https://kit-api.oslogdev.com" + +} + +// export const BASE_OSPRO = "https://ospro.krakatau-it.co.id"; // kit +// export const BASE_OSPRO = "https://ospro-api.ospro.id"; // adyawinsa +// export const BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; + + + + + +//for KIT server +// export const BASE_SIMPRO_LUMEN_IMAGE = `${BASE_SIMPRO_LUMEN}/assets/image`; +// export const BASE_SIMPRO_LUMEN_FILE = `${BASE_SIMPRO_LUMEN}/assets/file/project`; + + +// export const BASE_SIMPRO_LUMEN_R = "https://ospro-api.ospro.id/"; +// export const BASE_SIMPRO_LUMEN_R = `${BASE_OSPRO}/`; +// export const BASE_SIMPRO_LUMEN = "http://localhost:8074/simpro-lumen/api"; + +// export const USER_ADD = `${BASE_SIMPRO}/user/add` +// export const USER_LOGIN = `${BASE_SIMPRO}/user/login` +// export const USER_SEARCH = `${BASE_SIMPRO}/user/search` +// export const USER_LIST = `${BASE_SIMPRO}/user/list` +// export const USER_EDIT = (id) => { return `${BASE_SIMPRO}/user/edit/${id}` } +// export const USER_DELETE = (id) => { return `${BASE_SIMPRO}/user/delete/${id}` } + + +// export const ROLE_ADD = `${BASE_SIMPRO}/role/add` +// export const ROLE_SEARCH = `${BASE_SIMPRO}/role/search` +// export const ROLE_EDIT = (id) => { return `${BASE_SIMPRO}/role/edit/${id}` } +// export const ROLE_DELETE = (id) => { return `${BASE_SIMPRO}/role/delete/${id}` } + +export const USERROLE_ADD = `${BASE_SIMPRO}/user-role/add` +export const USERROLE_SEARCH = `${BASE_SIMPRO}/user-role/search` +export const USERROLE_EDIT = (id) => { return `${BASE_SIMPRO}/user-role/edit/${id}` } +export const USERROLE_DELETE = (id) => { return `${BASE_SIMPRO}/user-role/delete/${id}` } + +export const USERPROYEK_ADD = `${BASE_SIMPRO}/user-to-proyek/add` +export const USERPROYEK_ADD_DETAIL = `${BASE_SIMPRO}/user-to-proyek/add-detail` +export const USERPROYEK_LIST = `${BASE_SIMPRO}/user-to-proyek/list` +export const USERPROYEK_SEARCH = `${BASE_SIMPRO}/user-to-proyek/search` +export const USERPROYEK_EDIT = (id) => { return `${BASE_SIMPRO}/user-to-proyek/edit/${id}` } +export const USERPROYEK_DELETE = (id) => { return `${BASE_SIMPRO}/user-to-proyek/delete/${id}` } + +// export const MENU_ADD = `${BASE_SIMPRO}/menu/add` +// export const MENU_MANAGEMENT = (id) => { return `${BASE_SIMPRO}/menu-management/${id}` } +// export const MENU_SEARCH = `${BASE_SIMPRO}/menu/search` +// export const MENU_EDIT = (id) => { return `${BASE_SIMPRO}/menu/edit/${id}` } +// export const MENU_DELETE = (id) => { return `${BASE_SIMPRO}/menu/delete/${id}` } + +// export const PROYEK_ADD = `${BASE_SIMPRO}/proyek/add` +// export const PROYEK_LIST = `${BASE_SIMPRO}/proyek/list` +// export const PROYEK_SEARCH = `${BASE_SIMPRO}/proyek/search` +// export const PROYEK_SEARCH_DETAIL = `${BASE_SIMPRO}/proyek/search-detail` +// export const PROYEK_EDIT = (id) => { return `${BASE_SIMPRO}/proyek/edit/${id}` } +// export const PROYEK_DELETE = (id) => { return `${BASE_SIMPRO}/proyek/delete/${id}` } + +// export const ROLEMENU_ADD = `${BASE_SIMPRO}/role-menu/add` +// export const ROLEMENU_SEARCH = `${BASE_SIMPRO}/role-menu/search` +// export const ROLEMENU_EDIT = (id) => { return `${BASE_SIMPRO}/role-menu/edit/${id}` } +// export const ROLEMENU_DELETE = (id) => { return `${BASE_SIMPRO}/role-menu/delete/${id}` } +// export const ROLEMENU_DELETE_ROLE = (id) => { return `${BASE_SIMPRO}/role-menu/delete-byrole/${id}` } + +export const PRESENSI_ADD = `${BASE_SIMPRO}/clock-in-out/add` +export const PRESENSI_SEARCH = `${BASE_SIMPRO}/clock-in-out/search` +export const PRESENSI_EDIT = (id) => { return `${BASE_SIMPRO}/clock-in-out/edit/${id}` } +export const PRESENSI_DELETE = (id) => { return `${BASE_SIMPRO}/clock-in-out/delete/${id}` } + +// export const ABSENSI_ADD = `${BASE_SIMPRO}/absensi/add` +// export const ABSENSI_SEARCH = `${BASE_SIMPRO}/absensi/search` +// export const ABSENSI_EDIT = (id) => { return `${BASE_SIMPRO}/absensi/edit/${id}` } +// export const ABSENSI_DELETE = (id) => { return `${BASE_SIMPRO}/absensi/delete/${id}` } + +export const PLANNING_ADD = `${BASE_SIMPRO}/laporan-planning/add` +export const PLANNING_SEARCH = `${BASE_SIMPRO}/laporan-planning/search` +export const PLANNING_EDIT = (id) => { return `${BASE_SIMPRO}/laporan-planning/edit/${id}` } +export const PLANNING_DELETE = (id) => { return `${BASE_SIMPRO}/laporan-planning/delete/${id}` } + +// export const CONFIGALERT_ADD = `${BASE_SIMPRO}/config-alert/add` +// export const CONFIGALERT_SEARCH = `${BASE_SIMPRO}/config-alert/search` +// export const CONFIGALERT_EDIT = (id) => { return `${BASE_SIMPRO}/config-alert/edit/${id}` } +// export const CONFIGALERT_DELETE = (id) => { return `${BASE_SIMPRO}/config-alert/delete/${id}` } + +// export const CALERTUSER_ADD = `${BASE_SIMPRO}/config-alert-to-user/add` +// export const CALERTUSER_SEARCH = `${BASE_SIMPRO}/config-alert-to-user/search` +// export const CALERTUSER_EDIT = (id) => { return `${BASE_SIMPRO}/config-alert-to-user/edit/${id}` } +// export const CALERTUSER_DELETE = (id) => { return `${BASE_SIMPRO}/config-alert-to-user/delete/${id}` } + +export const ALERT_ADD = `${BASE_SIMPRO}/alert/add` +export const ALERT_SEARCH = `${BASE_SIMPRO}/alert/search` +export const ALERT_EDIT = (id) => { return `${BASE_SIMPRO}/alert/edit/${id}` } +export const ALERT_DELETE = (id) => { return `${BASE_SIMPRO}/alert/delete/${id}` } + +export const ALERTUSER_ADD = `${BASE_SIMPRO}/alert-to-user/add` +export const ALERTUSER_SEARCH = `${BASE_SIMPRO}/alert-to-user/search` +export const ALERTUSER_EDIT = (id) => { return `${BASE_SIMPRO}/alert-to-user/edit/${id}` } +export const ALERTUSER_DELETE = (id) => { return `${BASE_SIMPRO}/alert-to-user/delete/${id}` } + +export const ALERTUSER_STATUSVIEW = (id) => { return `${BASE_SIMPRO}/alert-to-user/edit-status-view/${id}` } +export const ALERT_STATUSVIEW = (id) => { return `${BASE_SIMPRO}/alert/edit-status-view/${id}` } + +export const SUBPROYEK_ADD = `${BASE_SIMPRO}/sub-proyek/add` +export const SUBPROYEK_SEARCH = `${BASE_SIMPRO}/sub-proyek/search` +export const SUBPROYEK_EDIT = (id) => { return `${BASE_SIMPRO}/sub-proyek/edit/${id}` } +export const SUBPROYEK_DELETE = (id) => { return `${BASE_SIMPRO}/sub-proyek/delete/${id}` } + +export const USER_PROYEK_SEARCH = `${BASE_SIMPRO}/user-proyek/search` +export const PLANNING_REALISASI_SEARCH = `${BASE_SIMPRO}/planning-realisasi/search` + +export const DASHBOARD_PROYEK = `${BASE_SIMPRO}/dashboard-proyek/list` +export const DASHBOARD_PROYEK_SEARCH = `${BASE_SIMPRO}/dashboard-proyek/search` +// export const IMAGE_SEARCH = `${BASE_SIMPRO}/image/search` +export const SIMPRO_BASE_IMAGE = `https://oslog.id/simpro-api/assets/images` + +export const API_BROADCAST_SIMPRO = `${BASE_SIMPRO}/broadcast` +export const USER_WASPANG = `${BASE_SIMPRO}/user-role/search` +export const USER_TO_PROYEK_SEARCH = `${BASE_SIMPRO}/user-to-proyek` +export const API_SIMPRO_WAYPOINT_WASPANG_MONITORING = (userId) => `${BASE_SIMPRO}/waypoint-monitoring/${userId}/search`; + +// export const K3_ADD = `${BASE_SIMPRO}/laporan-k3/add` +// export const K3_SEARCH = `${BASE_SIMPRO}/laporan-k3/search` +// export const K3_EDIT = (id) => { return `${BASE_SIMPRO}/laporan-k3/edit/${id}` } +// export const K3_DELETE = (id) => { return `${BASE_SIMPRO}/laporan-k3/delete/${id}` } + +export const SATUAN_KERJA_GET = `${BASE_SIMPRO}/satuan/list` + +export const DASHBOARD_STATUS_SEARCH = `${BASE_SIMPRO}/dashboard-status/search` +export const GANTT_CONTROL_MONITORING_SEARCH = `${BASE_SIMPRO}/proyek-monitoring/search-detail` +// export const TYPE_PROYEK = `${BASE_SIMPRO}/type-proyek/list` + +// export const MATERIAL_RESOURCE_ADD = `${BASE_SIMPRO}/material-resource/add` +// export const MATERIAL_RESOURCE_ADD_DETAIL = `${BASE_SIMPRO}/material-resource/add-detail` +// export const MATERIAL_RESOURCE_SEARCH = `${BASE_SIMPRO}/material-resource/search` +// export const MATERIAL_RESOURCE_EDIT = (id) => { return `${BASE_SIMPRO}/material-resource/edit/${id}` } +// export const MATERIAL_RESOURCE_DELETE = (id) => { return `${BASE_SIMPRO}/material-resource/delete/${id}` } + +// export const TOOLS_RESOURCE_ADD = `${BASE_SIMPRO}/tools-resource/add` +// export const TOOLS_RESOURCE_SEARCH = `${BASE_SIMPRO}/tools-resource/search` +// export const TOOLS_RESOURCE_EDIT = (id) => { return `${BASE_SIMPRO}/tools-resource/edit/${id}` } +// export const TOOLS_RESOURCE_DELETE = (id) => { return `${BASE_SIMPRO}/tools-resource/delete/${id}` } + +// export const PROJECT_TYPE_ADD = `${BASE_SIMPRO}/type-proyek/add` +// export const PROJECT_TYPE_SEARCH = `${BASE_SIMPRO}/type-proyek/search` +// export const PROJECT_TYPE_EDIT = (id) => { return `${BASE_SIMPRO}/type-proyek/edit/${id}` } +// export const PROJECT_TYPE_DELETE = (id) => { return `${BASE_SIMPRO}/type-proyek/delete/${id}` } + +// export const PROJECT_ROLE_ADD = `${BASE_SIMPRO}/role-proyek/add` +// export const PROJECT_ROLE_SEARCH = `${BASE_SIMPRO}/role-proyek/search` +// export const PROJECT_ROLE_EDIT = (id) => { return `${BASE_SIMPRO}/role-proyek/edit/${id}` } +// export const PROJECT_ROLE_DELETE = (id) => { return `${BASE_SIMPRO}/role-proyek/delete/${id}` } + +// export const PROJECT_CHARTER_ADD = `${BASE_SIMPRO}/project-charter/add` +// export const PROJECT_CHARTER_EDIT = (id) => { return `${BASE_SIMPRO}/project-charter/edit/${id}` } + +export const MATERIAL_TO_PROYEK_ADD = `${BASE_SIMPRO}/material-to-proyek/add` +export const MATERIAL_TO_PROYEK_ADD_DETAIL = `${BASE_SIMPRO}/material-to-proyek/add-detail` +export const MATERIAL_TO_PROYEK_SEARCH = `${BASE_SIMPRO}/material-to-proyek/search` +export const MATERIAL_TO_PROYEK_EDIT = (id) => { return `${BASE_SIMPRO}/material-to-proyek/edit/${id}` } +export const MATERIAL_TO_PROYEK_DELETE = (id) => { return `${BASE_SIMPRO}/material-to-proyek/delete/${id}` } + +export const TOOLS_TO_PROYEK_ADD = `${BASE_SIMPRO}/tools-to-proyek/add` +export const TOOLS_TO_PROYEK_ADD_DETAIL = `${BASE_SIMPRO}/tools-to-proyek/add-detail` +export const TOOLS_TO_PROYEK_SEARCH = `${BASE_SIMPRO}/tools-to-proyek/search` +export const TOOLS_TO_PROYEK_EDIT = (id) => { return `${BASE_SIMPRO}/tools-to-proyek/edit/${id}` } +export const TOOLS_TO_PROYEK_DELETE = (id) => { return `${BASE_SIMPRO}/tools-to-proyek/delete/${id}` } + +// lumen APi +export const USER_LOGIN_V2 = `${BASE_SIMPRO_LUMEN}/login` +export const USER_TO_PROYEK = `${BASE_SIMPRO_LUMEN}/user-to-project/assign` +export const TOOLS_ASSIGN_PROYEK = `${BASE_SIMPRO_LUMEN}/tools-to-project/assign` +export const MATERIAL_ASSIGN_PROYEK = `${BASE_SIMPRO_LUMEN}/material-to-project/assign` +export const TOOLS_CREATE = `${BASE_SIMPRO_LUMEN}/req-tools/create` + +export const USER_ADD = `${BASE_SIMPRO_LUMEN}/human-resource/add` +// export const USER_LOGIN = `${BASE_SIMPRO_LUMEN}/user/login` +export const USER_SEARCH = `${BASE_SIMPRO_LUMEN}/human-resource/search` +export const USER_LIST = `${BASE_SIMPRO_LUMEN}/human-resource/list` +export const USER_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/human-resource/update/${id}` } +export const USER_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/human-resource/delete/${id}` } + +export const ABSENSI_ADD = `${BASE_SIMPRO_LUMEN}/permit/add` +export const ABSENSI_SEARCH = `${BASE_SIMPRO_LUMEN}/permit/search` +export const ABSENSI_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/permit/edit/${id}` } +export const ABSENSI_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/permit/delete/${id}` } + +export const K3_ADD = `${BASE_SIMPRO_LUMEN}/report-k3/add` +export const K3_SEARCH = `${BASE_SIMPRO_LUMEN}/report-k3/search` +export const K3_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/report-k3/edit/${id}` } +export const K3_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/report-k3/delete/${id}` } + +export const DOCUMENT_ADD = `${BASE_SIMPRO_LUMEN}/document-project/upload` +export const DOCUMENT_GET = (id) => { return `${BASE_SIMPRO_LUMEN}/document-project/get/${id}` } +export const DOCUMENT_SEARCH = `${BASE_SIMPRO_LUMEN}/document-project/search` +export const DOCUMENT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/document-project/update/${id}` } +export const DOCUMENT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/document-project/delete/${id}` } +export const DOCUMENT_DOWNLOAD = (id) => { return `${BASE_SIMPRO_LUMEN}/document-project/download/${id}` } + +export const ROLE_ADD = `${BASE_SIMPRO_LUMEN}/role/add` +export const ROLE_SEARCH = `${BASE_SIMPRO_LUMEN}/role/search` +export const ROLE_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/role/update/${id}` } +export const ROLE_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/role/delete/${id}` } + +export const REQUEST_MATERIAL_ADD = `${BASE_SIMPRO_LUMEN}/request-material/add` +export const REQUEST_MATERIAL_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/request-material/update/${id}` } +export const REQUEST_MATERIAL_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/request-material/delete/${id}` } +export const REQUEST_MATERIAL_SEARCH = `${BASE_SIMPRO_LUMEN}/request-material/search` +export const REQUEST_MATERIAL_UPDATE_WAREHOUSE_SITE = (id) => { return `${BASE_SIMPRO_LUMEN}/request-material/update-warehouse-site/${id}` } + +export const MENU_ADD = `${BASE_SIMPRO_LUMEN}/menu/add` +export const MENU_MANAGEMENT = (id) => { return `${BASE_SIMPRO_LUMEN}/menu/management/${id}` } +export const MENU_SEARCH = `${BASE_SIMPRO_LUMEN}/menu/search` +export const MENU_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/menu/update/${id}` } +export const MENU_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/menu/delete/${id}` } + +export const ROLEMENU_ADD = `${BASE_SIMPRO_LUMEN}/role-menu/add` +export const ROLEMENU_SEARCH = `${BASE_SIMPRO_LUMEN}/role-menu/search` +export const ROLEMENU_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/role-menu/update/${id}` } +export const ROLEMENU_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/role-menu/delete/${id}` } +export const ROLEMENU_DELETE_ROLE = (id) => { return `${BASE_SIMPRO_LUMEN}/role-menu/delete-byrole/${id}` } + +export const PROYEK_ADD = `${BASE_SIMPRO_LUMEN}/project/add` +export const PROYEK_LIST = `${BASE_SIMPRO_LUMEN}/project/list` +export const PROYEK_SEARCH = `${BASE_SIMPRO_LUMEN}/project/search` +export const PROYEK_SEARCH_DETAIL = `${BASE_SIMPRO_LUMEN}/project/search` +export const PROYEK_GET_ID = (id) => { return `${BASE_SIMPRO_LUMEN}/project/edit/${id}` } +export const PROYEK_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/project/update/${id}` } +export const PROYEK_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/project/delete/${id}` } + +export const PROJECT_ROLE_ADD = `${BASE_SIMPRO_LUMEN}/project-role/add` +export const PROJECT_ROLE_SEARCH = `${BASE_SIMPRO_LUMEN}/project-role/search` +export const PROJECT_ROLE_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/project-role/update/${id}` } +export const PROJECT_ROLE_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/project-role/delete/${id}` } + +export const PROJECT_TYPE_ADD = `${BASE_SIMPRO_LUMEN}/project-type/add` +export const PROJECT_TYPE_SEARCH = `${BASE_SIMPRO_LUMEN}/project-type/search` +export const PROJECT_TYPE_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/project-type/update/${id}` } +export const PROJECT_TYPE_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/project-type/delete/${id}` } +export const TYPE_PROYEK = `${BASE_SIMPRO_LUMEN}/project-type/list` + +export const PROJECT_CHARTER_ADD = `${BASE_SIMPRO_LUMEN}/project-charter/add` +export const PROJECT_CHARTER_SEARCH = `${BASE_SIMPRO_LUMEN}/project-charter/search` +export const PROJECT_CHARTER_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/project-charter/update/${id}` } + +export const CONFIGALERT_ADD = `${BASE_SIMPRO_LUMEN}/config-alert/add` +export const CONFIGALERT_SEARCH = `${BASE_SIMPRO_LUMEN}/config-alert/search` +export const CONFIGALERT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/config-alert/update/${id}` } +export const CONFIGALERT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/config-alert/delete/${id}` } + +export const CALERTUSER_ADD = `${BASE_SIMPRO_LUMEN}/config-alert-to-user/add` +export const CALERTUSER_SEARCH = `${BASE_SIMPRO_LUMEN}/config-alert-to-user/search` +export const CALERTUSER_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/config-alert-to-user/update/${id}` } +export const CALERTUSER_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/config-alert-to-user/delete/${id}` } + +export const MATERIAL_RESOURCE_ADD = `${BASE_SIMPRO_LUMEN}/material-resource/add` +// export const MATERIAL_RESOURCE_ADD_DETAIL = `${BASE_SIMPRO_LUMEN}/material-resource/add-detail` +export const MATERIAL_RESOURCE_SEARCH = `${BASE_SIMPRO_LUMEN}/material-resource/search` +export const MATERIAL_RESOURCE_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/material-resource/update/${id}` } +export const MATERIAL_RESOURCE_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/material-resource/delete/${id}` } +export const MATERIAL_RESOURCE_LIST = `${BASE_SIMPRO_LUMEN}/material-resource/list` + +export const TOOLS_RESOURCE_ADD = `${BASE_SIMPRO_LUMEN}/tools-resource/add` +export const TOOLS_RESOURCE_SEARCH = `${BASE_SIMPRO_LUMEN}/tools-resource/search` +export const TOOLS_RESOURCE_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/tools-resource/update/${id}` } +export const TOOLS_RESOURCE_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/tools-resource/delete/${id}` } +export const TOOLS_RESOURCE_LIST = `${BASE_SIMPRO_LUMEN}/tools-resource/list` + +export const VERSION_GANTT_ADD = `${BASE_SIMPRO_LUMEN}/version-gantt/add` +export const VERSION_GANTT_SEARCH = `${BASE_SIMPRO_LUMEN}/version-gantt/search` +export const VERSION_GANTT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/version-gantt/update/${id}` } +export const VERSION_GANTT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/version-gantt/delete/${id}` } +export const VERSION_GANTT_LIST = `${BASE_SIMPRO_LUMEN}/version-gantt/list` + +export const USER_VERSION_GANTT_ADD = `${BASE_SIMPRO_LUMEN}/user-to-version-gantt/add` +export const USER_VERSION_GANTT_ADDS = `${BASE_SIMPRO_LUMEN}/user-to-version-gantt/add-multiple` +export const USER_VERSION_GANTT_SEARCH = `${BASE_SIMPRO_LUMEN}/user-to-version-gantt/search` +export const USER_VERSION_GANTT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/user-to-version-gantt/update/${id}` } +export const USER_VERSION_GANTT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/user-to-version-gantt/delete/${id}` } +export const USER_VERSION_GANTT_LIST = `${BASE_SIMPRO_LUMEN}/user-to-version-gantt/list` + +export const REQUEST_TOOLS_ADD = `${BASE_SIMPRO_LUMEN}/req-tools/add` +export const REQUEST_TOOLS_SEARCH = `${BASE_SIMPRO_LUMEN}/req-tools/search` +export const REQUEST_TOOLS_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/req-tools/update/${id}` } +export const REQUEST_TOOLS_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/req-tools/delete/${id}` } + +export const DIVISI_ADD = `${BASE_SIMPRO_LUMEN}/divisi/add` +export const DIVISI_SEARCH = `${BASE_SIMPRO_LUMEN}/divisi/search` +export const DIVISI_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/divisi/update/${id}` } +export const DIVISI_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/divisi/delete/${id}` } +export const DIVISI_LIST = `${BASE_SIMPRO_LUMEN}/divisi/list` + +export const SHIFT_ADD = `${BASE_SIMPRO_LUMEN}/shift/add` +export const SHIFT_SEARCH = `${BASE_SIMPRO_LUMEN}/shift/search` +export const SHIFT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/shift/update/${id}` } +export const SHIFT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/shift/delete/${id}` } +export const SHIFT_LIST = `${BASE_SIMPRO_LUMEN}/shift/list` + +export const USER_SHIFT_ADD = `${BASE_SIMPRO_LUMEN}/user-to-shift/add` +export const USER_SHIFT_SEARCH = `${BASE_SIMPRO_LUMEN}/user-to-shift/search` +// export const USER_SHIFT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/user-to-shift/update/${id}` } +export const USER_SHIFT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/user-to-shift/delete/${id}` } +export const USER_SHIFT_LIST = `${BASE_SIMPRO_LUMEN}/user-to-shift/list` + +export const RATE_SALLARY_ADD = `${BASE_SIMPRO_LUMEN}/rate-sallary/add` +export const RATE_SALLARY_SEARCH = `${BASE_SIMPRO_LUMEN}/rate-sallary/search` +export const RATE_SALLARY_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/rate-sallary/update/${id}` } +export const RATE_SALLARY_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/rate-sallary/delete/${id}` } +export const RATE_SALLARY_LIST = `${BASE_SIMPRO_LUMEN}/rate-sallary/list` + +export const TEMPLATE_GANTT_ADD = `${BASE_SIMPRO_LUMEN}/template-gantt/add` +export const TEMPLATE_GANTT_SEARCH = `${BASE_SIMPRO_LUMEN}/template-gantt/search` +export const TEMPLATE_GANTT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/template-gantt/update/${id}` } +export const TEMPLATE_GANTT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/template-gantt/delete/${id}` } +export const TEMPLATE_GANTT_LIST = `${BASE_SIMPRO_LUMEN}/template-gantt/list` +export const TEMPLATE_GANTT_TREE = (id) => { return `${BASE_SIMPRO_LUMEN}/template-gantt/get-tree/${id}` } + +export const PROJECT_PARTICIPANT_ADD = `${BASE_SIMPRO_LUMEN}/project-participants/add` +export const PROJECT_PARTICIPANT_SEARCH = `${BASE_SIMPRO_LUMEN}/project-participants/search` +export const PROJECT_PARTICIPANT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/project-participants/update/${id}` } +export const PROJECT_PARTICIPANT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/project-participants/delete/${id}` } +export const PROJECT_PARTICIPANT_DELETE_BY_PROYEK = (id) => { return `${BASE_SIMPRO_LUMEN}/project-participants/delete-by-proyek/${id}` } +export const PROJECT_PARTICIPANT_LIST = `${BASE_SIMPRO_LUMEN}/project-participants/list` +export const PROJECT_PARTICIPANT_WHERE_CUSTOM = (where, id) => { return `${BASE_SIMPRO_LUMEN}/project-participants/${where}/${id}` } + +export const PROJECT_APPROVAL_ADD = `${BASE_SIMPRO_LUMEN}/project-approval/add` +export const PROJECT_APPROVAL_SEARCH = `${BASE_SIMPRO_LUMEN}/project-approval/search` +export const PROJECT_APPROVAL_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/project-approval/update/${id}` } +export const PROJECT_APPROVAL_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/project-approval/delete/${id}` } +export const PROJECT_APPROVAL_DELETE_BY_PROYEK = (id) => { return `${BASE_SIMPRO_LUMEN}/project-approval/delete-by-proyek/${id}` } +export const PROJECT_APPROVAL_LIST = `${BASE_SIMPRO_LUMEN}/project-approval/list` +export const PROJECT_APPROVAL_WHERE_CUSTOM = (where, id) => { return `${BASE_SIMPRO_LUMEN}/project-approval/${where}/${id}` } + +export const PROJECT_MILESTONE_ADD = `${BASE_SIMPRO_LUMEN}/project-milestone/add` +export const PROJECT_MILESTONE_SEARCH = `${BASE_SIMPRO_LUMEN}/project-milestone/search` +export const PROJECT_MILESTONE_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/project-milestone/update/${id}` } +export const PROJECT_MILESTONE_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/project-milestone/delete/${id}` } +export const PROJECT_MILESTONE_DELETE_BY_PROYEK = (id) => { return `${BASE_SIMPRO_LUMEN}/project-milestone/delete-by-proyek/${id}` } +export const PROJECT_MILESTONE_LIST = `${BASE_SIMPRO_LUMEN}/project-milestone/list` +export const PROJECT_MILESTONE_WHERE_CUSTOM = (where, id) => { return `${BASE_SIMPRO_LUMEN}/project-milestone/${where}/${id}` } + + +export const PRESENCE_ADD = `${BASE_SIMPRO_LUMEN}/presence/add` +export const PRESENCE_SEARCH = `${BASE_SIMPRO_LUMEN}/presence/search` +export const PRESENCE_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/presence/update/${id}` } +export const PRESENCE_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/presence/delete/${id}` } +export const PRESENCE_LIST = `${BASE_SIMPRO_LUMEN}/presence/list` + +export const SATUAN_ADD = `${BASE_SIMPRO_LUMEN}/satuan/add` +export const SATUAN_SEARCH = `${BASE_SIMPRO_LUMEN}/satuan/search` +export const SATUAN_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/satuan/update/${id}` } +export const SATUAN_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/satuan/delete/${id}` } +export const SATUAN_LIST = `${BASE_SIMPRO_LUMEN}/satuan/list` + +export const CHECKLIST_K3_ADD = `${BASE_SIMPRO_LUMEN}/checklist-k3/add` +export const CHECKLIST_K3_SEARCH = `${BASE_SIMPRO_LUMEN}/checklist-k3/search` +export const CHECKLIST_K3_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/checklist-k3/update/${id}` } +export const CHECKLIST_K3_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/checklist-k3/delete/${id}` } +export const CHECKLIST_K3_LIST = `${BASE_SIMPRO_LUMEN}/checklist-k3/list` + +export const ASSIGN_HR_PROJECT_ADD = `${BASE_SIMPRO_LUMEN}/user-to-proyek/add` +export const ASSIGN_HR_PROJECT_SEARCH = `${BASE_SIMPRO_LUMEN}/user-to-proyek/search` +export const ASSIGN_HR_PROJECT_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/user-to-proyek/update/${id}` } +export const ASSIGN_HR_PROJECT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/user-to-proyek/delete/${id}` } +export const ASSIGN_HR_PROJECT_LIST = `${BASE_SIMPRO_LUMEN}/user-to-proyek/list` + +export const IMAGE_GET_BY_ID = (id, category) => { return `${BASE_SIMPRO_LUMEN}/image/${id}/${category}` } +export const IMAGE_SEARCH = `${BASE_SIMPRO_LUMEN}/image/search` +export const OSPRO_BASE_IMAGE = `${BASE_OSPRO}/api/assets/image` + +export const DASHBOARD_COST_PLANNING_ACTUAL = `${BASE_SIMPRO_LUMEN}/dashboard/cost-planning-actual` +export const DASHBOARD_PERSENTASE_PROGRESS_PROYEK = `${BASE_SIMPRO_LUMEN}/dashboard/percentage-planning-actual` +// export const DIVISI_SEARCH = `${BASE_SIMPRO_LUMEN}/divisi/search` +export const DASHBOARD_REPORT_POINTS = `${BASE_SIMPRO_LUMEN}/report-activity/search-point` +export const DASHBOARD_STATUS_PROYEK = `${BASE_SIMPRO_LUMEN}/dashboard/status-proyek` +// export const DASHBOARD_KURVA_S = `${BASE_SIMPRO_LUMEN}/dashboard/curva-s` +export const DASHBOARD_KURVA_S = `${BASE_SIMPRO_LUMEN}/activity/get-curva-s` + +export const CURRENCY_LIST = `${BASE_SIMPRO_LUMEN}/currency/list` + +export const GET_PERCENTAGE_PERDAY = `${BASE_SIMPRO_LUMEN}/activity/get-percentage` + +export const FOLDER_DOCUMENT_PROYEK_ADD = `${BASE_SIMPRO_LUMEN}/folder-document-proyek/add` +export const FOLDER_DOCUMENT_PROYEK_SEARCH = `${BASE_SIMPRO_LUMEN}/folder-document-proyek/search` +export const FOLDER_DOCUMENT_PROYEK_UPDATE = (id) => `${BASE_SIMPRO_LUMEN}/folder-document-proyek/update/${id}` +export const FOLDER_DOCUMENT_PROYEK_DELETE = (id) => `${BASE_SIMPRO_LUMEN}/folder-document-proyek/delete/${id}` +export const FOLDER_DOCUMENT_PROYEK_GET_TREE = (proyek_id) => `${BASE_SIMPRO_LUMEN}/folder-document-proyek/get-tree/${proyek_id}` + +export const PROJECT_TO_CHECKLIST_K3_ADD = `${BASE_SIMPRO_LUMEN}/project-to-checklist-k3/add` +export const PROJECT_TO_CHECKLIST_K3_ADDS = `${BASE_SIMPRO_LUMEN}/project-to-checklist-k3/add-multiple` +export const PROJECT_TO_CHECKLIST_K3_SEARCH = `${BASE_SIMPRO_LUMEN}/project-to-checklist-k3/search` +export const PROJECT_TO_CHECKLIST_K3_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/project-to-checklist-k3/update/${id}` } +export const PROJECT_TO_CHECKLIST_K3_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/project-to-checklist-k3/delete/${id}` } +export const PROJECT_TO_CHECKLIST_K3_LIST = `${BASE_SIMPRO_LUMEN}/project-to-checklist-k3/list` + +// export const WAYPOINT_SEARCH = `${BASE_SIMPRO_LUMEN}/waypoint/search` + +export const CONTROL_MONITORING_SEARCH = `${BASE_SIMPRO_LUMEN}/control-monitoring/search` +export const API_PANIC_BUTTON_SIMPRO = `${BASE_SIMPRO_LUMEN}/panic-button` +export const PANIC_BUTTON_SEARCH = `${BASE_SIMPRO_LUMEN}/panic-button/search`; +export const PANIC_BUTTON_UPDATE = (id) => { return `${BASE_SIMPRO_LUMEN}/panic-button/update/${id}` } + +export const WAYPOINT_SEARCH = `${BASE_SIMPRO_LUMEN}/waypoint/search` diff --git a/src/const/AppConst.js b/src/const/AppConst.js new file mode 100644 index 0000000..5bd9ac2 --- /dev/null +++ b/src/const/AppConst.js @@ -0,0 +1,27 @@ +export const APP_NAME = "SIMPRO" + +export const AT_TRIP_COLOR = "#a40778" +export const AT_CUSTOMER_COLOR = "#16a085" +export const AT_OFFICE_COLOR = "#3f4e62" +export const PRESENT_COLOR = "#20a8d8" +export const ABSENT_COLOR = "#c9302c" +export const TOTAL_ATTENDANCE_COLOR = "#3f4e62" +export const ACHIEVE_COLOR = "#20d832" +export const NOT_ACHIEVE_COLOR = "#c9302c" +export const PRESENSI_COLOR = "#20a8d8" +export const ABSENSI_COLOR = "#c9302c" +export const IZIN_COLOR = "#a40778" +export const DONE_COLOR = "#4de800" +export const NOT_YET_COLOR = "#ff9203" +export const TOTAL_COLOR = "#3f4e62" + +export const WHITE_COLOR = '#FFFFFF' +export const BLACK_COLOR = '#000000' +export const RED_COLOR = '#c9302c' +export const ORANGE_COLOR = '#ff9203' +export const GREEN_COLOR = '#4de800' +export const DARK_GREY_COLOR = '#3F4E62' +export const BLUE_COLOR = '#20A8D8' +export const PURPLE_COLOR = '#A40778' +export const BROWN_COLOR = '#8c3d00' +export const YELLOW_COLOR = '#ffff00' \ No newline at end of file diff --git a/src/const/CustomFunc.js b/src/const/CustomFunc.js new file mode 100644 index 0000000..7097820 --- /dev/null +++ b/src/const/CustomFunc.js @@ -0,0 +1,378 @@ +export const hideAttr = [ + "geom", + "password", + "id", + "username", + "created_date", + "created_by", + "modified_date", + "modified_by", + "updated_by", + "updated_date", + "updated_at", + "session_login", + "company", + "buffer_radius", + "group_sales", + "OBJECTID", + "fcm_token", + "key", + "planning_id", + "subproyek_id", + "user_id", + "created_at", + "_type", // waspang + "satuan_id", + "m_planning_nama", + "m_planning_target", + "m_satuan_description", + "m_subproyek_akhir_proyek", + "m_subproyek_area_kerja", + "m_subproyek_biaya", + "m_subproyek_biaya_actual", + "m_subproyek_color_progress", + "m_subproyek_jumlah_pekerja", + "m_subproyek_ket_progress", + "m_subproyek_lokasi_kantor", + "m_subproyek_mulai_proyek", + "m_subproyek_nama", + "m_subproyek_persentase_progress_actual", + "m_subproyek_persentase_progress_plan", + "m_users_email", + "m_users_username", + "m_planning_address", + "m_planning_jumlah_titik", + "m_planning_target_planning", + "m_satuan_name", + "m_subproyek_pic", + "m_users_gender", + "m_users_name", + "m_users_phone_number", + // "jumlah_pekerjaan" +]; + +export const salesAttrShow = [ + "id", + "group_sales_name", + "name", + "phone_number", + "email", + // "type_sales", + "address" +]; + +export const dateColumns = [ + "created_date", + "modified_date", + "birth_date", + "datesend", // last_waypoint_employee + "wptime", // last_waypoint_employee + "tanggal", + "mulai_tugas", + "akhir_tugas" +]; + +export const DATE_TIME_FORMAT = "DD-MM-YYYY"; + +export const COLUMN_DAILY_INFO_TABLE = [ + { + dataField: 'id', + text: 'ID', + hidden: true + }, + { + dataField: 'join.group_employee_name', + text: 'Group Employee' + }, + { + dataField: 'name', + text: 'Name' + }, + { + dataField: 'phone_number', + text: 'Phone Number' + }, + { + dataField: 'email', + text: 'Email' + }, + { + dataField: 'address', + text: 'Address' + }, + { + dataField: 'clock_in', + text: 'Clock In' + }, + { + dataField: 'clock_out', + text: 'Clock Out' + } +] + +export const formatLabel = (label) => { + let output = ""; + + if (label.includes("_")) { + // if sentences + let words = label.split("_"); + for (let i=0; i < words.length; i++) { + words[i] = words[i].charAt(0).toUpperCase() + words[i].substr(1); + } + output = words.join(' '); + } + else { + output = label.charAt(0).toUpperCase() + label.slice(1); + } + return output; +} + +export const QUERY_BUILDER_FIELD_SALES = +{ + "properties->username": { + label: 'Username', + type: 'text', + valueSources: ['value'], + }, + "properties->name": { + label: 'Name', + type: 'text', + valueSources: ['value'], + }, + "properties->email": { + label: 'Email', + type: 'text', + valueSources: ['value'], + }, + "properties->address": { + label: 'Address', + type: 'text', + valueSources: ['value'], + }, + "properties->phone_number": { + label: 'Phone Number', + type: 'number', + valueSources: ['value'], + }, + "properties->type_sales": { + label: 'Type Sales', + type: 'select', + valueSources: ['value'], + fieldSettings: { + listValues: [ + { value: 'B2B', title: 'B2B' }, + { value: 'B2C', title: 'B2C' }, + ], + } + }, +} + +export const QUERY_BUILDER_FIELD_CUSTOMER = +{ + "properties->name": { + label: 'Name', + type: 'text', + valueSources: ['value'], + }, + "properties->buffer_radius": { + label: 'Buffer Radius', + type: 'number', + fieldSettings: { + min: 1, + }, + valueSources: ['value'], + }, + "properties->address": { + label: 'Address', + type: 'text', + valueSources: ['value'], + }, + "properties->income": { + label: 'Income', + type: 'number', + valueSources: ['value'], + }, + "properties->store_manager": { + label: 'Store Manager', + type: 'text', + valueSources: ['value'], + }, +} + +export const QUERY_BUILDER_FIELD_OFFICE = +{ + "properties->company": { + label: 'Company', + type: 'text', + valueSources: ['value'], + }, + "properties->name": { + label: 'Name', + type: 'text', + valueSources: ['value'], + }, + "properties->employes": { + label: 'Employe', + type: 'text', + valueSources: ['value'], + }, + "properties->address": { + label: 'Alamat', + type: 'text', + valueSources: ['value'], + }, +} + + +export const getChildrenTree = (data) => + data.map((item, index) => { + if (item.subproyeks && item.subproyeks.length > 0) { + return { + "children": getChildrenTree(item.subproyeks), + "title": item.nama, + "key": Math.random(), + "id": item.id, + ...item + } + } + // else if (item.plannings && item.plannings.length > 0) { + // return { + // "children": getChildrenTree(item.plannings), + // "title": item.nama, + // "key": Math.random(), + // ...item + // } + // } + // OSPRO V2 + // else if (item.laporan_plannings && item.laporan_plannings.length > 0) { + // let lp = item.laporan_plannings.map(lp_item => { + // return { + // "nama_subproyek": item.join.m_subproyek_nama, + // "nama_planning": item.nama, + // "nama_user": item.join.m_users_name, + // ...lp_item + // } + // }); + + // return { + // "key": Math.random(), + // "title": item.nama, + // ...item, + // "laporan_plannings": lp // modify the laporan_plannings + // } + // } + return { + "title": item.nama, + "key": Math.random(), + ...item + } + }) + +export const formatRupiah = (angka, prefix) => { + var number_string = angka.replace(/[^,\d]/g, '').toString(), + split = number_string.split(','), + sisa = split[0].length % 3, + rupiah = split[0].substr(0, sisa), + ribuan = split[0].substr(sisa).match(/\d{3}/gi); + var separator = ""; + // tambahkan titik jika yang di input sudah menjadi angka ribuan + if(ribuan){ + separator = sisa ? '.' : ''; + rupiah += separator + ribuan.join('.'); + } + + rupiah = split[1] != undefined ? rupiah + ',' + split[1] : rupiah; + return prefix == undefined ? rupiah : (rupiah ? 'Rp. ' + rupiah : ''); +} + +export const renderFormatRupiah = (text, prefix) => { + if (text) { + return formatRupiah(text, prefix) + } else { + return "-" + } +} + +export const formatNumber = (angka) => { + var number_string = angka.replace(/[^,\d]/g, '').toString(), + split = number_string.split(','), + sisa = split[0].length % 3, + rupiah = split[0].substr(0, sisa), + ribuan = split[0].substr(sisa).match(/\d{3}/gi); + var separator = ""; + // tambahkan titik jika yang di input sudah menjadi angka ribuan + if(ribuan){ + separator = sisa ? '.' : ''; + rupiah += separator + ribuan.join('.'); + } + + rupiah = split[1] != undefined ? rupiah + ',' + split[1] : rupiah; + return rupiah +} + + +export const formatThousand = (x) => { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "."); +} + + +/* +Using it: +// sort the object by a property (descending) +// using parser if sorting ignores uppercase and lowercase +sortBy(data, { + prop: "date", + desc: true, + parser: function (item) { + //ignore case sensitive + return item.toUpperCase(); + } +}); +*/ +export const sortBy = (function () { + var toString = Object.prototype.toString, + // default parser function + parse = function (x) { return x; }, + // gets the item to be sorted + getItem = function (x) { + var isObject = x != null && typeof x === "object"; + var isProp = isObject && this.prop in x; + return this.parser(isProp ? x[this.prop] : x); + }; + + /** + * Sorts an array of elements. + * + * @param {Array} array: the collection to sort + * @param {Object} cfg: the configuration options + * @property {String} cfg.prop: property name (if it is an Array of objects) + * @property {Boolean} cfg.desc: determines whether the sort is descending + * @property {Function} cfg.parser: function to parse the items to expected type + * @return {Array} + */ + return function sortby (array, cfg) { + if (!(array instanceof Array && array.length)) return []; + if (toString.call(cfg) !== "[object Object]") cfg = {}; + if (typeof cfg.parser !== "function") cfg.parser = parse; + cfg.desc = !!cfg.desc ? -1 : 1; + return array.sort(function (a, b) { + a = getItem.call(cfg, a); + b = getItem.call(cfg, b); + return cfg.desc * (a < b ? -1 : +(a > b)); + }); + }; + +}()); + + +export const uniqueKeyValues = (arr, key) => { + return [... new Set(arr.map((obj) => {return obj[key]}))]; +} + +export const renderLabelStatus = (text) => { + let label = text; + if (text === 'fom' || text === 'fot' || text === 'po') { + label = text.toUpperCase(); + } + return label; +} \ No newline at end of file diff --git a/src/const/GeohrApiFunc.js b/src/const/GeohrApiFunc.js new file mode 100644 index 0000000..42cc87e --- /dev/null +++ b/src/const/GeohrApiFunc.js @@ -0,0 +1,773 @@ +import { + API_GEOHR_WAYPOINT_SALES_MONITORING, + API_GEOHR_SALES_MONITORING, + API_GEOHR_OFFICE_MONITORING, + API_GEOHR_CUSTOMER_MONITORING, + API_GEOHR_DAILY_INFO , + API_GEOHR_SALES_SEARCH, + API_GEOHR_EMPLOYEE_MONITORING, + API_GEOHR_WAYPOINT_EMPLOYEE_MONITORING, + API_DAILY_INFO_DETAIL, + USER_TO_PROYEK_SEARCH, + API_SIMPRO_WAYPOINT_WASPANG_MONITORING, + WAYPOINT_SEARCH +} from './ApiConst.js' +import { toast } from 'react-toastify'; +const id_org = window.localStorage.getItem('id_org'); +const roleName = window.localStorage.getItem('role_name'); +// Sales Routing +export const getSalesRoutingApi = async (salesId, dateString) => { + // Example + // salesId: int; 8 + // dateString: array; ["2021-06-17 00:00", "2021-06-17 23:59"] + console.log('[GeohrApiFunc.js] getSalesRoutingApi', salesId, dateString); + + let bodyParam = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"wptime", "logic_operator": "range", "value": dateString[0], "value1": dateString[1], "operator": "and"} + ], + "orders": {"columns": ["wptime"], "ascending": true} + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(API_GEOHR_WAYPOINT_SALES_MONITORING(salesId), param).then(response => response.json()).then(res => res); + console.log('getSalesRoutingApi result', result); + if (result.data) { + // return result.data; + + let output = { + type: "FeatureCollection", + features: [] + }; + + // find the sales with features[i]geometry.coordinates is not null + if (result.data.features && result.data.features.length > 0) { + for (let i=0; i < result.data.features.length; i++) { + if ((result.data.features[i].geometry.coordinates && result.data.features[i].geometry.coordinates.length > 0) && result.data.features[i].geometry.type) { + output.features.push(result.data.features[i]); + } + } + } + + return output; + } + else { + return null; + } + } + catch (e) { + console.log('error getSalesRoutingApi', e); + return null; + } +} + +// Sales Monitoring +export const getSalesFeatures = async (filterGroup, salesGroupTree) => { + + const LENGTH_SALES_TREE_CHILDREN = salesGroupTree[0].children.length; + + let bodyParam = { + "paging": {"start": 0, "length": -1}, + // "columns": [ + // {"name":"group_sales_id", "logic_operator": "=", "value": "8", "operator": "and"} + // ], + "joins": [ + {"name": "group_sales", "column_results": ["name", "description"]} + ], + "orders": {"columns": ["name"], "ascending": true} + } + + // kalo centang semua gausah pake filter, selain itu filter looping berdasarkan sales_group_id + if (filterGroup.length > 0 && filterGroup.length !== LENGTH_SALES_TREE_CHILDREN) { + console.log('filterrrrr'); + bodyParam["columns"] = []; + // for (let i=0; i < filterGroup.length; i++) { + // bodyParam.columns.push({"name":"group_sales_id", "logic_operator": "=", "value": filterGroup[i].toString(), "operator": "or"}); + // } + + bodyParam.columns.push({"name":"group_sales_id","logic_operator":"in","value":filterGroup.join(),"operator":"and"}); + + console.log('bodyParam', bodyParam); + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(API_GEOHR_SALES_MONITORING, param).then(response => response.json()).then(res => res); + console.log('getSalesFeatures result', result); + if (result.data) { + + let output = { + type: "FeatureCollection", + features: [] + }; + + // find the sales with features[i]geometry.coordinates is not null + if (result.data.features && result.data.features.length > 0) { + for (let i=0; i < result.data.features.length; i++) { + if (result.data.features[i].geometry.coordinates !== null && result.data.features[i].geometry.type !== null) { + output.features.push(result.data.features[i]); + } + } + } + + return output; + } + else { + return null; + } + } + catch (e) { + console.log('error getSalesFeatures', e); + return null; + } +} + +// Office Monitoring +export const getOfficeFeatures = async () => { + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify({ + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"name", "logic_operator": "like", "value": "", "operator": "and"} + ], + "orders": {"columns": ["name"], "ascending": true} + }) + } + + try { + const result = await fetch(API_GEOHR_OFFICE_MONITORING, param).then(response => response.json()).then(res => res); + console.log('getOfficeFeatures result', result); + if (result.data) { + return result.data; + } + else { + return null; + } + } + catch (e) { + console.log('error getOfficeFeatures', e); + return null; + } +} + +// Customer Monitoring +export const getCustomerFeatures = async () => { + let bodyParam = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"name", "logic_operator": "like", "value": "", "operator": "and"} + ], + "orders": {"columns": ["name"], "ascending": true} + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(API_GEOHR_CUSTOMER_MONITORING, param).then(response => response.json()).then(res => res); + console.log('getCustomerFeatures result', result); + if (result.data) { + + let output = { + type: "FeatureCollection", + features: [] + }; + + // find the sales with features[i]geometry.coordinates is not null + if (result.data.features && result.data.features.length > 0) { + for (let i=0; i < result.data.features.length; i++) { + if (result.data.features[i].geometry.coordinates !== null && result.data.features[i].geometry.type !== null) { + output.features.push(result.data.features[i]); + } + } + } + + return output; + } + else { + return null; + } + } + catch (e) { + console.log('error getCustomerFeatures', e); + return null; + } +} + + +// Daily Info +/* +Expected output: +{ + "code": 200, + "data": { + "at_trip": { + "total": 0, + "sales_ids": [] + }, + "at_customer": { + "total": 0, + "sales_ids": [] + }, + "at_office": { + "total": 0, + "sales_ids": [] + }, + "attendance": { + "total": 0, + "sales_ids": [] + }, + "absent": { + "total": 11, + "sales_ids": [ + 1, + 2, + 7, + 8, + 12, + 16, + 17, + 18, + 19, + 20, + 21 + ] + }, + "sales_total": { + "total": 11, + "sales_ids": [ + 1, + 2, + 7, + 8, + 12, + 16, + 17, + 18, + 19, + 20, + 21 + ] + }, + "at_time": "2021-06-18T10:45:26.19278792+07:00" + }, + "executionTime": "23.932818ms", + "message": "OK" +} +*/ +export const getDailyInfoApi = async () => { + + const typeSales = "B2B"; + + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + // body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(API_GEOHR_DAILY_INFO, param).then(response => response.json()).then(res => res); + console.log('getDailyInfoApi result', result); + if (result.data) { + + // let output = { + // type: "FeatureCollection", + // features: [] + // }; + + // // find the sales with features[i]geometry.coordinates is not null + // if (result.data.features && result.data.features.length > 0) { + // for (let i=0; i < result.data.features.length; i++) { + // if (result.data.features[i].geometry.coordinates !== null && result.data.features[i].geometry.type !== null) { + // output.features.push(result.data.features[i]); + // } + // } + // } + + // return output; + + return result.data; + } + else { + return null; + } + } + catch (e) { + console.log('error getDailyInfoApi', e); + return null; + } +} + +// Daily Info Table +/* +// old +export const requestTableDailyInfoApi = async (item) => { + console.log('requestTableDailyInfoApi item', item); + let filterSales = item.sales_ids; + + + if (filterSales && filterSales.length < 1 && item.total === 0) { + return null; // data is empty + } + else { + let bodyParam = { + "paging": {"start": 0, "length": -1}, + // "columns": [ + // {"name":"id", "logic_operator": "in", "value": "8,10", "operator": "and"} + // ], + "joins": [ + {"name": "group_sales", "column_results": ["name", "description"]} + ], + "orders": {"columns": ["name"], "ascending": true} + } + + // kalo centang semua gausah pake filter, selain itu filter looping berdasarkan sales_group_id + if (filterSales && filterSales.length > 0) { + bodyParam["columns"] = []; + bodyParam.columns.push({"name":"id","logic_operator":"in","value":filterSales.join(),"operator":"and"}); + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(API_GEOHR_SALES_SEARCH, param).then(response => response.json()).then(res => res); + console.log('requestTableDailyInfoApi result', result); + if (result.data) { + return result.data; + } + else { + return null; + } + } + catch (e) { + console.log('error requestTableDailyInfoApi', e); + return null; + } + } +}*/ + +// new +export const requestTableDailyInfoApi = async (param) => { + const formData = new FormData(); + // formData.append('start', 0); + // formData.append('length', 100); + let start = 0; + if (param.currentPage !== 1) { + start = (param.currentPage * param.rowsPerPage) - param.rowsPerPage + } + formData.append('table_type', param.table_type); + formData.append("id_org", id_org); + formData.append('field', param.searchDetailField); + if(param.search!=="" && param.search!==null){ + formData.append('value', param.search); + } + + // if(this.state.search!==""){ + // formData.append('value', this.state.search); + // } + + const length = param.rowsPerPage; + + const payload = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'multipart/form-data'}), + body: formData + } + + try { + const result = await fetch(API_DAILY_INFO_DETAIL(start,length), payload).then(response => response.json()).then(res => res); + // console.log('requestTableDailyInfoApi result 2 2 2 2 2s', result); + if (result.code_status === 200 && result.data) { + return result; + } + else { + return null; + } + } + catch (e) { + console.log('error get', e); + toast.warn(e.toString()); + return null; + } +} + +export const getEmployeeFeatures = async (filterGroup, employeeDivTree) => { + const LENGTH_EMP_DIV_TREE_CHILDREN = employeeDivTree[0].children.length; + + let bodyParam = { + "paging": {"start": 0, "length": -1}, + // "columns": [ + // {"name":"group_sales_id", "logic_operator": "=", "value": "8", "operator": "and"} + // ], + "joins": [ + {"name": "employee_division", "column_results": ["name", "description"]} + ], + "orders": {"columns": ["name"], "ascending": true} + } + + // kalo centang semua gausah pake filter, selain itu filter looping berdasarkan sales_group_id + if (filterGroup.length > 0 && filterGroup.length !== LENGTH_EMP_DIV_TREE_CHILDREN) { + console.log('filterrrrr'); + bodyParam["columns"] = []; + // for (let i=0; i < filterGroup.length; i++) { + // bodyParam.columns.push({"name":"group_sales_id", "logic_operator": "=", "value": filterGroup[i].toString(), "operator": "or"}); + // } + + bodyParam.columns.push({"name":"employee_division_id","logic_operator":"in","value":filterGroup.join(),"operator":"and"}); + + console.log('bodyParam', bodyParam); + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(API_GEOHR_EMPLOYEE_MONITORING, param).then(response => response.json()).then(res => res); + console.log('getEmployeeFeatures result', result); + if (result.data) { + + let output = { + type: "FeatureCollection", + features: [] + }; + + // find the sales with features[i]geometry.coordinates is not null + if (result.data.features && result.data.features.length > 0) { + for (let i=0; i < result.data.features.length; i++) { + if (result.data.features[i].geometry) { + if (result.data.features[i].geometry.coordinates !== null && result.data.features[i].geometry.type !== null) { + output.features.push(result.data.features[i]); + } + } + } + } + + return output; + } + else { + return null; + } + } + catch (e) { + console.log('error getEmployeeFeatures', e); + return null; + } +} + +// Sales Routing +export const getEmployeeRoutingApi = async (userId, dateString) => { + // Example + // userId: int; 8 + // dateString: array; ["2021-06-17 00:00", "2021-06-17 23:59"] + console.log('[GeohrApiFunc.js] getEmployeeRoutingApi', userId, dateString); + + let bodyParam = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"wptime", "logic_operator": "range", "value": dateString[0], "value1": dateString[1], "operator": "and"} + ], + "orders": {"columns": ["wptime"], "ascending": true} + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(API_GEOHR_WAYPOINT_EMPLOYEE_MONITORING(userId), param).then(response => response.json()).then(res => res); + console.log('getEmployeeRoutingApi result', result); + if (result.data) { + // return result.data; + + let output = { + type: "FeatureCollection", + features: [] + }; + + // find the sales with features[i]geometry.coordinates is not null + if (result.data.features && result.data.features.length > 0) { + for (let i=0; i < result.data.features.length; i++) { + if ((result.data.features[i].geometry.coordinates && result.data.features[i].geometry.coordinates.length > 0) && result.data.features[i].geometry.type) { + output.features.push(result.data.features[i]); + } + } + } + + return output; + } + else { + return null; + } + } + catch (e) { + console.log('error getEmployeeRoutingApi', e); + return null; + } +} + + +export const getWaspangFeatures = async (proyekIds) => { + // const LENGTH_EMP_DIV_TREE_CHILDREN = employeeDivTree[0].children.length; + + let bodyParam = { + "paging": {"start": 0, "length": -1}, + "joins": [ + { + "name": "m_proyek", + "column_join": "proyek_id", + "column_results": [ + "nama", + "biaya", + "color_progress", + "jumlah_pekerja", + "pic", + "mulai_proyek", + "akhir_proyek" + ] + }, + { + "name": "m_subproyek", + "column_join": "subproyek_id", + "column_results": [ + "nama", + "biaya", + "color_progress", + "jumlah_pekerja", + "pic", + "mulai_proyek", + "akhir_proyek" + ] + }, + { + "name": "m_users", + "column_join": "user_id", + "column_results": [ + "name", + "username", + "email", + "phone_number", + "gender" + ] + } + ], + "orders": {"columns": ["id"], "ascending": true} + } + + // kalo centang semua gausah pake filter, selain itu filter looping berdasarkan sales_group_id + if (proyekIds.length > 0) { + console.log('filterrrrr'); + bodyParam["columns"] = []; + + bodyParam.columns.push({"name":"proyek_id","logic_operator":"in","value":proyekIds.join(),"operator":"and"}); + + console.log('bodyParam', bodyParam); + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(USER_TO_PROYEK_SEARCH, param).then(response => response.json()).then(res => res); + console.log('getWaspangFeatures result', result); + if (result.data) { + + let output = { + type: "FeatureCollection", + features: [] + }; + + // find the sales with features[i]geometry.coordinates is not null + if (result.data.features && result.data.features.length > 0) { + for (let i=0; i < result.data.features.length; i++) { + if (result.data.features[i].geometry) { + if (result.data.features[i].geometry.coordinates !== null && result.data.features[i].geometry.type !== null) { + output.features.push(result.data.features[i]); + } + } + } + } + + return output; + } + else { + return null; + } + } + catch (e) { + console.log('error getWaspangFeatures', e); + return null; + } +} + +// Waspang Routing +export const getWaspangRoutingApi = async (userId, dateString) => { + // Example + // userId: int; 8 + // dateString: array; ["2021-06-17 00:00", "2021-06-17 23:59"] + console.log('[GeohrApiFunc.js] getWaspangRoutingApi', userId, dateString); + + let bodyParam = { + // "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"wptime", "logic_operator": "range", "value": dateString[0], "value1": dateString[1], "operator": "and"} + ], + "orders": {"columns": ["wptime"], "ascending": true} + } + + const param = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+window.localStorage.getItem('token') + }, + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(API_SIMPRO_WAYPOINT_WASPANG_MONITORING(userId), param).then(response => response.json()).then(res => res); + console.log('getWaspangRoutingApi result', result); + if (result.data) { + // return result.data; + + let output = { + type: "FeatureCollection", + features: [] + }; + + // find the sales with features[i]geometry.coordinates is not null + if (result.data.features && result.data.features.length > 0) { + for (let i=0; i < result.data.features.length; i++) { + if ((result.data.features[i].geometry.coordinates && result.data.features[i].geometry.coordinates.length > 0) && result.data.features[i].geometry.type) { + output.features.push(result.data.features[i]); + } + } + } + + return output; + } + else { + return null; + } + } + catch (e) { + console.log('error getWaspangRoutingApi', e); + return null; + } +} + +// Waspang Routing +export const getPresensiRoutingApi = async (userId, dateString) => { + // Example + // userId: int; 8 + // dateString: array; ["2021-06-17 00:00", "2021-06-17 23:59"] + console.log('[GeohrApiFunc.js] getPresensiRoutingApi', userId, dateString); + + let bodyParam = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name": "user_id", "logic_operator": "=", "value": userId}, + {"name": "wptime", "logic_operator": "range", "value": dateString[0], "value1": dateString[1]} + ], + "orders": {"columns": ["wptime"], "ascending": true} + } + + const param = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+window.localStorage.getItem('token') + }, + body: JSON.stringify(bodyParam) + } + + try { + const result = await fetch(WAYPOINT_SEARCH, param).then(response => response.json()).then(res => res); + console.log('getPresensiRoutingApi result', result); + if (result.data) { + // return result.data; + + let output = { + type: "FeatureCollection", + features: [] + }; + + // find the sales with features[i]geometry.coordinates is not null + // if (result.data.features && result.data.features.length > 0) { + // for (let i=0; i < result.data.features.length; i++) { + // if ((result.data.features[i].geometry.coordinates && result.data.features[i].geometry.coordinates.length > 0) && result.data.features[i].geometry.type) { + // output.features.push(result.data.features[i]); + // } + // } + // } + + // build geojson data format + // { + // "type": "Feature", + // "geometry": { + // "type": "Point", + // "coordinates": [125.6, 10.1] + // }, + // "properties": { + // "name": "Dinagat Islands" + // } + // } + + if (result.data.length > 0) { + let features = null; + for (let i=0; i < result.data.length; i++) { + if (result.data[i].lat && result.data[i].lon) { + features = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [parseFloat(result.data[i].lon), parseFloat(result.data[i].lat)] + }, + "properties": result.data[i] + } + output.features.push(features); + } + } + } + + console.log('routing features', output); + + return output; + } + else { + return null; + } + } + catch (e) { + console.log('error getWaspangRoutingApi', e); + return null; + } +} \ No newline at end of file diff --git a/src/const/GeohrMapStyles.js b/src/const/GeohrMapStyles.js new file mode 100644 index 0000000..f5e7765 --- /dev/null +++ b/src/const/GeohrMapStyles.js @@ -0,0 +1,313 @@ +import {Circle as CircleStyle, Fill, Stroke, Style, Text, Icon as IconOl} from 'ol/style'; +import pinRouteStart from '../assets/img/map/pin_route_green.png'; +import pinRouteEnd from '../assets/img/map/pin_route_red.png'; +import salesPin from '../assets/img/map/customer.png'; +import customerPin from '../assets/img/map/store.png'; +import officePin from '../assets/img/map/office.png'; +import { getRandomColor } from './GeoserverFunc.js'; +import { BASE_IMAGE_EMPLOYEE } from './ApiConst'; + +const MAX_RESOLUTION = 25; + +// export const SALES_FEATURES_STYLE = new Style({ +// image: new CircleStyle({ +// radius: 5, +// fill: null, +// stroke: new Stroke({color: 'blue', width: 1}), +// }) +// }) + +export const SALES_FEATURES_STYLE = new Style({ + image: new IconOl({ + anchor: [0.5, 1], + src: salesPin, + scale: 0.03 + }) +}); + +// export const EMPLOYEE_FEATURES_STYLE = new Style({ +// image: new IconOl({ +// anchor: [0.5, 1], +// src: salesPin, +// // src: "https://oslog.id/geohr-api/assets/images/employee/d7c6259c-9a78-4530-b2ec-1d1be9411c46-0.png", +// scale: 0.03 +// }) +// }); + +export const EMPLOYEE_FEATURES_STYLE = (feature) => { + const anchor = [0.5, 1]; + const scale = 0.1; + // console.log('feature.get', feature.get("profile_pictures")); + let iconImg = feature.get("profile_pictures") && feature.get("profile_pictures").length > 0 ? BASE_IMAGE_EMPLOYEE+feature.get("profile_pictures")[0].image : salesPin; + var style = new Style({ + image: new IconOl({ + anchor: anchor, + src: iconImg, + // src: "https://oslog.id/geohr-api/assets/images/employee/d7c6259c-9a78-4530-b2ec-1d1be9411c46-0.png", + scale: scale, + // graphicHeight: 32, + // graphicWidth: 32, + // size: [50, 50], + // imgSize: [50, 50], + // offset: [50, 50] + // anchor: [0.5, 46], + // anchorXUnits: 'fraction', + // anchorYUnits: 'pixels', + // crossOrigin: 'anonymous' + }) + }); + + var image = style.getImage().getImage(); + + image.onload = () => { + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + canvas.width = image.width; + canvas.height = image.height; + context.drawImage(image, 0, 0, image.width, image.height); + context.beginPath(); + var radius = Math.min(image.width, image.height)/2; + context.arc( + image.width/2, + image.height/2, + radius, + 0, + 2 * Math.PI + ); + context.globalCompositeOperation = 'destination-in'; + context.fill(); + style.setImage( + new IconOl({ + img: canvas, + imgSize: [canvas.width, canvas.height], + anchor: [ + ((anchor[0] - 0.5 ) * 2 * radius / canvas.width) + 0.5, + ((anchor[1] - 0.5 ) * 2 * radius / canvas.height) + 0.5 + ], + scale: scale, + }) + ) + + // console.log('canvas', canvas); + // return style; + } + + return style; +} + +// export const CUSTOMER_FEATURES_STYLE = new Style({ +// image: new CircleStyle({ +// radius: 5, +// fill: null, +// // fill: new Fill({ +// // color: '#db1414', +// // opacity: 0.5 +// // }), +// stroke: new Stroke({color: 'green', width: 1}), +// }) +// }) + +export const CUSTOMER_FEATURES_STYLE = new Style({ + image: new IconOl({ + anchor: [0.5, 1], + src: customerPin, + scale: 0.03 + }) +}); + +export const OFFICE_FEATURES_STYLE = new Style({ + image: new IconOl({ + anchor: [0.5, 1], + src: officePin, + scale: 0.03 + }) +}); + +// export const OFFICE_FEATURES_STYLE = new Style({ +// image: new CircleStyle({ +// radius: 5, +// fill: null, +// // fill: new Fill({ +// // color: '#db1414', +// // opacity: 0.5 +// // }), +// stroke: new Stroke({color: 'red', width: 1}), +// }) +// }) + +// export const ROUTE_MAP_STYLES = { +// 'route': new Style({ +// stroke: new Stroke({ +// width: 6, +// color: getRandomColor() +// }), +// text: new Text({ +// textAlign: "center", +// textBaseline: "middle", +// font: "bold 12px / 1.2 Courier New", +// text: "Route Sales 1", +// // fill: new Fill({color: "#000000"}), +// stroke: new Stroke({color: "ffffff", width: 0.5}), +// offsetX: 0, +// offsetY: 0, +// placement: "point", +// maxAngle: 0.7853981633974483, // 45 degree +// overflow: false, +// rotation: 0, +// }) +// }), +// 'pinRouteStart': new Style({ +// image: new IconOl({ +// anchor: [0.5, 1], +// src: pinRouteStart, +// scale: 0.075 +// }), +// }), +// 'pinRouteEnd': new Style({ +// image: new IconOl({ +// anchor: [0.5, 1], +// src: pinRouteEnd, +// scale: 0.075 +// }), +// }), +// 'geoMarker': new Style({ +// image: new CircleStyle({ +// radius: 7, +// fill: new Fill({color: 'black'}), +// stroke: new Stroke({ +// color: 'white', +// width: 2, +// }), +// }), +// }), +// }; + +export const ROUTE_MAP_STYLES = (feature, resolution) => { + let outputStyle = null; + + // console.log('ROUTE_MAP_STYLES feature', feature, resolution); + // console.log('ROUTE_MAP_STYLES get type', feature.get('type')); + // console.log('ROUTE_MAP_STYLES resolution', resolution); + // let label = feature.get('properties') && feature.get('properties').name ? feature.get('properties').name : ''; + let label = feature.getProperties() && feature.getProperties().properties && feature.getProperties().properties.name ? feature.getProperties().properties.name : ''; + // let label = feature.getProperties(); + // console.log('label', label); + + switch (feature.get('type')) { + case 'route': + outputStyle = new Style({ + stroke: new Stroke({ + width: 6, + color: feature.getProperties() && feature.getProperties().routeColor ? feature.getProperties().routeColor : '#000000' + }), + text: new Text({ + textAlign: "center", + textBaseline: "middle", + font: "bold 12px / 1.2 Courier New", + // text: resolution > MAX_RESOLUTION ? '' : stringDivider(label, 16, '\n'), + text: stringDivider(label, 16, '\n'), + fill: new Fill({color: 'green'}), + stroke: new Stroke({color: "#ffffff", width: 3}), + offsetX: 0, + offsetY: 0, + placement: "point", + maxAngle: 0.7853981633974483, // 45 degree + overflow: false, + rotation: 0, + }) + }); + break; + case 'pinRouteStart': + outputStyle = new Style({ + image: new IconOl({ + anchor: [0.5, 1], + src: pinRouteStart, + scale: 0.075 + }), + }); + break; + case 'pinRouteEnd': + outputStyle = new Style({ + image: new IconOl({ + anchor: [0.5, 1], + src: pinRouteEnd, + scale: 0.075 + }), + }); + break; + case 'geoMarker': + outputStyle = new Style({ + image: new CircleStyle({ + radius: 7, + fill: new Fill({color: 'black'}), + stroke: new Stroke({ + color: 'white', + width: 2, + }), + }), + }); + } + + return outputStyle; +} + + +export const stringDivider = (str, width, spaceReplacer) => { + if (str.length > width) { + var p = width; + while (p > 0 && str[p] != ' ' && str[p] != '-') { + p--; + } + if (p > 0) { + var left; + if (str.substring(p, p + 1) == '-') { + left = str.substring(0, p + 1); + } else { + left = str.substring(0, p); + } + var right = str.substring(p + 1); + return left + spaceReplacer + stringDivider(right, width, spaceReplacer); + } + } + return str; +} + +export const PROJECT_FEATURES_STYLE = new Style({ + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + // color: '#188FFD', + color: '#4de800', + opacity: 0.5 + }), + stroke: new Stroke({color: 'blue', width: 1}), + }) +}) + +export const WASPANG_FEATURES_STYLE = new Style({ + image: new IconOl({ + anchor: [0.5, 1], + src: salesPin, + scale: 0.03 + }) +}); + +export const LAPORAN_FEATURES_STYLE = new Style({ + image: new CircleStyle({ + radius: 5, + fill: new Fill({ + // color: '#188FFD', + color: '#4de800', + opacity: 0.5 + }), + stroke: new Stroke({color: 'blue', width: 1}), + }) +}) + +export const PRESENSI_FEATURES_STYLE = new Style({ + image: new IconOl({ + anchor: [0.5, 1], + src: salesPin, + scale: 0.03 + }) +}); diff --git a/src/const/GeoserverFunc.js b/src/const/GeoserverFunc.js new file mode 100644 index 0000000..7fa20d4 --- /dev/null +++ b/src/const/GeoserverFunc.js @@ -0,0 +1,1253 @@ +import axios from 'axios'; +import { wfsDispatcherUrl, appConfig } from './MapConst.js'; +import { API_GET_COLUMN_TABLE, API_LOGIN, API_CREATE_NEW_LAYER, API_UPDATE_MAP, API_GET_IMAGE, API_UPDATE_FEATURE, +API_LAYER_ATTRIBUTE_BY_LAYERNAME, IMAGE_SEARCH } from './ApiConst.js'; +import { findWhere } from 'underscore'; +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style'; +import { Select } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Vector as VectorSource} from 'ol/source'; +import {getArea, getLength} from 'ol/sphere'; + +// Request table data +export const reqTableData = async (typeName) => { + let xmlDoc = null; + let resTableData = null; + + if (typeName === undefined) { + return; + } + + const config = { + headers: {'Content-Type': 'text/xml'} + }; + + const reqPayload = ` + + + + `; + + let reqAxios = await axios.post(wfsDispatcherUrl, reqPayload, config).then((res) => { return res; }).catch(err => err.message); + console.log('reqAxios',reqAxios); + + if (reqAxios.data !== undefined) { + console.log('typeof response', typeof(reqAxios.data)); + if (typeof(reqAxios.data) == 'object') { + resTableData = reqAxios.data; + // return resTableData; + return { + "success": true, + "result": resTableData + } + } + else { + let parser = new DOMParser(); + if (reqAxios.data !== undefined) { + xmlDoc = parser.parseFromString(reqAxios.data, "text/xml"); + resTableData = xmlDoc.getElementsByTagName('ows:ExceptionText')[0].childNodes[0].nodeValue; + } + console.log('xmlDoc',xmlDoc) + console.log('resTableData',resTableData); + // return resTableData; + return { + "success": false, + "result": resTableData + } + } + } + else { + resTableData = reqAxios; + // return resTableData; + return { + "success": false, + "result": resTableData + } + } + +} + +// Get table columns +/*export const getTableColumns = async (tableName) => { + + let result = null; + + let reqPayload = { + tableName: tableName + } + const config = { + headers: { + 'Content-Type': 'application/json', + } + }; + + let reqAxios = await axios.post(API_GET_COLUMN_TABLE, reqPayload, config).then((res) => { return res; }); + + if (reqAxios.data !== undefined) { + result = reqAxios.data; + return result; + } + else { + result = reqAxios; + return result; + } +}*/ + +export const getTableColumns = async (tableName) => { + // let result = null; + let obj = { + tableName: tableName + } + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(obj) + } + + try { + const result = await fetch(API_GET_COLUMN_TABLE, param).then(response => response.json()).then(res => res) + if(result){ + // result = result.data; + // return result; + + return { + "success": true, + "result": result + } + } else { + // result = result; + // return result; + return { + "success": false, + "result": result + } + } + } catch(err) { + // return err.message; + return { + "success": false, + "result": err.message + } + } +} + +export const test = () => { + axios.get("http://localhost/osmap-php/index.php/siopas/user/test_user").then((res) => { console.log('test axios', res) }); +} + +// SIOPAS Login Function +export const loginSiopas = (username, password) => { + let result = null; + fetch(API_LOGIN, { + method: 'POST', + header: JSON.stringify({ + 'Content-Type': 'multipart/form-data' + }), + body: JSON.stringify({ + username: username, + password: password + }) + + }).then((response) => { + console.log('response', response); + response.json(); + }).then((responseJson) => { + console.log('responseJson login', responseJson) + return; + }) + .catch((error) => { + console.log('error login', error) + }); +} + +// get geometry type of layer +export const getGeomType = async (layerName) => { + // The original url looks like this: + // let url = http://192.168.99.110/geoserver/rest/workspaces/bmd_denpasar/featuretypes/tanah_kantor_instansi_pemerintah.json + let url = appConfig.geoserver_host + 'rest/workspaces/' + appConfig.workspace_name + '/featuretypes/' + layerName + '.json'; + let reqAxios = await axios.get(url).then((res) => { return res }).catch(err => err.message); + console.log('getGeomType reqAxios', reqAxios); + let geomType = ''; + + // check if return 404 + if (reqAxios !== "Request failed with status code 404") { + if (reqAxios !== undefined) { + if (reqAxios.data !== undefined) { + if (reqAxios.data.featureType !== undefined) { + if (reqAxios.data.featureType.attributes !== undefined) { + if (reqAxios.data.featureType.attributes.attribute !== undefined) { + if (reqAxios.data.featureType.attributes.attribute.length > 0) { + let attribute = reqAxios.data.featureType.attributes.attribute; + for (let i=0; i < attribute.length; i++) { + // the attribute looks like this + // { + // "name": "the_geom", + // "minOccurs": 0, + // "maxOccurs": 1, + // "nillable": true, + // "binding": "com.vividsolutions.jts.geom.MultiPolygon" + // }, + if (attribute[i].binding.includes('geom')) { + let str = attribute[i].binding; + let geomStr = str.substr(str.indexOf('geom')+5); // so I get the MultiPolygon (or other geom type provided in geoserver api) + geomType = geomStr; + } + } + } + } + } + } + } + } + // return geomType; + return { + "success": true, + "result": geomType + } + } + else { + + return { + "success": false, + "result": reqAxios + }; + } +} + +export const createNewLayer = async (reqPayload) => { + let payload = { + workspace_name: appConfig.workspace_name, + datastore_name: appConfig.datastore_name, + layer_name: reqPayload.layer_name, + layer_type: reqPayload.layer_type, + columns: reqPayload.columns + } + + // let reqAxios = await axios.post(wfsDispatcherUrl, reqPayload, config).then((res) => { return res; }); + + let reqAxios = await axios({ + method: 'post', + url: API_CREATE_NEW_LAYER, + headers: { + 'Content-Type': 'application/json', + }, + data: payload + }).then((res) => { + return res; + }).catch((error) => { + return error; + }); + + console.log('createNewLayer API',reqAxios); + + return reqAxios; +} + + +export const updateFeature = async (layerName, fid, properties) => { + /* + Example: + + Edit Attributes / Edit Feature + --------------------- + URL: http://192.168.99.110/geoserver/wfs/WfsDispatcher + Method: POST + Request Payload: + + + + + nama_area + bandara3_edit + + + status_area + 0 + + + + + + + */ + + console.log('layerName, fid, properties', layerName, fid, properties); + + const config = { + headers: {'Content-Type': 'text/xml'} + }; + let result = null; + let xmlDoc = null; + + let xml_fid = ``; + let xml_properties = ''; + + for (let i=0; i + ${properties[i].label} + ${properties[i].value} + `; + } + + let payload = ` + + + ${xml_properties} + + ${xml_fid} + + + `; + + let reqAxios = await axios.post(wfsDispatcherUrl, payload, config).then(res => res).catch(err => err.message); + console.log('updateFeature reqAxios',reqAxios); + + if (reqAxios.data !== undefined) { + console.log('typeof response', typeof(reqAxios.data)); + let parser = new DOMParser(); + xmlDoc = parser.parseFromString(reqAxios.data, "text/xml"); + let status = xmlDoc.getElementsByTagName('wfs:Status')[0]; + console.log('xml status', status); + // console.log('childNodes', xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0]); + let statusText = xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0].nextSibling.localName; + console.log('xml status text', xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0].nextSibling.localName); // SUCCESS / FAILED + if (status !== undefined) { + // result = xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0].nodeValue; + // console.log('xml result', result); + if (statusText == 'SUCCESS') { + result = 'Success Update Feature'; + return { + "success": true, + "result": result + } + } + else if (statusText == 'FAILED') { + result = 'Error Update Feature'; + let errMessage = xmlDoc.getElementsByTagName('wfs:Message')[0].innerHTML; + return { + "success": false, + "result": errMessage + } + } + } + else { + return { + "success": false, + "result": "The entity name must immediately follow the entity reference." + } + } + } + else { + result = reqAxios; + // return result; + return { + "success": false, + "result": result + } + } +} + + +export const updateFeature2 = async (layerName, fid, column_value) => { + const config = { + headers: {'Content-Type': 'application/json'} + }; + console.log('layerName', layerName); + console.log('fid', fid); + // console.log('properties',properties); + let payload = { + "layer_name": layerName, + "fid": fid, + "column_value": column_value + }; + + console.log('payload',payload); + + // let reqAxios = await axios.post(API_UPDATE_FEATURE, payload, config).then(res => res).catch(err => err.message); + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(payload) + } + + const resFetch = await fetch(API_UPDATE_FEATURE, param).then(response => response.json()).then(res => res) + console.log('resFetch', resFetch); + + + console.log('updateFeature resFetch',resFetch); + if (resFetch !== undefined) { + if (resFetch.code_type == 'success') { + return { + "success": true, + "result": "Update Success" + } + } + else { + return { + "success": false, + "result": "Update Failed" + } + } + } + else { + return { + "success": false, + "result": resFetch + } + } +} + +export const updateMap = async (requestPayload) => { + + /*let requestPayload = { + "map_title": "BMD Denpasar", + "map_zoom": 9, + "map_projection": "EPSG:3857", + "center_x": "12826670.196943447", + "center_y": "-967549.1600801547", + "map_layers": [ + { + "idx": 0, + "layer_name": "OSM", + "layer_type": "base", + "layer_source": "OlSourceOsm", + "layer_visible": true, + "layer_position": 0 + }, + { + "idx": 1, + "layer_name": "point_tanah_kantor_desa_lurah0", + "layer_type": "Point", + "layer_source": { + "url": "http://192.168.99.110/geoserver/wms", + "params": { + "LAYERS": "bmd_denpasar:point_tanah_kantor_desa_lurah0", + "TILED": true, + "SLD": "http://192.168.99.110/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS=bmd_denpasar:point_tanah_kantor_desa_lurah0" + }, + "serverType": "geoserver", + "transition": 0, + "crossOrigin": "anonymous" + }, + "layer_visible": true, + "layer_position": 1 + } + ] + }*/ + + const config = { + headers: {'Content-Type': 'application/json'} + }; + // console.log('requestPayload before axios', requestPayload); + let reqAxios = await axios.post(API_UPDATE_MAP, requestPayload, config).then(res => res ).catch(error => error); + return reqAxios; +} + + +export const getRandomColor = () => { + let letters = '0123456789ABCDEF'; + let color = '#'; + for (var i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; +} + +export const createNewFeature = async (obj) => { + /* + Create New Feature + ------------------- + URL: http://192.168.99.110/geoserver/wfs/WfsDispatcher + Method: POST + Request Payload: + - layer name + - geom type (multi / single) + - map projection + - coordinates + - other feature atrributes (fields) + + The obj example: + { + "layer_name": "test_bmd_2_point", + "layer_geom_type": "Point", + "map_projection": "EPSG:3857", + "coordinates": [ + [12859380.865788266, -975611.0164268095] + ], + "layer_attributes": [{ + "idx": 0 + "label": "nama_point" + "value": "nusa penida" + }] + } + + Examples: + (Point) + + + + + + + 11886499.833369536,-693527.4813667177 + + + point1 + + + + + Full example documentation on src/assets/docs/wfs_example.txt file + */ + + const config = { + headers: {'Content-Type': 'text/xml'} + }; + let coords = ``; + let layer_attributes = ``; + let result = null; + let xmlDoc = null; + + if (obj.layer_geom_type == "Point") { + let longitude = obj.coordinates[0][0]; + let latitude = obj.coordinates[0][1]; + coords = ` + ${longitude},${latitude} + ` + } + else if (obj.layer_geom_type == "MultiPoint") { + console.log('obj.coordinates', obj.coordinates); + /*coordinates: Array(2) + 0: Array(1) + 0: (2) [12854303.787587587, -955479.4157146217] + 1: Array(1) + 0: (2) [12855873.913617639, -955135.2785083359]*/ + coords = ``; + for (let i=0; i + + ${longitude},${latitude} + + `; + } + coords += ``; + } + else if (obj.layer_geom_type == "LineString") { + console.log('obj.coordinates', obj.coordinates); // [ 0: (4) [Array(2), Array(2), Array(2), Array(2)] ] + + coords = ` + `; + + for(let i=0; i + `; + } + else if (obj.layer_geom_type == "MultiLineString") { + console.log('obj.coordinates', obj.coordinates); + // coordinates: Array(2) + // 0: Array(1) + // 0: (6) [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)] + // 1: Array(1) + // 0: (5) [Array(2), Array(2), Array(2), Array(2), Array(2)] + + coords += ``; + + for (let i=0; i < obj.coordinates.length; i++) { + coords += ` + + `; + for(let j=0; j < obj.coordinates[i][0].length; j++) { + let longitude = obj.coordinates[i][0][j][0]; + let latitude = obj.coordinates[i][0][j][1]; + let lastLoop = obj.coordinates[i][0].length - 1; + + if (j == lastLoop) { + coords += `${longitude},${latitude}`; // no space at the end of loop + } else { + coords += `${longitude},${latitude} `; // with space + } + } + coords += ` + + `; + } + + coords += ``; + } + else if (obj.layer_geom_type == "Polygon") { + console.log('obj.coordinates', obj.coordinates); + /*coordinates: Array(1) + 0: Array(1) + 0: (6) [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)] + */ + coords = ` + + + `; + // 11885890.360625308,-693614.4345447493 11886066.325063715,-693607.6666826681 11886047.016727008,-693545.3625436059 11885890.360625308,-693556.9077170598 11885891.754008677,-693576.0169747006 11885881.005051253,-693576.0169747006 11885890.360625308,-693614.4345447493 + for (let i=0; i < obj.coordinates[0][0].length; i++) { + let longitude = obj.coordinates[0][0][i][0]; + let latitude = obj.coordinates[0][0][i][1]; + let lastLoop = obj.coordinates[0][0].length - 1; + if (i == lastLoop) { + coords += `${longitude},${latitude}`; // no space at the end of loop + } else { + coords += `${longitude},${latitude} `; // with space + } + } + coords += ` + + + `; + + // console.log('coords', coords); + // return; + + } + else if (obj.layer_geom_type == "MultiPolygon") { + console.log('obj.coordinates', obj.coordinates); + /*coordinates: Array(2) + 0: Array(1) + 0: [Array(5)] + 0: (5) [Array(2), Array(2), Array(2), Array(2), Array(2)] + 1: Array(1) + 0: [Array(5)] + 0: (5) [Array(2), Array(2), Array(2), Array(2), Array(2)] + */ + + coords += ``; + for(let i=0; i + + + + `; + for (let j=0; j + + + + `; + } + coords += ` + `; + + } + + if (obj.layer_attributes) { + if (obj.layer_attributes.length > 0) { + for(let i=0; i${obj.layer_attributes[i].value}`; + } + } + } + + let payload = ` + + + + + ${coords} + + ${layer_attributes} + + + `; + + console.log('payload', payload); + + let reqAxios = await axios.post(wfsDispatcherUrl, payload, config).then(res => res).catch(err => err.message); + console.log('createNewFeature reqAxios',reqAxios); + + if (reqAxios.data !== undefined) { + // + console.log('typeof response', typeof(reqAxios.data)); + let parser = new DOMParser(); + xmlDoc = parser.parseFromString(reqAxios.data, "text/xml"); + let status = xmlDoc.getElementsByTagName('wfs:Status')[0]; + console.log('xml status', status); + let statusText = xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0].nextSibling.localName; + console.log('xml status text', xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0].nextSibling.localName); // SUCCESS / FAILED + if (status !== undefined) { + // result = xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0].nodeValue; + // console.log('xml result', result); + if (statusText == 'SUCCESS') { + result = 'Successfully create new feature!'; + return { + "success": true, + "result": result + } + } + else if (statusText == 'FAILED') { + result = 'Error create new feature'; + let errMessage = xmlDoc.getElementsByTagName('wfs:Message')[0].innerHTML; + return { + "success": false, + "result": errMessage + } + } + } + } + else { + result = reqAxios; + // return result; + return { + "success": false, + "result": result + } + } + +} + +export const updateFeatureGeometry = async (obj) => { + + /* + Edit Geometry + ----------------- + URL: http://192.168.99.110/geoserver/wfs/WfsDispatcher + Method: POST + Request Payload: + + + + + the_geom + + + 12630296.161275087,-871501.5712076114 + + + + + + + + + Response: + + + + + + + + + + + + */ + + const config = { + headers: {'Content-Type': 'text/xml'} + }; + let coords = ``; + // let layer_attributes = ``; + let result = null; + let xmlDoc = null; + + // Note: obj.layer_name == fid, ex: test_bmd.3 + let splittedLayerName = obj.layer_name.toString().split("."); + let layer_name = splittedLayerName[0]; + let fid = splittedLayerName[1]; + + if (obj.layer_geom_type == "Point") { + let longitude = obj.coordinates[0][0]; + let latitude = obj.coordinates[0][1]; + coords = ` + ${longitude},${latitude} + ` + } + else if (obj.layer_geom_type == "MultiPoint") { + console.log('obj.coordinates', obj.coordinates); + /*coordinates: Array(2) + 0: Array(1) + 0: (2) [12854303.787587587, -955479.4157146217] + 1: Array(1) + 0: (2) [12855873.913617639, -955135.2785083359]*/ + coords = ``; + for (let i=0; i + + ${longitude},${latitude} + + `; + } + coords += ``; + } + else if (obj.layer_geom_type == "LineString") { + console.log('obj.coordinates', obj.coordinates); // [ 0: (4) [Array(2), Array(2), Array(2), Array(2)] ] + + coords = ` + `; + + for(let i=0; i + `; + } + else if (obj.layer_geom_type == "MultiLineString") { + + } + else if (obj.layer_geom_type == "Polygon") { + console.log('obj.coordinates', obj.coordinates); + /*coordinates: Array(1) + 0: Array(1) + 0: (6) [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)] + */ + coords = ` + + + `; + // 11885890.360625308,-693614.4345447493 11886066.325063715,-693607.6666826681 11886047.016727008,-693545.3625436059 11885890.360625308,-693556.9077170598 11885891.754008677,-693576.0169747006 11885881.005051253,-693576.0169747006 11885890.360625308,-693614.4345447493 + for (let i=0; i < obj.coordinates[0][0].length; i++) { + let longitude = obj.coordinates[0][0][i][0]; + let latitude = obj.coordinates[0][0][i][1]; + let lastLoop = obj.coordinates[0][0].length - 1; + if (i == lastLoop) { + coords += `${longitude},${latitude}`; // no space at the end of loop + } else { + coords += `${longitude},${latitude} `; // with space + } + } + coords += ` + + + `; + + // console.log('coords', coords); + // return; + + } + else if (obj.layer_geom_type == "MultiPolygon") { + console.log('obj.coordinates', obj.coordinates); + /*coordinates: Array(2) + 0: Array(1) + 0: [Array(5)] + 0: (5) [Array(2), Array(2), Array(2), Array(2), Array(2)] + 1: Array(1) + 0: [Array(5)] + 0: (5) [Array(2), Array(2), Array(2), Array(2), Array(2)] + */ + + coords += ``; + for(let i=0; i + + + + `; + for (let j=0; j + + + + `; + } + coords += ``; + + } + + // if (obj.layer_attributes) { + // if (obj.layer_attributes.length > 0) { + // for(let i=0; i${obj.layer_attributes[i].value}`; + // } + // } + // } + + let payload = ` + + + + the_geom + + ${coords} + + + + + + + `; + + console.log('payload', payload); + + let reqAxios = await axios.post(wfsDispatcherUrl, payload, config).then(res => res).catch(err => err.message); + console.log('createNewFeature reqAxios',reqAxios); + + if (reqAxios.data !== undefined) { + // + console.log('typeof response', typeof(reqAxios.data)); + let parser = new DOMParser(); + xmlDoc = parser.parseFromString(reqAxios.data, "text/xml"); + let status = xmlDoc.getElementsByTagName('wfs:Status')[0]; + console.log('xml status', status); + result = xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0].nodeValue; + console.log('xml result', result); + return { + "success": true, + "result": result + } + } + else { + result = reqAxios; + // return result; + return { + "success": false, + "result": result + } + } + +} + + +export const deleteFeature = async (layerName, fid) => { + /* + Delete Feature + ------------------------------- + URL: http://192.168.99.110/geoserver/wfs/WfsDispatcher + Method: POST + Request Payload: + + + + + + + + + Response: + + + + + + + + + + + + */ + const config = { + headers: {'Content-Type': 'text/xml'} + }; + let result = null; + let xmlDoc = null; + // let xml_fid = ``; + let payload = ` + + + + + + + `; + let reqAxios = await axios.post(wfsDispatcherUrl, payload, config).then(res => res).catch(err => err.message); + console.log('deleteFeature reqAxios',reqAxios); + + if (reqAxios.data !== undefined) { + // + let parser = new DOMParser(); + xmlDoc = parser.parseFromString(reqAxios.data, "text/xml"); + let status = xmlDoc.getElementsByTagName('wfs:Status')[0]; + console.log('xml status', status); + result = xmlDoc.getElementsByTagName('wfs:Status')[0].childNodes[0].nodeValue; + console.log('xml result', result); + return { + "success": true, + "result": result + } + } + else { + result = reqAxios; + return { + "success": false, + "result": result + } + } + +} + + +export const getLayerColor = async (SLD_URL) => { + + let result = null; + let xmlDoc = null; + let resAxios = await axios.get(SLD_URL).then(res => res).catch(err => err.message); + + // console.log('getLayerColor resAxios', resAxios); + + if (resAxios.data) { + let parser = new DOMParser(); + xmlDoc = parser.parseFromString(resAxios.data, "text/xml"); + let fillColor = ''; + let cssParameter = xmlDoc.getElementsByTagName('sld:CssParameter'); + // console.log('cssParameter', cssParameter); + for (let i=0; i { + // console.log("type", type); + let category = ''; + if(type==="m_sales"){ + category = "m_sales"; + }else if(type==="m_office"){ + category = "office"; + }else if(type==="m_customer"){ + category = "customer"; + }else if(type==="m_employee"){ + category = "employee" + }else if(type==="realisasi"){ + category = "laporan-planning" + } + else if (type==="report_activity") { + category = "report_activity" + } + + // const config = { + // headers: { + // 'Content-type': `application/json`, + // 'Authorization': `Bearer ${window.localStorage.getItem('token')}` + // } + // }; + + // let token = window.localStorage.getItem('token'); + // let config = { + // headers: + // { + // Authorization: `Bearer ${token}`, + // "Content-type": `application/json` + // } + // }; + + let payload = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"category", "logic_operator": "=", "value": category.toString(), "operator": "and"}, + {"name": "ref_id", "logic_operator": "=", "value": id.toString(), "operator": "and"} + ], + "orders": {"columns": ["image"], "ascending": true} + } + + let config = { + method: 'POST', // *GET, POST, PUT, DELETE, etc. + // mode: 'cors', // no-cors, *cors, same-origin + // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+window.localStorage.getItem('token') + // 'Content-Type': 'application/x-www-form-urlencoded', + }, + // redirect: 'follow', // manual, *follow, error + // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: JSON.stringify(payload) // body data type must match "Content-Type" header + } + + let resFetch = await fetch(IMAGE_SEARCH, config).then(response => response.json()).then(res => res); + // let resAxios = await axios.post(IMAGE_SEARCH, requestPayload, config).then(res => res ).catch(error => error); + let resAxios = {data: resFetch} // karena pake fetch return api nya gak di dalam data.data + return resAxios; +} + + +export const getLayerAttribute = async (layerName) => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_ATTRIBUTE_BY_LAYERNAME+layerName, param).then(response => response.json()).then(res => res) + + if (result.code_type == 'success') { + return { + "success": true, + "result": result + } + } + else { + return { + "success": false, + "result": result.code_message + } + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + // return err; + return { + "success": false, + "result": err.message.toString() + } + } +} + + +/** + * Format length output. + * @param {LineString} line The line. + * @return {string} The formatted length. + */ +export const formatLength = (line) => { + var length = getLength(line); + var output; + if (length > 100) { + output = (Math.round(length / 1000 * 100) / 100) + + ' ' + 'km'; + } else { + output = (Math.round(length * 100) / 100) + + ' ' + 'm'; + } + return output; +}; + +/** + * Format area output. + * @param {Polygon} polygon The polygon. + * @return {string} Formatted area. + */ +export const formatArea = (polygon) => { + var area = getArea(polygon); + console.log('area', area); + var output; + if (area > 10000) { + output = (Math.round(area / 1000000 * 100) / 100) + + ' ' + 'km2'; + } else { + output = (Math.round(area * 100) / 100) + + ' ' + 'm2'; + } + return output; +}; + +export const lightOrDark = (color) => { + let r, g, b, hsp; + + // Check the format of the color, HEX or RGB? + if (color.match(/^rgb/)) { + + // If HEX --> store the red, green, blue values in separate variables + color = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/); + + r = color[1]; + g = color[2]; + b = color[3]; + } + else { + + // If RGB --> Convert it to HEX: http://gist.github.com/983661 + color = +("0x" + color.slice(1).replace( + color.length < 5 && /./g, '$&$&' + ) + ); + + r = color >> 16; + g = color >> 8 & 255; + b = color & 255; + } + + // HSP (Highly Sensitive Poo) equation from http://alienryderflex.com/hsp.html + hsp = Math.sqrt( + 0.299 * (r * r) + + 0.587 * (g * g) + + 0.114 * (b * b) + ); + + // Using the HSP value, determine whether the color is light or dark + if (hsp>127.5) { + + return 'light'; + } + else { + + return 'dark'; + } +} + +export const numberWithCommas = (x) => { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); +} \ No newline at end of file diff --git a/src/const/Kominfo.js b/src/const/Kominfo.js new file mode 100644 index 0000000..88ea1d1 --- /dev/null +++ b/src/const/Kominfo.js @@ -0,0 +1,988 @@ +export const opt2G = [ +{ + title: 'Operator', + key: '2g-0', + layers: { + prov: { + name: 'indonesia_2g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'indonesia_2g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'indonesia_2g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'indonesia_2g_all_desa_3857', + visible: false + } + }, + children: [ + { + title: 'Telkomsel', + key: '2g-0-1', + layers: { + prov: { + name: 'tsel_2g_all_prov_3857', + visible: false + }, + kab: { + name: 'tsel_2g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'tsel_2g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'tsel_2g_all_desa_3857', + visible: false + }, + bts: { + name: 'tsel_2g_q3_19_i', + visible: false + } + } + }, + { + title: 'XL', + key: '2g-0-2', + layers: { + prov: { + name: 'xl_2g_all_prov_join', + visible: false + }, + kab: { + name: 'xl_2g_all_kab_join', + visible: false + }, + kec: { + name: 'xl_2g_all_kec_join', + visible: false + }, + desa: { + name: 'xl_2g_all_desa', + visible: false + }, + bts: { + name: 'xl_2g_q3_2019_i', + visible: false + } + } + }, + { + title: 'Indosat', + key: '2g-0-3', + layers: { + prov: { + name: 'isat_2g_all_prov_join', + visible: false + }, + kab: { + name: 'isat_2g_all_kab_join', + visible: false + }, + kec: { + name: 'isat_2g_all_kec_join', + visible: false + }, + desa: { + name: 'isat_2g_all_desa', + visible: false + }, + bts: { + name: 'isat_2g_q3_19_i', + visible: false + } + } + }, + { + title: 'H3I', + key: '2g-0-4', + layers: { + prov: { + name: 'h3i_2g_all_prov_join', + visible: false + }, + kab: { + name: 'h3i_2g_all_kab_join', + visible: false + }, + kec: { + name: 'h3i_2g_all_kec_join', + visible: false + }, + desa: { + name: 'h3i_2g_all_desa', + visible: false + }, + bts: { + name: 'h3i_2g_q3_2019_i', + visible: false + } + } + }] +}] + +// 2G Mukim +export const opt2G_mukim = [ +{ + title: 'Operator', + key: '2g-0', + layers: { + prov: { + name: 'indonesia_2g_all_desa_3857_mukim', + visible: false + }, + kab: { + name: 'indonesia_2g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'indonesia_2g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'indonesia_2g_all_desa_3857_mukim', + visible: false + } + }, + children: [ + { + title: 'Telkomsel', + key: '2g-0-1', + layers: { + prov: { + name: 'tsel_2g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'tsel_2g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'tsel_2g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'tsel_2g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'tsel_2g_q3_19_i', + visible: false + } + } + }, + { + title: 'XL', + key: '2g-0-2', + layers: { + prov: { + name: 'xl_2g_all_prov_join_mukim', + visible: false + }, + kab: { + name: 'xl_2g_all_kab_join_mukim', + visible: false + }, + kec: { + name: 'xl_2g_all_kec_join_mukim', + visible: false + }, + desa: { + name: 'xl_2g_all_desa_mukim', + visible: false + }, + bts: { + name: 'xl_2g_q3_2019_i', + visible: false + } + } + }, + { + title: 'Indosat', + key: '2g-0-3', + layers: { + prov: { + name: 'isat_2g_all_prov_join_mukim', + visible: false + }, + kab: { + name: 'isat_2g_all_kab_join_mukim', + visible: false + }, + kec: { + name: 'isat_2g_all_kec_join_mukim', + visible: false + }, + desa: { + name: 'isat_2g_all_desa_mukim', + visible: false + }, + bts: { + name: 'isat_2g_q3_19_i', + visible: false + } + } + }, + { + title: 'H3I', + key: '2g-0-4', + layers: { + prov: { + name: 'h3i_2g_all_prov_join_mukim', + visible: false + }, + kab: { + name: 'h3i_2g_all_kab_join_mukim', + visible: false + }, + kec: { + name: 'h3i_2g_all_kec_join_mukim', + visible: false + }, + desa: { + name: 'h3i_2g_all_desa_mukim', + visible: false + }, + bts: { + name: 'h3i_2g_q3_2019_i', + visible: false + } + } + }] +}] + +export const netQuality2G = [ +{ + title: 'Kuat Sinyal', + key: 'qty-2g-0', + children: [ + { + title: '85', + key: 'qty-2g-0-0', + }, + { + title: '92', + key: 'qty-2g-0-1', + }, + { + title: '102', + key: 'qty-2g-0-2', + }, + { + title: '114', + key: 'qty-2g-0-3', + } + ] +}]; + +export const opt3G = [ +{ + title: 'Operator', + key: '3g-0', + layers: { + prov: { + name: 'indonesia_3g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'indonesia_3g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'indonesia_3g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'indonesia_3g_all_desa_3857', + visible: false + } + }, + children: [ + { + title: 'Telkomsel', + key: '3g-0-1', + layers: { + prov: { + name: 'tsel_3g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'tsel_3g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'tsel_3g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'tsel_3g_all_desa_3857', + visible: false + }, + bts: { + name: 'tsel_3g_q3_19_i', + visible: false + } + } + }, + { + title: 'XL', + key: '3g-0-2', + layers: { + prov: { + name: 'xl_3g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'xl_3g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'xl_3g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'xl_3g_all_desa_3857', + visible: false + }, + bts: { + name: 'xl_3g_q3_2019_i', + visible: false + } + } + }, + { + title: 'Indosat', + key: '3g-0-3', + layers: { + prov: { + name: 'isat_3g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'isat_3g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'isat_3g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'isat_3g_all_desa_3857', + visible: false + }, + bts: { + name: 'isat_3g_q3_19_i', + visible: false + } + } + }, + { + title: 'H3I', + key: '3g-0-4', + layers: { + prov: { + name: 'h3i_3g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'h3i_3g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'h3i_3g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'h3i_3g_all_desa_3857', + visible: false + }, + bts: { + name: 'h3i_3g_q3_2019_i', + visible: false + } + } + }] +}] + +// 3G Mukim +export const opt3G_mukim = [ +{ + title: 'Operator', + key: '3g-0', + layers: { + prov: { + name: 'indonesia_3g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'indonesia_3g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'indonesia_3g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'indonesia_3g_all_desa_3857_join_mukim', + visible: false + } + }, + children: [ + { + title: 'Telkomsel', + key: '3g-0-1', + layers: { + prov: { + name: 'tsel_3g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'tsel_3g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'tsel_3g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'tsel_3g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'tsel_3g_q3_19_i', + visible: false + } + } + }, + { + title: 'XL', + key: '3g-0-2', + layers: { + prov: { + name: 'xl_3g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'xl_3g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'xl_3g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'xl_3g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'xl_3g_q3_2019_i', + visible: false + } + } + }, + { + title: 'Indosat', + key: '3g-0-3', + layers: { + prov: { + name: 'isat_3g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'isat_3g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'isat_3g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'isat_3g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'isat_3g_q3_19_i', + visible: false + } + } + }, + { + title: 'H3I', + key: '3g-0-4', + layers: { + prov: { + name: 'h3i_3g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'h3i_3g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'h3i_3g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'h3i_3g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'h3i_3g_q3_2019_i', + visible: false + } + } + }] +}] + +export const netQuality3G = [ +{ + title: 'Kuat Sinyal', + key: 'qty-3g-0', + children: [ + { + title: '85', + key: 'qty-3g-0-0', + }, + { + title: '95', + key: 'qty-3g-0-1', + }, + { + title: '105', + key: 'qty-3g-0-2', + }, + { + title: '114', + key: 'qty-3g-0-3', + } + ] +}]; + +/*export const opt4G = [ + 'All', + 'Telkomsel', + 'XL', + 'Indosat', + 'H3I', + 'STI', + 'Smartfren' +];*/ + +export const opt4G = [ +{ + title: 'Operator', + key: '4g-0', + layers: { + prov: { + name: 'indonesia_4g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'indonesia_4g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'indonesia_4g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'indonesia_4g_all_desa_38570', + visible: false + } + }, + children: [ + { + title: 'Telkomsel', + key: '4g-0-1', + layers: { + prov: { + name: 'tsel_4g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'tsel_4g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'tsel_4g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'tsel_4g_all_desa_3857', + visible: false + }, + bts: { + name: 'tsel_4g_q3_19_i', + visible: false + } + } + }, + { + title: 'XL', + key: '4g-0-2', + layers: { + prov: { + name: 'xl_4g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'xl_4g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'xl_4g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'xl_4g_all_desa_3857', + visible: false + }, + bts: { + name: 'xl_4g_q3_2019_i', + visible: false + } + } + }, + { + title: 'Indosat', + key: '4g-0-3', + layers: { + prov: { + name: 'isat_4G_all_prov_3857_join', + visible: false + }, + kab: { + name: 'isat_4G_all_kab_3857_join', + visible: false + }, + kec: { + name: 'isat_4G_all_kec_3857_join', + visible: false + }, + desa: { + name: 'isat_4g_all_desa_3857', + visible: false + }, + bts: { + name: 'isat_4g_q3_19_i_shp', + visible: false + } + } + }, + { + title: 'H3I', + key: '4g-0-4', + layers: { + prov: { + name: 'h3i_4g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'h3i_4g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'h3i_4g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'h3i_4g_all_desa_3857', + visible: false + }, + bts: { + name: 'h3i_4g_q3_2019_i_shp', + visible: false + } + } + }, + { + title: 'STI', + key: '4g-0-5', + layers: { + prov: { + name: 'sti_4g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'sti_4g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'sti_4g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'sti_4g_all_desa_3857', + visible: false + }, + bts: { + name: 'sti_4g_q3_2019_i', + visible: false + } + } + }, + { + title: 'Smartfren', + key: '4g-0-6', + layers: { + prov: { + name: 'smart_4g_all_prov_3857_join', + visible: false + }, + kab: { + name: 'smart_4g_all_kab_3857_join', + visible: false + }, + kec: { + name: 'smart_4g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'smart_4g_all_desa_3857', + visible: false + }, + bts: { + name: 'sf_4g_q3_2019_i', + visible: false + } + } + }] +} +] + +// 4G Mukim +export const opt4G_mukim = [ +{ + title: 'Operator', + key: '4g-0', + layers: { + prov: { + name: 'indonesia_4g_all_prov_3857_mukim', + visible: false + }, + kab: { + name: 'indonesia_4g_all_kab_3857_mukim', + visible: false + }, + kec: { + name: 'indonesia_4g_all_kec_3857_mukim', + visible: false + }, + desa: { + name: 'indonesia_4g_all_desa_3857_mukim', + visible: false + } + }, + children: [ + { + title: 'Telkomsel', + key: '4g-0-1', + layers: { + prov: { + name: 'tsel_4g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'tsel_4g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'tsel_4g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'tsel_4g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'tsel_4g_q3_19_i', + visible: false + } + } + }, + { + title: 'XL', + key: '4g-0-2', + layers: { + prov: { + name: 'xl_4g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'xl_4g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'xl_4g_all_kec_3857_join', + visible: false + }, + desa: { + name: 'xl_4g_all_kec_3857_join_mukim', + visible: false + }, + bts: { + name: 'xl_4g_q3_2019_i', + visible: false + } + } + }, + { + title: 'Indosat', + key: '4g-0-3', + layers: { + prov: { + name: 'isat_4G_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'isat_4G_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'isat_4G_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'isat_4g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'isat_4g_q3_19_i_shp', + visible: false + } + } + }, + { + title: 'H3I', + key: '4g-0-4', + layers: { + prov: { + name: 'h3i_4g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'h3i_4g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'h3i_4g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'h3i_4g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'h3i_4g_q3_2019_i_shp', + visible: false + } + } + }, + { + title: 'STI', + key: '4g-0-5', + layers: { + prov: { + name: 'sti_4g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'sti_4g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'sti_4g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'sti_4g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'sti_4g_q3_2019_i', + visible: false + } + } + }, + { + title: 'Smartfren', + key: '4g-0-6', + layers: { + prov: { + name: 'smart_4g_all_prov_3857_join_mukim', + visible: false + }, + kab: { + name: 'smart_4g_all_kab_3857_join_mukim', + visible: false + }, + kec: { + name: 'smart_4g_all_kec_3857_join_mukim', + visible: false + }, + desa: { + name: 'smart_4g_all_desa_3857_mukim', + visible: false + }, + bts: { + name: 'sf_4g_q3_2019_i', + visible: false + } + } + }] +} +] + +/*export const netQuality4G = [ + 90, + 95, + 105, + 110, + 114 +]*/ + +export const netQuality4G = [ +{ + title: 'Kuat Sinyal', + key: 'qty-4g-0', + children: [ + { + title: '90', + key: 'qty-4g-0-0', + }, + { + title: '92', + key: 'qty-4g-0-1', + }, + { + title: '102', + key: 'qty-4g-0-2', + }, + { + title: '110', + key: 'qty-4g-0-3', + }, + { + title: '114', + key: 'qty-4g-0-4', + } + ] +}]; + +export const bts = [ +{ + title: 'BTS', + key: 'bts-0' +}]; + +export const mukim = [ +{ + title: 'Pemukiman', + key: 'pemukiman-0' +}] \ No newline at end of file diff --git a/src/const/LayerTreeConst.js b/src/const/LayerTreeConst.js new file mode 100644 index 0000000..d26b610 --- /dev/null +++ b/src/const/LayerTreeConst.js @@ -0,0 +1,245 @@ +export const salesTree = [ +{ + title: 'Sales', + key: 'sales-0', + children: [ + { + title: 'Kebayoran', + key: 'sales-0-0', + }, + { + title: 'Pancoran', + key: 'sales-0-1', + }, + { + title: 'Tebet', + key: 'sales-0-2', + }, + { + title: 'Pondok Indah', + key: 'sales-0-3', + }, + { + title: 'Ancol', + key: 'sales-0-4', + }, + { + title: 'Pademangan', + key: 'sales-0-5', + }, + { + title: 'Grogol', + key: 'sales-0-6', + }, + { + title: 'Pondok Kelapa', + key: 'sales-0-7', + }, + { + title: 'Pondok Cabe', + key: 'sales-0-8', + }, + { + title: 'Pasar Minggu', + key: 'sales-0-9', + }, + { + title: 'Pasar Senen', + key: 'sales-0-10', + }, + { + title: 'Gondangdia', + key: 'sales-0-11', + }, + { + title: 'Gandaria', + key: 'sales-0-12', + }, + { + title: 'Gajah Mada', + key: 'sales-0-13', + }, + { + title: 'Ciputat', + key: 'sales-0-14', + }, + { + title: 'Pesanggrahan', + key: 'sales-0-15', + } + ] +}]; + +export const customerTree = [ +{ + title: 'Customer', + key: 'customer-0' +}]; + +export const officeTree = [ +{ + title: 'Office', + key: 'office-0' +}]; + +export const demografiTree = [ +{ + title: 'Demografi', + key: 'demografi-0', + children: [ + { + title: 'Gender', + key: 'demografi-0-0', + layers: { + name: "demografi_laki_laki_perempuan", + visible: false + } + }, + { + title: 'Jumlah Penduduk', + key: 'demografi-0-1', + layers: { + name: "demografi_jumlah_penduduk", + visible: false + } + }, + { + title: 'Usia', + key: 'demografi-0-2', + layers: { + name: "demografi_usia", + visible: false + } + }, + { + title: 'Kepadatan Penduduk', + key: 'demografi-0-3', + layers: { + name: "demografi_kepadatan_penduduk", + visible: false + } + } + ] +}]; + +// export const analisaTree = [ +// { +// title: 'Analisa', +// key: 'analisa-0', +// children: [ +// { +// title: 'Potensi Penambahan Toko', +// key: 'analisa-0-0', +// layers: { +// name: "potensi_penambahan_toko", +// visible: false +// } +// }, +// { +// title: 'Buffer Income', +// key: 'analisa-0-1', +// layers: { +// name: "buffer_income", +// visible: false +// }, +// children: [ +// { +// title: '50 jt', +// key: 'analisa-0-1-0', +// layers: { +// name: "50_jt", +// visible: false +// } +// }, +// { +// title: '80 jt', +// key: 'analisa-0-1-1', +// layers: { +// name: "80_jt", +// visible: false +// } +// }, +// { +// title: '100 jt', +// key: 'analisa-0-1-2', +// layers: { +// name: "100_jt", +// visible: false +// } +// } +// ] +// }, +// { +// title: 'Irisan', +// key: 'analisa-0-2', +// layers: { +// name: "irisan", +// visible: false +// } +// } +// ] +// }] + +export const analisaTree = [ +{ + title: 'Analisa', + key: 'analisa-0', + children: [ + { + title: 'Potensi Penambahan Toko', + key: 'analisa-0-0', + layers: { + name: "potensi_penambahan_toko", + visible: false + } + }, + { + title: 'Buffer Income', + key: 'analisa-0-1', + layers: { + name: "buffer_income", + visible: false + } + }, + { + title: 'Irisan', + key: 'analisa-0-2', + layers: { + name: "irisan", + visible: false + } + } + ] +}] + + +export const employeeTree = [ +{ + title: 'Karyawan', + key: 'employee-0' +}]; + + +export const projectTreeConst = [ +{ + title: 'Semua', + key: 'project-0' +}] + + +export const projectTreeConst1 = [{ + "title": "Project Satu", + "key": "proyek-0", + "akhir_proyek": "string", + "biaya": "string", + "color_progress": "string", + "created_at": "string", + "created_by": "string", + "id": 0, + "jumlah_pekerja": 0, + "mulai_proyek": "string", + "nama": "string", + "pic": "string", + "updated_at": "string", + "updated_by": "string" +}] \ No newline at end of file diff --git a/src/const/MapConst.js b/src/const/MapConst.js new file mode 100644 index 0000000..a6bfaff --- /dev/null +++ b/src/const/MapConst.js @@ -0,0 +1,122 @@ +import axios from 'axios'; + +// let workspace_name = window.localStorage.getItem('workspace_name'); +// let datastore_name = window.localStorage.getItem('datastore_name'); +let workspace_name = "geohr"; +let datastore_name = "geohr"; +let mapId = window.localStorage.getItem('map_id'); + +export let appConfig = { + // geoserver_host: "http://192.168.99.110/geoserver/", // local ip ardhi + geoserver_host: "https://osmap.id/geoserver/", // online + workspace_name: workspace_name, + datastore_name: datastore_name, + geoserver_username: "admin", + geoserver_password: "geoserver" +} + +export let MAP_ID = mapId; + +// export const reloadConstants = () => { +// console.log('reloading constants...'); +// workspace_name = window.localStorage.getItem('workspace_name'); +// datastore_name = window.localStorage.getItem('datastore_name'); +// mapId = window.localStorage.getItem('map_id'); + +// appConfig.workspace_name = workspace_name; +// appConfig.datastore_name = datastore_name; +// MAP_ID = mapId; + +// console.log('appConfig reloadConstants', appConfig); +// return; +// } + +// export const emptyConstants = () => { +// console.log('empty constants...'); +// workspace_name = ''; +// datastore_name = ''; +// mapId = ''; + +// appConfig.workspace_name = workspace_name; +// appConfig.datastore_name = datastore_name; +// MAP_ID = mapId; + +// console.log('appConfig emptyConstants', appConfig); +// return; +// } + +// console.log('u_group',u_group); +// if (u_group === 'kominfo') { +// workspace_name = 'kominfo'; +// datastore_name = 'kominfo'; +// mapId = 2; +// } +// else if (u_group === 'demors') { +// // demors +// workspace_name = 'geonode'; +// datastore_name = 'geoshape_imports'; +// mapId = 4; +// } +// else { +// // default +// workspace_name = 'bmd_denpasar'; +// datastore_name = 'bmd_denpasar_imports'; +// mapId = 1; +// } + +/*export const appConfig = { + geoserver_host: "http://192.168.99.110/geoserver/", // local ip ardhi + // geoserver_host: "https://osmap.id/geoserver/", // online + workspace_name: "bmd_denpasar", + datastore_name: "bmd_denpasar_imports", + geoserver_username: "admin", + geoserver_password: "geoserver" +}*/ + + + +export const BMD_DENPASAR_MAPSERVICE_URL = "http://siopas.co.id/bmd-denpasar/public/maps/mbtiles-mapservices-epsg3857.php"; +export const IU_MAPSERVICE_URL = "http://202.47.70.196/map-service/mbtiles-mapservices-epsg3857.php"; + +// WMS_CAPABILITIES_URL (for getting ows/layers from geoserver) +// Please note: CORS headers must be set on server providing capabilities document. Otherwise proxy needed. +// export const WMS_CAPABILITIES_URL = appConfig.geoserver_host+'ows/service?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities'; +export const WMS_CAPABILITIES_URL = appConfig.geoserver_host+appConfig.workspace_name+'/ows/service?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities'; + +export const WMS_CAPABILITIES_URL_2 = appConfig.geoserver_host+'/wms?service?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities'; + +// Nominatim URL (for search address) +export const nominatimBaseUrl = "https://address.oslog.id/search?"; + +// WFSDispatcher URL. (CRUD feature) +export const wfsDispatcherUrl = appConfig.geoserver_host+"wfs/WfsDispatcher"; + +// export const layerStyleUrl = appConfig.geoserver_host+"wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS=bmd_denpasar:tanah_kantor_desa_dan_lurah0"; +export const layerStyleUrl = appConfig.geoserver_host+"wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS="+appConfig.workspace_name+":"; + +// Legend Graphic +// http://192.168.99.110/geoserver/wms?REQUEST=GetLegendGraphic&VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER=bmd_denpasar:tanah_sekolah +export const legendPicUrl = appConfig.geoserver_host+"wms?REQUEST=GetLegendGraphic&VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER="+appConfig.workspace_name+":"; + +export const layerThumbnailUrl = (layerName) => { + // url = "http://192.168.99.110/geoserver/wms/reflect?layers=bmd_denpasar:tanah_sekolah&width=280&height=150&format=image/png8"; + const width = "280"; + const height = "150"; + const format = "image/png8"; + let imageUrl = appConfig.geoserver_host+"wms/reflect?layers="+appConfig.workspace_name+":"+layerName+"&width="+width+"&height="+height+"&format="+format+""; + return imageUrl; +} + +// Set Request to axios in request geoserver +export const setRequestMapHeader = () => { + axios.interceptors.request.use(function (config) { + // Do something before request is sent + // config.headers["Authorization"] = `Bearer ${window.localStorage.getItem('token')}`; + config.headers["Authorization"] = "Basic " + btoa(appConfig.geoserver_username + ":" + appConfig.geoserver_password) + // config.headers["crossDomain"] = true; + return config; + }, function (error) { + // Do something with request error + return Promise.reject(error); + }); +} diff --git a/src/const/currency.json b/src/const/currency.json new file mode 100644 index 0000000..5305eb4 --- /dev/null +++ b/src/const/currency.json @@ -0,0 +1,158 @@ +[ + {"cc":"AED","symbol":"\u062f.\u0625;","name":"UAE dirham"}, + {"cc":"AFN","symbol":"Afs","name":"Afghan afghani"}, + {"cc":"ALL","symbol":"L","name":"Albanian lek"}, + {"cc":"AMD","symbol":"AMD","name":"Armenian dram"}, + {"cc":"ANG","symbol":"NA\u0192","name":"Netherlands Antillean gulden"}, + {"cc":"AOA","symbol":"Kz","name":"Angolan kwanza"}, + {"cc":"ARS","symbol":"$","name":"Argentine peso"}, + {"cc":"AUD","symbol":"$","name":"Australian dollar"}, + {"cc":"AWG","symbol":"\u0192","name":"Aruban florin"}, + {"cc":"AZN","symbol":"AZN","name":"Azerbaijani manat"}, + {"cc":"BAM","symbol":"KM","name":"Bosnia and Herzegovina konvertibilna marka"}, + {"cc":"BBD","symbol":"Bds$","name":"Barbadian dollar"}, + {"cc":"BDT","symbol":"\u09f3","name":"Bangladeshi taka"}, + {"cc":"BGN","symbol":"BGN","name":"Bulgarian lev"}, + {"cc":"BHD","symbol":".\u062f.\u0628","name":"Bahraini dinar"}, + {"cc":"BIF","symbol":"FBu","name":"Burundi franc"}, + {"cc":"BMD","symbol":"BD$","name":"Bermudian dollar"}, + {"cc":"BND","symbol":"B$","name":"Brunei dollar"}, + {"cc":"BOB","symbol":"Bs.","name":"Bolivian boliviano"}, + {"cc":"BRL","symbol":"R$","name":"Brazilian real"}, + {"cc":"BSD","symbol":"B$","name":"Bahamian dollar"}, + {"cc":"BTN","symbol":"Nu.","name":"Bhutanese ngultrum"}, + {"cc":"BWP","symbol":"P","name":"Botswana pula"}, + {"cc":"BYR","symbol":"Br","name":"Belarusian ruble"}, + {"cc":"BZD","symbol":"BZ$","name":"Belize dollar"}, + {"cc":"CAD","symbol":"$","name":"Canadian dollar"}, + {"cc":"CDF","symbol":"F","name":"Congolese franc"}, + {"cc":"CHF","symbol":"Fr.","name":"Swiss franc"}, + {"cc":"CLP","symbol":"$","name":"Chilean peso"}, + {"cc":"CNY","symbol":"\u00a5","name":"Chinese/Yuan renminbi"}, + {"cc":"COP","symbol":"Col$","name":"Colombian peso"}, + {"cc":"CRC","symbol":"\u20a1","name":"Costa Rican colon"}, + {"cc":"CUC","symbol":"$","name":"Cuban peso"}, + {"cc":"CVE","symbol":"Esc","name":"Cape Verdean escudo"}, + {"cc":"CZK","symbol":"K\u010d","name":"Czech koruna"}, + {"cc":"DJF","symbol":"Fdj","name":"Djiboutian franc"}, + {"cc":"DKK","symbol":"Kr","name":"Danish krone"}, + {"cc":"DOP","symbol":"RD$","name":"Dominican peso"}, + {"cc":"DZD","symbol":"\u062f.\u062c","name":"Algerian dinar"}, + {"cc":"EEK","symbol":"KR","name":"Estonian kroon"}, + {"cc":"EGP","symbol":"\u00a3","name":"Egyptian pound"}, + {"cc":"ERN","symbol":"Nfa","name":"Eritrean nakfa"}, + {"cc":"ETB","symbol":"Br","name":"Ethiopian birr"}, + {"cc":"EUR","symbol":"\u20ac","name":"European Euro"}, + {"cc":"FJD","symbol":"FJ$","name":"Fijian dollar"}, + {"cc":"FKP","symbol":"\u00a3","name":"Falkland Islands pound"}, + {"cc":"GBP","symbol":"\u00a3","name":"British pound"}, + {"cc":"GEL","symbol":"GEL","name":"Georgian lari"}, + {"cc":"GHS","symbol":"GH\u20b5","name":"Ghanaian cedi"}, + {"cc":"GIP","symbol":"\u00a3","name":"Gibraltar pound"}, + {"cc":"GMD","symbol":"D","name":"Gambian dalasi"}, + {"cc":"GNF","symbol":"FG","name":"Guinean franc"}, + {"cc":"GQE","symbol":"CFA","name":"Central African CFA franc"}, + {"cc":"GTQ","symbol":"Q","name":"Guatemalan quetzal"}, + {"cc":"GYD","symbol":"GY$","name":"Guyanese dollar"}, + {"cc":"HKD","symbol":"HK$","name":"Hong Kong dollar"}, + {"cc":"HNL","symbol":"L","name":"Honduran lempira"}, + {"cc":"HRK","symbol":"kn","name":"Croatian kuna"}, + {"cc":"HTG","symbol":"G","name":"Haitian gourde"}, + {"cc":"HUF","symbol":"Ft","name":"Hungarian forint"}, + {"cc":"IDR","symbol":"Rp","name":"Indonesian rupiah"}, + {"cc":"ILS","symbol":"\u20aa","name":"Israeli new sheqel"}, + {"cc":"INR","symbol":"\u20B9","name":"Indian rupee"}, + {"cc":"IQD","symbol":"\u062f.\u0639","name":"Iraqi dinar"}, + {"cc":"IRR","symbol":"IRR","name":"Iranian rial"}, + {"cc":"ISK","symbol":"kr","name":"Icelandic kr\u00f3na"}, + {"cc":"JMD","symbol":"J$","name":"Jamaican dollar"}, + {"cc":"JOD","symbol":"JOD","name":"Jordanian dinar"}, + {"cc":"JPY","symbol":"\u00a5","name":"Japanese yen"}, + {"cc":"KES","symbol":"KSh","name":"Kenyan shilling"}, + {"cc":"KGS","symbol":"\u0441\u043e\u043c","name":"Kyrgyzstani som"}, + {"cc":"KHR","symbol":"\u17db","name":"Cambodian riel"}, + {"cc":"KMF","symbol":"KMF","name":"Comorian franc"}, + {"cc":"KPW","symbol":"W","name":"North Korean won"}, + {"cc":"KRW","symbol":"W","name":"South Korean won"}, + {"cc":"KWD","symbol":"KWD","name":"Kuwaiti dinar"}, + {"cc":"KYD","symbol":"KY$","name":"Cayman Islands dollar"}, + {"cc":"KZT","symbol":"T","name":"Kazakhstani tenge"}, + {"cc":"LAK","symbol":"KN","name":"Lao kip"}, + {"cc":"LBP","symbol":"\u00a3","name":"Lebanese lira"}, + {"cc":"LKR","symbol":"Rs","name":"Sri Lankan rupee"}, + {"cc":"LRD","symbol":"L$","name":"Liberian dollar"}, + {"cc":"LSL","symbol":"M","name":"Lesotho loti"}, + {"cc":"LTL","symbol":"Lt","name":"Lithuanian litas"}, + {"cc":"LVL","symbol":"Ls","name":"Latvian lats"}, + {"cc":"LYD","symbol":"LD","name":"Libyan dinar"}, + {"cc":"MAD","symbol":"MAD","name":"Moroccan dirham"}, + {"cc":"MDL","symbol":"MDL","name":"Moldovan leu"}, + {"cc":"MGA","symbol":"FMG","name":"Malagasy ariary"}, + {"cc":"MKD","symbol":"MKD","name":"Macedonian denar"}, + {"cc":"MMK","symbol":"K","name":"Myanma kyat"}, + {"cc":"MNT","symbol":"\u20ae","name":"Mongolian tugrik"}, + {"cc":"MOP","symbol":"P","name":"Macanese pataca"}, + {"cc":"MRO","symbol":"UM","name":"Mauritanian ouguiya"}, + {"cc":"MUR","symbol":"Rs","name":"Mauritian rupee"}, + {"cc":"MVR","symbol":"Rf","name":"Maldivian rufiyaa"}, + {"cc":"MWK","symbol":"MK","name":"Malawian kwacha"}, + {"cc":"MXN","symbol":"$","name":"Mexican peso"}, + {"cc":"MYR","symbol":"RM","name":"Malaysian ringgit"}, + {"cc":"MZM","symbol":"MTn","name":"Mozambican metical"}, + {"cc":"NAD","symbol":"N$","name":"Namibian dollar"}, + {"cc":"NGN","symbol":"\u20a6","name":"Nigerian naira"}, + {"cc":"NIO","symbol":"C$","name":"Nicaraguan c\u00f3rdoba"}, + {"cc":"NOK","symbol":"kr","name":"Norwegian krone"}, + {"cc":"NPR","symbol":"NRs","name":"Nepalese rupee"}, + {"cc":"NZD","symbol":"NZ$","name":"New Zealand dollar"}, + {"cc":"OMR","symbol":"OMR","name":"Omani rial"}, + {"cc":"PAB","symbol":"B./","name":"Panamanian balboa"}, + {"cc":"PEN","symbol":"S/.","name":"Peruvian nuevo sol"}, + {"cc":"PGK","symbol":"K","name":"Papua New Guinean kina"}, + {"cc":"PHP","symbol":"\u20b1","name":"Philippine peso"}, + {"cc":"PKR","symbol":"Rs.","name":"Pakistani rupee"}, + {"cc":"PLN","symbol":"z\u0142","name":"Polish zloty"}, + {"cc":"PYG","symbol":"\u20b2","name":"Paraguayan guarani"}, + {"cc":"QAR","symbol":"QR","name":"Qatari riyal"}, + {"cc":"RON","symbol":"L","name":"Romanian leu"}, + {"cc":"RSD","symbol":"din.","name":"Serbian dinar"}, + {"cc":"RUB","symbol":"R","name":"Russian ruble"}, + {"cc":"SAR","symbol":"SR","name":"Saudi riyal"}, + {"cc":"SBD","symbol":"SI$","name":"Solomon Islands dollar"}, + {"cc":"SCR","symbol":"SR","name":"Seychellois rupee"}, + {"cc":"SDG","symbol":"SDG","name":"Sudanese pound"}, + {"cc":"SEK","symbol":"kr","name":"Swedish krona"}, + {"cc":"SGD","symbol":"S$","name":"Singapore dollar"}, + {"cc":"SHP","symbol":"\u00a3","name":"Saint Helena pound"}, + {"cc":"SLL","symbol":"Le","name":"Sierra Leonean leone"}, + {"cc":"SOS","symbol":"Sh.","name":"Somali shilling"}, + {"cc":"SRD","symbol":"$","name":"Surinamese dollar"}, + {"cc":"SYP","symbol":"LS","name":"Syrian pound"}, + {"cc":"SZL","symbol":"E","name":"Swazi lilangeni"}, + {"cc":"THB","symbol":"\u0e3f","name":"Thai baht"}, + {"cc":"TJS","symbol":"TJS","name":"Tajikistani somoni"}, + {"cc":"TMT","symbol":"m","name":"Turkmen manat"}, + {"cc":"TND","symbol":"DT","name":"Tunisian dinar"}, + {"cc":"TRY","symbol":"TRY","name":"Turkish new lira"}, + {"cc":"TTD","symbol":"TT$","name":"Trinidad and Tobago dollar"}, + {"cc":"TWD","symbol":"NT$","name":"New Taiwan dollar"}, + {"cc":"TZS","symbol":"TZS","name":"Tanzanian shilling"}, + {"cc":"UAH","symbol":"UAH","name":"Ukrainian hryvnia"}, + {"cc":"UGX","symbol":"USh","name":"Ugandan shilling"}, + {"cc":"USD","symbol":"US$","name":"United States dollar"}, + {"cc":"UYU","symbol":"$U","name":"Uruguayan peso"}, + {"cc":"UZS","symbol":"UZS","name":"Uzbekistani som"}, + {"cc":"VEB","symbol":"Bs","name":"Venezuelan bolivar"}, + {"cc":"VND","symbol":"\u20ab","name":"Vietnamese dong"}, + {"cc":"VUV","symbol":"VT","name":"Vanuatu vatu"}, + {"cc":"WST","symbol":"WS$","name":"Samoan tala"}, + {"cc":"XAF","symbol":"CFA","name":"Central African CFA franc"}, + {"cc":"XCD","symbol":"EC$","name":"East Caribbean dollar"}, + {"cc":"XDR","symbol":"SDR","name":"Special Drawing Rights"}, + {"cc":"XOF","symbol":"CFA","name":"West African CFA franc"}, + {"cc":"XPF","symbol":"F","name":"CFP franc"}, + {"cc":"YER","symbol":"YER","name":"Yemeni rial"}, + {"cc":"ZAR","symbol":"R","name":"South African rand"}, + {"cc":"ZMK","symbol":"ZK","name":"Zambian kwacha"}, + {"cc":"ZWR","symbol":"Z$","name":"Zimbabwean dollar"} +] \ No newline at end of file diff --git a/src/const/interceptorApi.js b/src/const/interceptorApi.js new file mode 100644 index 0000000..8eabe88 --- /dev/null +++ b/src/const/interceptorApi.js @@ -0,0 +1,28 @@ +import axios from 'axios' + +axios.interceptors.request.use( + async config => { + // const value = await redisClient.get(rediskey) + // const keys = JSON.parse(value) + return config; + }, + error => { + Promise.reject(error) + }); + +// Response interceptor for API calls +axios.interceptors.response.use((response) => { + console.log("token not expire") + return response +}, async function (error) { + // const originalRequest = error.config; + if (error.response.status == 401) { + console.log("error", error.response) + console.log("token expire") + localStorage.clear(); + window.location = window.location.origin; + } + return Promise.reject(error); +}); + +export default axios \ No newline at end of file diff --git a/src/containers/DefaultLayout/Default.css b/src/containers/DefaultLayout/Default.css new file mode 100644 index 0000000..774926e --- /dev/null +++ b/src/containers/DefaultLayout/Default.css @@ -0,0 +1,15 @@ +.dashboard-title { + text-align: center; + margin-top: 5px; + margin-left: auto; + margin-right: auto; +} + +.title-note { + margin-top: -10px; + font-size: 12px; +} + +.hide { + display: none; +} \ No newline at end of file diff --git a/src/containers/DefaultLayout/DefaultAside.js b/src/containers/DefaultLayout/DefaultAside.js new file mode 100644 index 0000000..9c36a8f --- /dev/null +++ b/src/containers/DefaultLayout/DefaultAside.js @@ -0,0 +1,317 @@ +import React, { Component } from 'react'; +import { Nav, NavItem, NavLink, Progress, TabContent, TabPane, ListGroup, ListGroupItem } from 'reactstrap'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { AppSwitch } from '@coreui/react' + +const propTypes = { + children: PropTypes.node, +}; + +const defaultProps = {}; + +class DefaultAside extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + activeTab: '1', + }; + } + + toggle(tab) { + if (this.state.activeTab !== tab) { + this.setState({ + activeTab: tab, + }); + } + } + + render() { + + // eslint-disable-next-line + const { children, ...attributes } = this.props; + + return ( + + + + + + Today + +
+ admin@bootstrapmaster.com +
+
Meeting with Lucas
+ +   1 - 3pm + + + Palo Alto, CA + +
+ +
+ admin@bootstrapmaster.com +
+
Skype with Megan
+ +   4 - 5pm + + + On-line + +
+ Tomorrow + +
New UI Project - deadline
+   10 - 11pm +   creativeLabs HQ +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+
+ +
#10 Startups.Garden Meetup
+   1 - 3pm +   Palo Alto, CA +
+ +
Team meeting
+   4 - 6pm +   creativeLabs HQ +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+ admin@bootstrapmaster.com +
+
+
+
+
+ +
+
+
+ admin@bootstrapmaster.com + +
+
+
+ Lukasz Holeczek + 1:52 PM +
+
Lorem ipsum dolor sit amet
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt... + +
+
+
+
+
+ admin@bootstrapmaster.com + +
+
+
+ Lukasz Holeczek + 1:52 PM +
+
Lorem ipsum dolor sit amet
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt... + +
+
+
+
+
+ admin@bootstrapmaster.com + +
+
+
+ Lukasz Holeczek + 1:52 PM +
+
Lorem ipsum dolor sit amet
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt... + +
+
+
+
+
+ admin@bootstrapmaster.com + +
+
+
+ Lukasz Holeczek + 1:52 PM +
+
Lorem ipsum dolor sit amet
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt... + +
+
+
+
+
+ admin@bootstrapmaster.com + +
+
+
+ Lukasz Holeczek + 1:52 PM +
+
Lorem ipsum dolor sit amet
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt... + +
+
+ +
Settings
+ +
+
+ Option 1 + +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + +
+
+ +
+
+ Option 2 + +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + +
+
+ +
+
+ Option 3 + +
+ Option disabled. +
+
+
+ +
+
+ Option 4 + +
+
+ +
+
System Utilization
+ +
+ CPU Usage +
+ + 348 Processes. 1/4 Cores. + +
+ Memory Usage +
+ + 11444GB/16384MB + +
+ SSD 1 Usage +
+ + 243GB/256GB + +
+ SSD 2 Usage +
+ + 25GB/256GB +
+
+
+ ); + } +} + +DefaultAside.propTypes = propTypes; +DefaultAside.defaultProps = defaultProps; + +export default DefaultAside; diff --git a/src/containers/DefaultLayout/DefaultFooter.js b/src/containers/DefaultLayout/DefaultFooter.js new file mode 100644 index 0000000..9480479 --- /dev/null +++ b/src/containers/DefaultLayout/DefaultFooter.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +const propTypes = { + children: PropTypes.node, +}; + +const defaultProps = {}; + +class DefaultFooter extends Component { + render() { + + // eslint-disable-next-line + const { children, ...attributes } = this.props; + + return ( + + {/*
CoreUI © 2019 creativeLabs. */} + Powered by PT. Integrasia Utama + + ); + } +} + +DefaultFooter.propTypes = propTypes; +DefaultFooter.defaultProps = defaultProps; + +export default DefaultFooter; diff --git a/src/containers/DefaultLayout/DefaultHeader.js b/src/containers/DefaultLayout/DefaultHeader.js new file mode 100644 index 0000000..3d65ab9 --- /dev/null +++ b/src/containers/DefaultLayout/DefaultHeader.js @@ -0,0 +1,390 @@ +import React, { Component } from 'react'; +import { Link, NavLink } from 'react-router-dom'; +import { Badge, UncontrolledDropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem } from 'reactstrap'; +import PropTypes from 'prop-types'; +import { Menu, Dropdown, Row, Col } from 'antd' +import { AppAsideToggler, AppNavbarBrand, AppSidebarToggler } from '@coreui/react'; +import { ALERTUSER_SEARCH, ALERT_SEARCH, ALERTUSER_STATUSVIEW, ALERT_STATUSVIEW, APP_MODE } from '../../const/ApiConst'; +// import logo from '../../assets/img/brand/logo.svg' +// import logo from '../../assets/img/brand/logo_siopas.png' +// import logo_bmd_denpasar from '../../assets/img/brand/logo_bmd_denpasar.png' +// import logo_kominfo from '../../assets/img/brand/logo_kominfo.jpeg' +import user_logo from '../../assets/img/avatars/user.png' +import logo_header_adw from '../../assets/img/logo_adyawinsa.jpg' +import logo_header_kit from '../../assets/img/logo_kit.png' +import logo_header_nawakara from '../../assets/img/logo_nawakara.png' +// import sygnet from '../../assets/img/brand/sygnet.svg' +import axios from 'axios'; +import './Default.css' +const token = window.localStorage.getItem('token'); +const propTypes = { + children: PropTypes.node, +}; + +const config = { + headers: + { + Authorization: `Bearer ${token}`, + "Content-type": `application/json` + } +}; + +const defaultProps = {}; + + +class DefaultHeader extends Component { + + constructor(props) { + super(props); + this.state = { + fullname: localStorage.getItem('fullname'), + u_group: localStorage.getItem('u_group'), + dataAlert: [], + totalAlert: 0, + listReadNotif: [] + } + this.callAlert = ""; + } + + + + componentDidMount() { + this.getLogo(); + this.getHeaderMenu(); + // this.getDataAlert(); + // this.callAlert = setInterval(() => { + // this.getDataAlert(); + // // console.log('Logs every minute'); + // }, 50000); + } + + componentDidUpdate(prevProps, prevState) { + if (this.state.listReadNotif !== prevState.listReadNotif) { + if (this.state.listReadNotif.length > 0) { + this.markAsRead(); + } + } + } + + componentWillUnmount() { + clearInterval(this.callAlert); + } + + getDataAlert = async () => { + let url = ''; + let payload = ''; + if (parseInt(localStorage.getItem('role_id')) === 1) { + url = ALERT_SEARCH; + payload = { + "paging": { "start": 0, "length": -1 }, + "columns": [ + { "name": "status_view", "logic_operator": "=", "value": "false", "operator": "AND" }, + // {"name": "config_alert_id", "logic_operator": "IN", "value": localStorage.getItem('userConfigAlert'), "operator": "AND"} + ], + "joins": [ + { "name": "config_alert", "column_join": "config_alert_id", "column_results": ["nama", "keterangan"] }, + { "name": "laporan_planning", "column_join": "laporan_planning_id", "column_results": ["deskripsi", "status", "jumlah_pekerjaan"] }, + // {"name": "m_planning", "column_join": "planning_id", "column_results": ["nama", "jumlah_titik", "target_planning", "address"]}, + // {"name": "m_subproyek", "column_join": "subproyek_id", "column_results": ["nama", "biaya", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "area_kerja", "lokasi_kantor", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual", "ket_progress", "lat", "lon", "buffer_radius", "geom"]}, + // {"name": "m_proyek", "column_join": "proyek_id", "column_results": ["nama", "biaya", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "area_kerja", "lokasi_kantor", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual", "ket_progress", "lat", "lon", "buffer_radius", "geom"]}, + { "name": "m_satuan", "column_join": "satuan_id", "column_results": ["name", "description"] } + ], + "orders": { "columns": ["created_at"], "ascending": false } + } + } else { + url = ALERTUSER_SEARCH; + payload = { + "paging": { "start": 0, "length": -1 }, + "columns": [ + { "name": "status_view", "logic_operator": "=", "value": "false", "operator": "AND" }, + // {"name": "config_alert_id", "logic_operator": "IN", "value": localStorage.getItem('userConfigAlert'), "operator": "AND","table_name":"alert"}, + { "name": "user_id", "logic_operator": "=", "value": localStorage.getItem('user_id'), "operator": "AND" }, + ], + "joins": [ + { "name": "alert", "column_join": "alert_id", "column_results": ["nama", "keterangan"] }, + { "name": "config_alert", "column_join": "config_alert_id", "column_results": ["nama", "keterangan"] }, + { "name": "laporan_planning", "column_join": "laporan_planning_id", "column_results": ["deskripsi", "status", "jumlah_pekerjaan"] }, + // {"name": "m_planning", "column_join": "planning_id", "column_results": ["nama", "jumlah_titik", "target_planning", "address"]}, + // {"name": "m_subproyek", "column_join": "subproyek_id", "column_results": ["nama", "biaya", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "area_kerja", "lokasi_kantor", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual", "ket_progress", "lat", "lon", "buffer_radius", "geom"]}, + // {"name": "m_proyek", "column_join": "proyek_id", "column_results": ["nama", "biaya", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "area_kerja", "lokasi_kantor", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual", "ket_progress", "lat", "lon", "buffer_radius", "geom"]}, + { "name": "m_satuan", "column_join": "satuan_id", "column_results": ["name", "description"] }, + { "name": "m_users", "column_join": "user_id", "column_results": ["name", "username", "email", "phone_number", "gender"] } + ], + "orders": { "columns": ["created_at"], "ascending": false } + } + } + + if (localStorage.getItem('userConfigAlert')) { + if (parseInt(localStorage.getItem('role_id')) === 1) { + payload['columns'].push({ "name": "config_alert_id", "logic_operator": "IN", "value": localStorage.getItem('userConfigAlert'), "operator": "AND" }) + } else { + payload['columns'].push({ "name": "config_alert_id", "logic_operator": "IN", "value": localStorage.getItem('userConfigAlert'), "operator": "AND", "table_name": "alert" }) + } + } else { + if (parseInt(localStorage.getItem('role_id')) === 1) { + payload['columns'].push({ "name": "config_alert_id", "logic_operator": "IN", "value": "0", "operator": "AND" }) + } else { + payload['columns'].push({ "name": "config_alert_id", "logic_operator": "IN", "value": "0", "operator": "AND", "table_name": "alert" }) + } + } + + const result = await axios + .post(url, payload, config) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.code == 200) { + console.log("cek cek cek 45", result.data.data) + this.setState({ dataAlert: result.data.data, totalAlert: result.data.totalRecord }); + } + } + + getLogoImage = () => { + let img = + switch (APP_MODE) { + case 'KIT': + img = + break; + case 'ADW': + img = + break; + case 'NAWAKARA': + img = + break; + case 'IU': + img =
OSPRO
+ break; + default: + img = + } + return img; + } + + getLogo = () => { + // const { fullname, u_group } = this.state; + // if (u_group == 'kominfo') { + // return ( + // + // ) + // } + // else { + // return ( + // + // ) + // } + + return ( +
+ {/*
*/} + {/* OSPRO */} + {this.getLogoImage()} + {/* adw w= 100, kit w = 100, nawakara w= 40 */} + {/* PROJECT */} +
+ ) + } + + dropDownMenu = () => { + const { dataAlert, totalAlert } = this.state + let dataAlertLength = dataAlert.length + let currentLoop = 0; + return ( + + + Notifications + + {dataAlert.map((n, index) => { + currentLoop++; + if (currentLoop < 6) { + return ( + +

{n.keterangan ? n.keterangan : n.join.alert_keterangan ? n.join.alert_keterangan : "-"}

+
+ ) + } + })} + {totalAlert > 5 ? + <> + + + Lihat Selengkapnya + : null} + {totalAlert === 0 ? + Tidak ada notifikasi untuk saat ini! + : null} +
+ ) + } + + gotoReportAlert = () => { + this.props.history.replace('/laporan-alert') + } + + onReadNotif = (visible) => { + if (!visible) { + const { dataAlert } = this.state + let currentLoop = 0; + let listId = [] + dataAlert.map((val, index) => { + currentLoop++; + if (currentLoop < 6) { + listId.push(val.id); + } + }); + + this.setState({ listReadNotif: listId }); + } + } + + markAsRead = async () => { + console.log("markAsRead") + let data = this.state.listReadNotif + const payload = { + "status_view": true + } + let promises = [] + let result = [] + if (data.length > 0) { + if (parseInt(localStorage.getItem('role_id')) === 1) { + data.map((val, index) => { + let url = ALERT_STATUSVIEW(val) + promises.push(axios.put(url, payload, config) + .then(res => result.push(res))); + }); + + } else { + data.map((val, index) => { + let url = ALERTUSER_STATUSVIEW(val) + promises.push(axios.put(url, payload, config) + .then(res => result.push(res))) + }); + } + await Promise.all(promises); + } + this.setState({ listReadNotif: [] }, () => { + // this.getDataAlert() + }); + } + + getHeaderMenu = () => { + const { fullname, u_group } = this.state; + if (u_group == 'kominfo') { + /*return ( + + )*/ + + // return ( + //
+ //
Layanan Telekomunikasi Q2 2020 di Wilayah Indonesia
+ //

Coverage Operator Cellular dan Data Site Q2 2020

+ //
+ // ) + + return ( +
+
Layanan Telekomunikasi
+
+ ) + } + else { + return ( + + ) + } + } + + render() { + + // eslint-disable-next-line + const { children, ...attributes } = this.props; + + return ( + + + {/**/} + {this.getLogo()} + {/**/} + + + {this.getHeaderMenu()} + + + {/* */} + {/**/} + + ); + } +} + +DefaultHeader.propTypes = propTypes; +DefaultHeader.defaultProps = defaultProps; + +export default DefaultHeader; diff --git a/src/containers/DefaultLayout/DefaultLayout.js b/src/containers/DefaultLayout/DefaultLayout.js new file mode 100644 index 0000000..b4d20cd --- /dev/null +++ b/src/containers/DefaultLayout/DefaultLayout.js @@ -0,0 +1,470 @@ +import React, { Component, Suspense } from 'react'; +import { Redirect, Route, Switch } from 'react-router-dom'; +import * as router from 'react-router-dom'; +import { Container } from 'reactstrap'; +import axios from 'axios'; +import { ALERTUSER_SEARCH, ALERT_SEARCH, MENU_SEARCH, ROLEMENU_SEARCH, ALERTUSER_STATUSVIEW, ALERT_STATUSVIEW, MENU_MANAGEMENT, CONTROL_MONITORING_SEARCH } from '../../const/ApiConst'; + +import { + // CIcon, + AppAside, + AppFooter, + AppHeader, + AppSidebar, + AppSidebarFooter, + AppSidebarForm, + AppSidebarHeader, + AppSidebarMinimizer, + AppBreadcrumb2 as AppBreadcrumb, + AppSidebarNav2 as AppSidebarNav, +} from '@coreui/react'; +// sidebar nav config +import navigation from '../../_nav'; +// import navigation_kominfo from '../../_nav_kominfo'; +// routes config +import routes from '../../routes'; +import { emptyConstants } from '../../const/MapConst.js'; +const countErr = localStorage.getItem('countErr'); +const token = window.localStorage.getItem('token'); +const DefaultAside = React.lazy(() => import('./DefaultAside')); +const DefaultFooter = React.lazy(() => import('./DefaultFooter')); +const DefaultHeader = React.lazy(() => import('./DefaultHeader')); +let errorCount = 0; +const config = { + headers: + { + Authorization: `Bearer ${token}`, + "Content-type": `application/json` + } +}; +class DefaultLayout extends Component { + + constructor(props) { + super(props); + this.state = { + role_id: localStorage.getItem('role_id'), + token: localStorage.getItem('token'), + menu: { items: [] }, + routes2: routes, + finalRoutes: [], + breadrCrumbReady: false, + } + } + + async componentDidMount() { + const menu = localStorage.getItem("menu_login") + if (menu) this.setMenu(JSON.parse(menu)) + // this.getMenu(); + + // this.getDataMenu(); + console.log("componentdidmount defaultLayout"); + this.getAppBreadcrumb(); + if(!localStorage.getItem("token")){ + console.log("token kosong"); + this.signOut(); + } + } + + componentDidUpdate(prevProps, prevState) { + const menu = localStorage.getItem("menu_login") + console.log(" token ", localStorage.getItem("token")) + const { pathname } = this.props.location; + if (this.state.token !== prevState.token) { + // this.getDataMenu() + + this.setMenu(JSON.parse(menu)) + } + if (this.state.role_id !== prevState.role_id) { + // this.getDataMenu() + this.setMenu(JSON.parse(menu)) + } + if (this.state.menu !== prevState.menu) { + this.setFinalRoutes() + } + } + + + + setFinalRoutes = () => { + const { routes2 } = this.state; + if (routes2) { + const newRouter2 = routes2; + // console.log("cek newRouter 2", newRouter2) + this.state.routes2.map((val, index) => { + let indexRes = this.state.menu.items.findIndex(x => x.url === val.path); + if (indexRes >= 0) { + + let obj = newRouter2[index] + + obj['name'] = this.state.menu.items[indexRes].name + + newRouter2[index] = obj + + } + }) + this.setState({ finalRoutes: newRouter2 }, () => { + this.setState({ breadrCrumbReady: true }) + }); + } + } + + loading = () =>
Loading...
+ + async signOut(e) { + if (e) { + e.preventDefault() + } + // localStorage.removeItem("u_group"); + await localStorage.removeItem("role_id"); + await window.localStorage.clear(); + // emptyConstants(); + this.props.history.replace('/login') + } + + getDataMenu = async () => { + errorCount++; + + let url = MENU_MANAGEMENT(this.state.role_id) + const result = await axios + .get(url, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if (result && result.data && result.data.code == 200) { + window.localStorage.setItem('countErr', 0); + let resData = result.data.data + // resData.sort((a,b) => (a.join.m_menu_sequence > b.join.m_menu_sequence) ? 1 : ((b.join.m_menu_sequence > a.join.m_menu_sequence) ? -1 : 0)) + // console.log(resData) + // resData.sort(function (a, b) { + // return a.join.m_menu_sequence - b.join.m_menu_sequence; + // }); + this.setMenu(resData) + } else { + if (errorCount < 4) { + this.getDataMenu(); + } else { + if (countErr) { + await window.localStorage.setItem('countErr', countErr + 1); + } else { + await window.localStorage.setItem('countErr', 1); + } + if (countErr) { + if (parseInt(countErr) > 2) { + this.signOut(); + } else { + window.location.reload(); + } + } else { + window.location.reload(); + } + } + // NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + parseMenuChild = (data) => { + const menu = data.map(res => { + const obj = { + name: res.name, + url: res.url, + icon: res.icon + } + if (res.children.length > 0) { + obj.children = this.parseMenuChild(res.children) + } else { + delete obj.children + } + + return obj + }) + + return menu + } + + setMenu = data => { + + + const menu = data.map(res => { + + const obj = { + name: res.name, + url: res.url, + icon: res.icon + } + if (res.children.length > 0) { + obj.children = this.parseMenuChild(res.children) + } else { + delete obj.children + } + + return obj + }) + // console.log(menu) + // console.log('setMenu', data) + // const menu = data || []; + // const listMenu = [] + // menu.map((val, index)=>{ + // let row = { + // name:val.join.m_menu_name, + // url:val.join.m_menu_url, + // icon:val.join.m_menu_icon + // } + // listMenu.push(row) + // }); + + // console.log("menu list",listMenu) + // const listMenu = [ + // { + // name: 'Dashboard', + // url: '/dashboard', + // icon: 'icon-speedometer', + // }, + // { + // name: 'Initiating', + // url: '/base', + // icon: 'cil-home', + // children: [ + // { + // name: 'Project Charter', + // url: '/project-charter', + // icon: 'cil-description', + // }, + // { + // name: 'Human Resource', + // url: '/human-resource', + // icon: 'cil-people', + // }, + // { + // name: 'Material Resource', + // url: '/material-resource', + // icon: 'fa fa-cube', + // } + // ], + // }, + // { + // name: 'Planning', + // url: '/base', + // icon: 'cil-calendar', + // children: [ + // { + // name: 'Gantt', + // url: '/gantt', + // icon: 'fa fa-list-ul', + // } + // ], + // }, + // { + // name: 'Executing', + // url: '/base', + // icon: 'fa fa-check', + // children: [ + // { + // name: 'Planning Harian', + // url: '/planning-harian', + // icon: 'fa fa-calendar-check-o', + // } + // ], + // }, + // { + // name: 'Control Monitoring', + // url: '/control-monitoring', + // icon: 'cil-clock', + // children: [ + // { + // name: 'Control Monitoring', + // url: '/control-monitoring', + // icon: 'cil-clock', + // }, + // { + // name: 'Presensi Resource', + // url: '/presensi-resource', + // icon: 'fa fa-calendar-check-o', + // }, + // { + // name: 'Absensi Resource', + // url: '/absensi-resource', + // icon: 'fa fa-calendar-times-o', + // }, + // { + // name: 'Laporan K3', + // url: '/laporan-k3', + // icon: 'fa fa-plus-circle', + // }, + // { + // name: 'Broadcast', + // url: '/broadcast', + // icon: 'cil-bullhorn', + // } + // ] + // }, + // { + // name: 'Closing', + // url: '/closing', + // icon: 'fa fa-calendar-times-o' + // }, + // { + // name: 'Setting Platform', + // url: '/base', + // icon: 'fa fa-cog', + // children: [ + // { + // name: 'Menu', + // url: '/menu', + // icon: 'fa fa-list', + // }, + // { + // name: 'Role', + // url: '/roles', + // icon: 'cil-people', + // }, + // { + // name: 'Project Role', + // url: '/project-role', + // icon: 'fa fa-tags', + // }, + // // { + // // name: 'Admin', + // // url: '/user-admin', + // // icon: 'icon-user', + // // }, + // { + // name: 'Project Type', + // url: '/project-type', + // icon: 'fa fa-bookmark-o', + // }, + // { + // name: 'Alert Notifikasi', + // url: '/config-alert', + // icon: 'fa fa-bell', + // } + // ], + // } + // ] + console.log("menu", menu) + this.setState({ menu: { items: menu } }, () => { + // this.checkLocation(); + }) + + } + + checkLocation = () => { + const { pathname } = this.props.location; + let getIndex = this.getIndexMenu(pathname); + if (getIndex < 0) { + this.props.history.push("/403"); + } + } + + getIndexMenu = (url) => { + let index = this.state.menu.items.findIndex(obj => obj.url === url); + return index + } + + getMenu = () => { + const { u_group } = this.state; + if (u_group == 'kominfo') { + // return + } + else { + return + } + } + + getAppBreadcrumb = () => { + const { u_group } = this.state; + if (u_group == 'kominfo') { + routes.map((route, idx) => { + if (route.path == '/dashboard-kominfo') { + return + } + else { + return + } + }); + } + else { + return + } + } + + isKominfo = () => { + let u_group = localStorage.getItem('u_group'); + if (u_group == 'kominfo') { + return true; + } + else { + return false; + } + } + + render() { + // const u_group = localStorage.getItem('u_group') + // if(u_group === null){ + // return( + // + // ) + // } + return ( +
+ + + this.signOut(e)} /> + + +
+ + + + + + {this.getMenu()} + + + + +
+ {this.state.breadrCrumbReady ? this.getAppBreadcrumb() : null} + {/**/} + + + + {routes.map((route, idx) => { + return route.component ? ( + ( + + )} /> + ) : (null); + })} + + + + +
+ + + + + +
+ {!this.isKominfo ? + ( + + + + + + ) : null + } +
+ ); + } +} + +export default DefaultLayout; diff --git a/src/containers/DefaultLayout/__tests__/DefaultAside.test.js b/src/containers/DefaultLayout/__tests__/DefaultAside.test.js new file mode 100644 index 0000000..b017646 --- /dev/null +++ b/src/containers/DefaultLayout/__tests__/DefaultAside.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import DefaultAside from '../DefaultAside'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/containers/DefaultLayout/__tests__/DefaultFooter.test.js b/src/containers/DefaultLayout/__tests__/DefaultFooter.test.js new file mode 100644 index 0000000..4bbb3e8 --- /dev/null +++ b/src/containers/DefaultLayout/__tests__/DefaultFooter.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import DefaultFooter from '../DefaultFooter'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/containers/DefaultLayout/__tests__/DefaultHeader.test.js b/src/containers/DefaultLayout/__tests__/DefaultHeader.test.js new file mode 100644 index 0000000..d83e72f --- /dev/null +++ b/src/containers/DefaultLayout/__tests__/DefaultHeader.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { MemoryRouter } from 'react-router-dom'; +import DefaultHeader from '../DefaultHeader'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/containers/DefaultLayout/__tests__/DefaultLayout.test.js b/src/containers/DefaultLayout/__tests__/DefaultLayout.test.js new file mode 100644 index 0000000..a2d5145 --- /dev/null +++ b/src/containers/DefaultLayout/__tests__/DefaultLayout.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import {MemoryRouter, Route} from 'react-router-dom'; +import DefaultLayout from '../DefaultLayout'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/containers/DefaultLayout/index.js b/src/containers/DefaultLayout/index.js new file mode 100644 index 0000000..2719acd --- /dev/null +++ b/src/containers/DefaultLayout/index.js @@ -0,0 +1,3 @@ +import DefaultLayout from './DefaultLayout'; + +export default DefaultLayout; diff --git a/src/containers/DefaultLayout/package.json b/src/containers/DefaultLayout/package.json new file mode 100644 index 0000000..b8cc824 --- /dev/null +++ b/src/containers/DefaultLayout/package.json @@ -0,0 +1,6 @@ +{ + "name": "DefaultLayout", + "version": "0.0.0", + "private": true, + "main": "./DefaultLayout.js" +} diff --git a/src/containers/index.js b/src/containers/index.js new file mode 100644 index 0000000..35f9b9d --- /dev/null +++ b/src/containers/index.js @@ -0,0 +1,3 @@ +import DefaultLayout from './DefaultLayout'; + +export { DefaultLayout }; diff --git a/src/dummy_data/paxel.geojson b/src/dummy_data/paxel.geojson new file mode 100644 index 0000000..9140641 --- /dev/null +++ b/src/dummy_data/paxel.geojson @@ -0,0 +1,101 @@ +{ + "type": "FeatureCollection", + "totalFeatures": "unknown", + "features": [ + { + "type": "Feature", + "id": "paxel_area_code_20210426.5083", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + 106.522270001261, + -6.66321000044036 + ], + [ + 106.536260000699, + -6.66382999935416 + ], + [ + 106.556582195013, + -6.68497003983464 + ], + [ + 106.541310000657, + -6.71486999865072 + ], + [ + 106.542630000375, + -6.74058999942815 + ], + [ + 106.550740756582, + -6.7473471734325 + ], + [ + 106.533284161136, + -6.75148465469226 + ], + [ + 106.524773753407, + -6.76232523192311 + ], + [ + 106.498398062322, + -6.75218427058684 + ], + [ + 106.46682763259, + -6.75104031405846 + ], + [ + 106.459021692598, + -6.73381922767441 + ], + [ + 106.481760000125, + -6.72494999944212 + ], + [ + 106.504030000085, + -6.70412999979885 + ], + [ + 106.51431934866, + -6.65951255475977 + ], + [ + 106.522270001261, + -6.66321000044036 + ] + ] + ] + ] + }, + "geometry_name": "the_geom", + "properties": { + "fid": 37287, + "desa": "MALASARI", + "provinsi": "JAWA BARAT", + "kabkot": "BOGOR", + "kecamatan": "NANGGUNG", + "post_code": null, + "city_code": "", + "ph_code": "", + "locker_cod": "", + "area_code": "", + "id": 37287, + "cluster_area_code": "", + "service_ty": null + } + } + ], + "crs": { + "type": "name", + "properties": { + "name": "urn:ogc:def:crs:EPSG::4326" + } + } +} \ No newline at end of file diff --git a/src/dummy_data/route.json b/src/dummy_data/route.json new file mode 100644 index 0000000..9bb8756 --- /dev/null +++ b/src/dummy_data/route.json @@ -0,0 +1,7 @@ +{ + "routes": [ + { + "geometry": "hldhx@lnau`BCG_EaC??cFjAwDjF??uBlKMd@}@z@??aC^yk@z_@se@b[wFdE??wFfE}NfIoGxB_I\\gG}@eHoCyTmPqGaBaHOoD\\??yVrGotA|N??o[N_STiwAtEmHGeHcAkiA}^aMyBiHOkFNoI`CcVvM??gG^gF_@iJwC??eCcA]OoL}DwFyCaCgCcCwDcGwHsSoX??wI_EkUFmq@hBiOqBgTwS??iYse@gYq\\cp@ce@{vA}s@csJqaE}{@iRaqE{lBeRoIwd@_T{]_Ngn@{PmhEwaA{SeF_u@kQuyAw]wQeEgtAsZ}LiCarAkVwI}D??_}RcjEinPspDwSqCgs@sPua@_OkXaMeT_Nwk@ob@gV}TiYs[uTwXoNmT{Uyb@wNg]{Nqa@oDgNeJu_@_G}YsFw]kDuZyDmm@i_@uyIJe~@jCg|@nGiv@zUi_BfNqaAvIow@dEed@dCcf@r@qz@Egs@{Acu@mCum@yIey@gGig@cK_m@aSku@qRil@we@{mAeTej@}Tkz@cLgr@aHko@qOmcEaJw~C{w@kai@qBchBq@kmBS{kDnBscBnFu_Dbc@_~QHeU`IuyDrC_}@bByp@fCyoA?qMbD}{AIkeAgBk_A_A{UsDke@gFej@qH{o@qGgb@qH{`@mMgm@uQus@kL{_@yOmd@ymBgwE}x@ouBwtA__DuhEgaKuWct@gp@cnBii@mlBa_@}|Asj@qrCg^eaC}L{dAaJ_aAiOyjByH{nAuYu`GsAwXyn@ywMyOyqD{_@cfIcDe}@y@aeBJmwA`CkiAbFkhBlTgdDdPyiB`W}xDnSa}DbJyhCrXitAhT}x@bE}Z_@qW_Kwv@qKaaAiBgXvIm}A~JovAxCqW~WanB`XewBbK{_A`K}fBvAmi@xBycBeCauBoF}}@qJioAww@gjHaPopA_NurAyJku@uGmi@cDs[eRaiBkQstAsQkcByNmaCsK_uBcJgbEw@gkB_@ypEqDoqSm@eZcDwjBoGw`BoMegBaU_`Ce_@_uBqb@ytBwkFqiT_fAqfEwe@mfCka@_eC_UmlB}MmaBeWkkDeHwqAoX}~DcBsZmLcxBqOwqE_DkyAuJmrJ\\o~CfIewG|YibQxBssB?es@qGciA}RorAoVajA_nAodD{[y`AgPqp@mKwr@ms@umEaW{dAmb@umAw|@ojBwzDaaJsmBwbEgdCsrFqhAihDquAi`Fux@}_Dui@_eB_u@guCuyAuiHukA_lKszAu|OmaA{wKm}@clHs_A_rEahCssKo\\sgBsSglAqk@yvDcS_wAyTwpBmPc|BwZknFoFscB_GsaDiZmyMyLgtHgQonHqT{hKaPg}Dqq@m~Hym@c`EuiBudIabB{hF{pWifx@snAw`GkFyVqf@y~BkoAi}Lel@wtc@}`@oaXi_C}pZsi@eqGsSuqJ|Lqeb@e]kgPcaAu}SkDwzGhn@gjYh\\qlNZovJieBqja@ed@siO{[ol\\kCmjMe\\isHorCmec@uLebB}EqiBaCg}@m@qwHrT_vFps@kkI`uAszIrpHuzYxx@e{Crw@kpDhN{wBtQarDy@knFgP_yCu\\wyCwyA{kHo~@omEoYmoDaEcPiuAosDagD}rO{{AsyEihCayFilLaiUqm@_bAumFo}DgqA_uByi@swC~AkzDlhA}xEvcBa}Cxk@ql@`rAo|@~bBq{@``Bye@djDww@z_C_cAtn@ye@nfC_eC|gGahH~s@w}@``Fi~FpnAooC|u@wlEaEedRlYkrPvKerBfYs}Arg@m}AtrCkzElw@gjBbh@woBhR{gCwGkgCc[wtCuOapAcFoh@uBy[yBgr@c@iq@o@wvEv@sp@`FajBfCaq@fIipAdy@ewJlUc`ExGuaBdEmbBpBssArAuqBBg}@s@g{AkB{bBif@_bYmC}r@kDgm@sPq_BuJ_s@{X_{AsK_d@eM{d@wVgx@oWcu@??aDmOkNia@wFoSmDyMyCkPiBePwAob@XcQ|@oNdCoSfFwXhEmOnLi\\lbAulB`X_d@|k@au@bc@oc@bqC}{BhwDgcD`l@ed@??bL{G|a@eTje@oS~]cLr~Bgh@|b@}Jv}EieAlv@sPluD{z@nzA_]`|KchCtd@sPvb@wSb{@ko@f`RooQ~e[upZbuIolI|gFafFzu@iq@nMmJ|OeJn^{Qjh@yQhc@uJ~j@iGdd@kAp~BkBxO{@|QsAfYgEtYiGd]}Jpd@wRhVoNzNeK`j@ce@vgK}cJnSoSzQkVvUm^rSgc@`Uql@xIq\\vIgg@~kDyq[nIir@jNoq@xNwc@fYik@tk@su@neB}uBhqEesFjoGeyHtCoD|D}Ed|@ctAbIuOzqB_}D~NgY`\\um@v[gm@v{Cw`G`w@o{AdjAwzBh{C}`Gpp@ypAxn@}mAfz@{bBbNia@??jIab@`CuOlC}YnAcV`@_^m@aeB}@yk@YuTuBg^uCkZiGk\\yGeY}Lu_@oOsZiTe[uWi[sl@mo@soAauAsrBgzBqgAglAyd@ig@asAcyAklA}qAwHkGi{@s~@goAmsAyDeEirB_{B}IsJuEeFymAssAkdAmhAyTcVkFeEoKiH}l@kp@wg@sj@ku@ey@uh@kj@}EsFmG}Jk^_r@_f@m~@ym@yjA??a@cFd@kBrCgDbAUnAcBhAyAdk@et@??kF}D??OL" + } + ] +} \ No newline at end of file diff --git a/src/dummy_data/route2.json b/src/dummy_data/route2.json new file mode 100644 index 0000000..7d09827 --- /dev/null +++ b/src/dummy_data/route2.json @@ -0,0 +1,257 @@ +{ + "geocoded_waypoints": [ + { + "geocoder_status": "OK", + "place_id": "ChIJT2zELKHxaS4RR5fpIKhtekA", + "types": [ + "street_address" + ] + }, + { + "geocoder_status": "OK", + "place_id": "ChIJdbdzpG7xaS4RUCaaEC6NIOo", + "types": [ + "street_address" + ] + } + ], + "routes": [ + { + "bounds": { + "northeast": { + "lat": -6.245234399999999, + "lng": 106.8006477 + }, + "southwest": { + "lat": -6.2623544, + "lng": 106.7885345 + } + }, + "copyrights": "Map data ©2021", + "legs": [ + { + "distance": { + "text": "3.0 km", + "value": 2992 + }, + "duration": { + "text": "9 mins", + "value": 552 + }, + "end_address": "Jl. Melawai 5 No.172, RT.3/RW.1, Melawai, Kec. Kby. Baru, Kota Jakarta Selatan, Daerah Khusus Ibukota Jakarta 12160, Indonesia", + "end_location": { + "lat": -6.245234399999999, + "lng": 106.8006477 + }, + "start_address": "Jl. Bri Radio Dalam No.34/4, RT.2/RW.15, Gandaria Utara, Kec. Kby. Baru, Kota Jakarta Selatan, Daerah Khusus Ibukota Jakarta 12140, Indonesia", + "start_location": { + "lat": -6.2623544, + "lng": 106.7885345 + }, + "steps": [ + { + "distance": { + "text": "1.5 km", + "value": 1495 + }, + "duration": { + "text": "4 mins", + "value": 221 + }, + "end_location": { + "lat": -6.2493628, + "lng": 106.7918222 + }, + "html_instructions": "Head north on Jl. Radio Dalam Raya toward Jl. Dwijaya Raya
Pass by Alfamart Radio Dalam 4 (on the right)
", + "polyline": { + "points": "tbfe@icxjSOEs@M}@K}C]m@KUKg@G[EKAQCaAO_@GGAoB[SEaCa@e@Ga@GuB]yA[{Ba@iAYkA[a@KqAi@w@WaA[KCgAYQCMAw@I}AKUCeAOQCoAUMCoAQyAQc@EcBKe@C}@IC?g@E_AG" + }, + "start_location": { + "lat": -6.2623544, + "lng": 106.7885345 + }, + "travel_mode": "DRIVING" + }, + { + "distance": { + "text": "0.5 km", + "value": 532 + }, + "duration": { + "text": "2 mins", + "value": 99 + }, + "end_location": { + "lat": -6.2491213, + "lng": 106.7959681 + }, + "html_instructions": "Turn right onto Jl. Kramat Pela", + "maneuver": "turn-right", + "polyline": { + "points": "nqce@{wxjSMA\\sBJm@JOBQFa@h@cC@EN_@Le@Hc@?QBQBIDQ@Q@O?UASCSAMCGEGU]QSWWQUq@q@MGQEGAKA" + }, + "start_location": { + "lat": -6.2493628, + "lng": 106.7918222 + }, + "travel_mode": "DRIVING" + }, + { + "distance": { + "text": "25 m", + "value": 25 + }, + "duration": { + "text": "1 min", + "value": 4 + }, + "end_location": { + "lat": -6.249124, + "lng": 106.7961904 + }, + "html_instructions": "Turn right toward Jl. Barito II", + "maneuver": "turn-right", + "polyline": { + "points": "~oce@yqyjSAK@_@" + }, + "start_location": { + "lat": -6.2491213, + "lng": 106.7959681 + }, + "travel_mode": "DRIVING" + }, + { + "distance": { + "text": "0.2 km", + "value": 154 + }, + "duration": { + "text": "1 min", + "value": 36 + }, + "end_location": { + "lat": -6.249612600000001, + "lng": 106.7974484 + }, + "html_instructions": "Turn right onto Jl. Barito II", + "maneuver": "turn-right", + "polyline": { + "points": "~oce@esyjS`@_@P[JSJ]DM@KH}@Bu@" + }, + "start_location": { + "lat": -6.249124, + "lng": 106.7961904 + }, + "travel_mode": "DRIVING" + }, + { + "distance": { + "text": "0.4 km", + "value": 414 + }, + "duration": { + "text": "1 min", + "value": 77 + }, + "end_location": { + "lat": -6.2459198, + "lng": 106.7979545 + }, + "html_instructions": "Turn left onto Jl. Panglima Polim (signs for Senayan/Semanggi)
Pass by ATM Bank UOB (on the left)
", + "maneuver": "turn-left", + "polyline": { + "points": "`sce@a{yjSmBMgDUyBQGAyDW[CgAIk@E" + }, + "start_location": { + "lat": -6.249612600000001, + "lng": 106.7974484 + }, + "travel_mode": "DRIVING" + }, + { + "distance": { + "text": "0.2 km", + "value": 220 + }, + "duration": { + "text": "1 min", + "value": 48 + }, + "end_location": { + "lat": -6.245905899999999, + "lng": 106.7999397 + }, + "html_instructions": "Turn right onto Jl. Melawai Raya
Pass by MM Juice Melawai (on the right)
", + "maneuver": "turn-right", + "polyline": { + "points": "~{be@e~yjS?SAq@?S?A?]Cg@AKD_F" + }, + "start_location": { + "lat": -6.2459198, + "lng": 106.7979545 + }, + "travel_mode": "DRIVING" + }, + { + "distance": { + "text": "74 m", + "value": 74 + }, + "duration": { + "text": "1 min", + "value": 33 + }, + "end_location": { + "lat": -6.2452361, + "lng": 106.7999455 + }, + "html_instructions": "Turn left after HAZEL Brew Coffee (on the right)", + "maneuver": "turn-left", + "polyline": { + "points": "|{be@sjzjSO?C?_@?qAA" + }, + "start_location": { + "lat": -6.245905899999999, + "lng": 106.7999397 + }, + "travel_mode": "DRIVING" + }, + { + "distance": { + "text": "78 m", + "value": 78 + }, + "duration": { + "text": "1 min", + "value": 34 + }, + "end_location": { + "lat": -6.245234399999999, + "lng": 106.8006477 + }, + "html_instructions": "Turn right onto Jl. Melawai 9
Destination will be on the left
", + "maneuver": "turn-right", + "polyline": { + "points": "vwbe@ujzjSAkC" + }, + "start_location": { + "lat": -6.2452361, + "lng": 106.7999455 + }, + "travel_mode": "DRIVING" + } + ], + "traffic_speed_entry": [], + "via_waypoint": [] + } + ], + "overview_polyline": { + "points": "tbfe@icxjScAS{Ei@m@KUKcAM_C]mGeAgAOoEy@eE{@mBg@iCaAmA_@yA]eAKsBOwAS}AYiDc@kF_@yBOh@aDJOBQFa@h@cCPe@ViABc@H[Ba@Ai@Ea@IOg@q@i@m@q@q@MGYGKAAK@_@`@_@\\o@Pk@JiABu@mBMaHg@eHg@k@E?SAeAEsAD_FO?c@?qAAAkC" + }, + "summary": "Jl. Radio Dalam Raya", + "warnings": [], + "waypoint_order": [] + } + ], + "status": "OK" +} \ No newline at end of file diff --git a/src/dummy_data/route3.geojson b/src/dummy_data/route3.geojson new file mode 100644 index 0000000..9304824 --- /dev/null +++ b/src/dummy_data/route3.geojson @@ -0,0 +1,259 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "id": "sales.1", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "sales.1", + "name": "Sales 1" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 106.78853, + -6.26235 + ], + [ + 106.78863, + -6.26201 + ], + [ + 106.78884, + -6.26091 + ], + [ + 106.7889, + -6.26068 + ], + [ + 106.78896, + -6.26057 + ], + [ + 106.78903, + -6.26023 + ], + [ + 106.78918, + -6.25959 + ], + [ + 106.78953, + -6.25824 + ], + [ + 106.78961, + -6.25788 + ], + [ + 106.7899, + -6.25684 + ], + [ + 106.7902, + -6.25585 + ], + [ + 106.7904, + -6.2553 + ], + [ + 106.79073, + -6.25461 + ], + [ + 106.79089, + -6.25422 + ], + [ + 106.79104, + -6.25377 + ], + [ + 106.7911, + -6.25342 + ], + [ + 106.79118, + -6.25284 + ], + [ + 106.79128, + -6.2524 + ], + [ + 106.79141, + -6.25193 + ], + [ + 106.79159, + -6.25108 + ], + [ + 106.79175, + -6.2499 + ], + [ + 106.79183, + -6.24929 + ], + [ + 106.79264, + -6.2495 + ], + [ + 106.79272, + -6.24956 + ], + [ + 106.79281, + -6.24958 + ], + [ + 106.79298, + -6.24962 + ], + [ + 106.79364, + -6.24983 + ], + [ + 106.79383, + -6.24992 + ], + [ + 106.7942, + -6.25004 + ], + [ + 106.79438, + -6.25006 + ], + [ + 106.79452, + -6.25011 + ], + [ + 106.79469, + -6.25013 + ], + [ + 106.7949, + -6.25012 + ], + [ + 106.79507, + -6.25009 + ], + [ + 106.79515, + -6.25004 + ], + [ + 106.7954, + -6.24984 + ], + [ + 106.79563, + -6.24963 + ], + [ + 106.79588, + -6.24938 + ], + [ + 106.79592, + -6.24931 + ], + [ + 106.79596, + -6.24918 + ], + [ + 106.79597, + -6.24912 + ], + [ + 106.79603, + -6.24911 + ], + [ + 106.79619, + -6.24912 + ], + [ + 106.79635, + -6.24929 + ], + [ + 106.79659, + -6.24944 + ], + [ + 106.79681, + -6.24953 + ], + [ + 106.79718, + -6.24959 + ], + [ + 106.79745, + -6.24961 + ], + [ + 106.79752, + -6.24906 + ], + [ + 106.79772, + -6.24761 + ], + [ + 106.79792, + -6.24614 + ], + [ + 106.79795, + -6.24592 + ], + [ + 106.79805, + -6.24592 + ], + [ + 106.7984, + -6.24591 + ], + [ + 106.79882, + -6.24588 + ], + [ + 106.79994, + -6.24591 + ], + [ + 106.79994, + -6.24583 + ], + [ + 106.79994, + -6.24565 + ], + [ + 106.79995, + -6.24524 + ], + [ + 106.80065, + -6.24523 + ] + ] + } + } + ] +} \ No newline at end of file diff --git a/src/dummy_data/sales.geojson b/src/dummy_data/sales.geojson new file mode 100644 index 0000000..c9e0832 --- /dev/null +++ b/src/dummy_data/sales.geojson @@ -0,0 +1,85 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "id": "sales.1", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "sales.1", + "name": "Sales 1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.79569244384766, + -6.2395378839383016 + ] + } + }, + { + "id": "sales.2", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "sales.2", + "name": "Sales 2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.78788185119627, + -6.2620624718682665 + ] + } + }, + { + "id": "sales.3", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "sales.3", + "name": "Sales 3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.81011199951172, + -6.249349850172153 + ] + } + }, + { + "id": "sales.4", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "sales.4", + "name": "Sales 4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.81543350219727, + -6.215817763782759 + ] + } + }, + { + "id": "sales.5", + "geometry_name": "the_geom", + "type": "Feature", + "properties": { + "id": "sales.5", + "name": "Sales 5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.80745124816895, + -6.2746041510497355 + ] + } + } + ] +} \ No newline at end of file diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..3abe104 --- /dev/null +++ b/src/index.css @@ -0,0 +1,24 @@ +.nowrap { + white-space: nowrap; +} + +.withBtn .modal-title { + width: 100%; + display: flex; + justify-content: space-between; +} + +.shift-schedule .ant-table-thead>tr>th { + padding: 2px 0px !important; + /* margin: 0 !important; */ +} + +.shift-schedule .ant-table-tbody>tr>td { + padding: 2px 0px !important; + /* margin: 0 !important; */ +} + +.sidebar-minimized .sidebar .nav > .nav-dropdown > .nav-dropdown-items { + max-height: 300px !important; + overflow: auto; +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..9ac5b40 --- /dev/null +++ b/src/index.js @@ -0,0 +1,17 @@ +import 'react-app-polyfill/ie9'; // For IE 9-11 support +import 'react-app-polyfill/stable'; +// import 'react-app-polyfill/ie11'; // For IE 11 support +import './polyfill' +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './App'; +import * as serviceWorker from './serviceWorker'; +import "antd/dist/antd.css"; + +ReactDOM.render(, document.getElementById('root')); + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: http://bit.ly/CRA-PWA +serviceWorker.unregister(); diff --git a/src/polyfill.js b/src/polyfill.js new file mode 100644 index 0000000..57f887d --- /dev/null +++ b/src/polyfill.js @@ -0,0 +1,46 @@ +/* +* required polyfills +*/ +import "core-js"; +import 'core-js/features/set/map'; + +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +// import 'core-js/es6/symbol' +// import 'core-js/es6/object' +// import 'core-js/es6/function' +// import 'core-js/es6/parse-int' +// import 'core-js/es6/parse-float' +// import 'core-js/es6/number' +// import 'core-js/es6/math' +// import 'core-js/es6/string' +// import 'core-js/es6/date' +// import 'core-js/es6/array' +// import 'core-js/es6/regexp' +// import 'core-js/es6/map' +// import 'core-js/es6/weak-map' +// import 'core-js/es6/set' +// import 'core-js/es7/object' + +/** IE10 and IE11 requires the following for the Reflect API. */ +// import 'core-js/es6/reflect' + +/** Evergreen browsers require these. **/ +// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. +// import 'core-js/es7/reflect' + +// CustomEvent() constructor functionality in IE9, IE10, IE11 +(function () { + + if ( typeof window.CustomEvent === "function" ) return false + + function CustomEvent ( event, params ) { + params = params || { bubbles: false, cancelable: false, detail: undefined } + var evt = document.createEvent( 'CustomEvent' ) + evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ) + return evt + } + + CustomEvent.prototype = window.Event.prototype + + window.CustomEvent = CustomEvent +})() diff --git a/src/routes.js b/src/routes.js new file mode 100644 index 0000000..5c8163c --- /dev/null +++ b/src/routes.js @@ -0,0 +1,113 @@ +import React from 'react'; + +const BaseLayers = React.lazy(() => import('./views/BaseLayers')); +const Layers = React.lazy(() => import('./views/Layers/Layers')); +const Layer = React.lazy(() => import('./views/Layers/Layer')); +const MapConfig = React.lazy(() => import('./views/MapConfig')); +// const DashboardKominfo = React.lazy(() => import('./views/DashboardKominfo')); +// const Sales = React.lazy(() => import('./views/Master/MasterSales')); +// const GroupSales = React.lazy(() => import('./views/Master/MasterGroupSales')); +const OfficeHours = React.lazy(() => import('./views/Master/MasterOfficeHours')); +// const Customer = React.lazy(() => import('./views/Master/MasterCustomer')); +// const SettingSales = React.lazy(() => import('./views/Master/MasterSales/SettingSales')); +// const SettingOffice = React.lazy(() => import('./views/Master/MasterOffice/SettingOffice')); +// const SettingCustomer = React.lazy(() => import('./views/Master/MasterCustomer/SettingCustomer')); +// new +const DivisiKaryawan = React.lazy(() => import('./views/Master/MasterTipeKaryawan')); +const UserAdmin = React.lazy(() => import('./views/Master/UserAdmin')); +// const UserWaspang = React.lazy(() => import('./views/Master/UserWaspang')); +const Presensi = React.lazy(() => import('./views/SimproV2/Presence')); +const LaporanTugas = React.lazy(() => import('./views/Master/MasterTask')); +const Izin = React.lazy(() => import('./views/Master/MasterCuti')); +const Roles = React.lazy(() => import('./views/Master/MasterRoles')); +const ProjectRole = React.lazy(() => import('./views/Master/RoleProject')); +const Menu = React.lazy(() => import('./views/Master/MasterMenu')); +const Broadcast = React.lazy(() => import('./views/Master/MasterBroadcast')); +const PanicButton = React.lazy(() => import('./views/SimproV2/PanicButton')); +const Absensi = React.lazy(() => import('./views/Master/MasterAbsensi')); +const Lembur = React.lazy(() => import('./views/Master/MasterLembur')); +const Organization = React.lazy(() => import('./views/Master/MasterOrganization')); +const Proyek = React.lazy(() => import('./views/Master/Proyek')); +// const SubProyek = React.lazy(() => import('./views/Master/SubProyek')); +const DashboardSimpro = React.lazy(() => import('./views/DashboardSimpro')); +const controlMonitoring = React.lazy(() => import('./views/Report/ControlMonitoring')); +const K3 = React.lazy(() => import('./views/Report/k3')); +const TestGantt = React.lazy(() => import('./views/testgantt')); +const ConfigAlert = React.lazy(() => import('./views/Master/ConfigAlert')); +// const NetworkDiagram = React.lazy(() => import('./views/Master/NetworkDiagram')); +const LaporanAlert = React.lazy(() => import('./views/Report/alert')); +const ControlMonitoringGantt = React.lazy(() => import('./views/ControlMonitoringGantt')); +const CreatedProyek = React.lazy(() => import('./views/SimproV2/CreatedProyek')); +const ResourceWorker = React.lazy(() => import('./views/SimproV2/ResourceWorker')); +const ResourceMaterial = React.lazy(() => import('./views/SimproV2/ResourceMaterial')); +const ResourceTools = React.lazy(() => import('./views/SimproV2/ResourceTools')); +const PlanningHarian = React.lazy(() => import('./views/SimproV2/PlanningHarian')); +const Closing = React.lazy(() => import('./views/SimproV2/Closing')); +const ProjectType = React.lazy(() => import('./views/SimproV2/ProjectType')); +const Divisi = React.lazy(() => import('./views/SimproV2/Divisi')); +const Satuan = React.lazy(() => import('./views/SimproV2/Satuan')); +const RateCost = React.lazy(() => import('./views/SimproV2/RateCost')); +const Gantt = React.lazy(() => import('./views/SimproV2/Gantt')); +const Shift = React.lazy(() => import('./views/SimproV2/Shift')); +const UserShift = React.lazy(() => import('./views/SimproV2/UserShift')); +const ScheduleShift = React.lazy(() => import('./views/SimproV2/ScheduleShift')); +const ChecklistK3 = React.lazy(() => import('./views/SimproV2/ChecklistK3')); +const DashboardPMO = React.lazy(() => import('./views/DashboardPMO')); +const DashboardProject = React.lazy(() => import('./views/DashboardProject')); +const DashboardSecurity = React.lazy(() => import('./views/DashboardSecurity')); +// const PlanningVsRealisasi = React.lazy(() => import('./views/Master/PlanningVsRealisasi')); + +// https://github.com/ReactTraining/react-router/tree/master/packages/react-router-config +const routes = [ + { path: '/', exact: true, name: 'Home' }, + { path: '/dashboard', name: 'Dashboard', component: DashboardSimpro }, + { path: '/map/config', exact: true, name: 'Config', component: MapConfig }, + { path: '/map/baselayers', exact: true, name: 'Base Layers', component: BaseLayers }, + { path: '/map/layers', exact: true, name: 'Layers', component: Layers }, + { path: '/map/layers/:id', exact: true, name: 'Layer Details', component: Layer }, + { path: '/office-hours', exact: true, name: 'Jam Kerja', component: OfficeHours }, + { path: '/user-admin', exact: true, name: 'User Admin', component: UserAdmin }, + // { path: '/user-waspang', exact: true, name: 'User Waspang', component: UserWaspang }, + { path: '/divisi-karyawan', exact: true, name: 'Divisi Karyawan', component: DivisiKaryawan }, + { path: '/presensi', exact: true, name: 'Presensi', component: Presensi }, + { path: '/absensi', exact: true, name: 'Absensi', component: Absensi }, + { path: '/laporan-tugas-karyawan', exact: true, name: 'Laporan Tugas Karyawan', component: LaporanTugas }, + { path: '/izin', exact: true, name: 'Izin', component: Izin }, + { path: '/broadcast', exact: true, name: 'Broadcast', component: Broadcast }, + { path: '/panic-button', exact: true, name: 'Tombol Darurat', component: PanicButton }, + { path: '/proyek', exact: true, name: 'Created Project', component: Proyek }, + // { path: '/sub-proyek', exact: true, name: 'Subproyek', component: SubProyek }, + { path: '/menu', exact: true, name: 'Menu', component: Menu }, + { path: '/roles', exact: true, name: 'Roles', component: Roles }, + { path: '/lembur', exact: true, name: 'Lembur', component: Lembur }, + { path: '/organization', exact: true, name: 'Organisasi', component: Organization }, + { path: '/control-monitoring', exact: true, name: 'Control Monitoring', component: controlMonitoring }, + { path: '/laporan-k3', exact: true, name: 'Laporan K3', component: K3 }, + { path: '/proyek-gantt', exact: true, name: 'Gantt Chart Proyek', component: TestGantt }, + { path: '/config-alert', exact: true, name: 'Config Alert', component: ConfigAlert }, + { path: '/laporan-alert', exact: true, name: 'Laporan Alert', component: LaporanAlert }, + { path: '/control-monitoring-gantt', exact: true, name: 'Control Monitoring Gantt', component: ControlMonitoringGantt }, + { path: '/projects', exact: true, name: 'Projects', component: CreatedProyek }, + { path: '/human-resource', exact: true, name: 'Human Resource', component: ResourceWorker }, + { path: '/material-resource', exact: true, name: 'Material Resource', component: ResourceMaterial }, + { path: '/tools-resource', exact: true, name: 'Tools Resource', component: ResourceTools }, + { path: '/projects/:id/:project/gantt', exact: true, name: 'Gantt', component: Gantt }, + { path: '/planning-harian', exact: true, name: 'Planning Harian', component: PlanningHarian }, + { path: '/presensi-resource', exact: true, name: 'Presensi Resource', component: Presensi }, + { path: '/absensi-resource', exact: true, name: 'Absensi Resource', component: Absensi }, + { path: '/closing', exact: true, name: 'Closing', component: Closing }, + { path: '/project-type', exact: true, name: 'Project Type', component: ProjectType }, + { path: '/divisi', exact: true, name: 'Divisi', component: Divisi }, + { path: '/satuan', exact: true, name: 'Satuan', component: Satuan }, + { path: '/rate-cost', exact: true, name: 'Rate Cost', component: RateCost }, + { path: '/project-role', exact: true, name: 'Project Role', component: ProjectRole }, + { path: '/working-hour', exact: true, name: 'Working Hour', component: Shift }, + { path: '/user-shift', exact: true, name: 'Shift', component: UserShift }, + { path: '/schedule-shift', exact: true, name: 'Schedule Shift', component: ScheduleShift }, + { path: '/checklist-k3', exact: true, name: 'Checklist K3', component: ChecklistK3 }, + { path: '/dashboard-pmo', exact: true, name: 'Dashboard PMO', component: DashboardPMO }, + { path: '/dashboard-security', exact: true, name: 'Dashboard Security', component: DashboardSecurity }, + { path: '/dashboard-project', exact: true, name: 'Dashboard Project', component: DashboardProject }, +]; + +export default routes; diff --git a/src/scss/_custom.scss b/src/scss/_custom.scss new file mode 100644 index 0000000..cc6452e --- /dev/null +++ b/src/scss/_custom.scss @@ -0,0 +1,135 @@ +// Here you can add other styles +// @media (min-width: 992px) +.sidebar-minimized .sidebar .nav-link { + min-width: 300px !important; +} + +.sidebar-minimized .sidebar .nav-link:hover { + min-width: 300px !important; +} + +.sidebar .nav-dropdown-items { + min-width: 250px !important; +} + +.sidebar-minimized .sidebar .nav-dropdown-items .nav-item { + min-width: 250px !important; +} + + +.box-dashboard-pm { + margin-top: 10px; + width: 100%; + height: 90px; + padding: 10px; + border-radius: 2px; + box-shadow: 1px 1px 3px 0px rgba(97, 96, 96, 0.5); + -webkit-box-shadow: 1px 1px 3px 0px rgba(97, 96, 96, 0.5); + -moz-box-shadow: 1px 1px 3px 0px rgba(97, 96, 96, 0.5); + + .box-icon { + margin: 0; + position: absolute; + top: 50%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + } + + .box-content { + text-align: center; + + .text-box { + color: #fff; + font-weight: 500 + } + + .text-box-secondary { + color: #e6e6e6; + font-weight: 500 + } + } +} + +.health-status { + height: 15px; + width: 15px; + border-radius: 50%; +} + +.health-status-danger { + @extend .health-status; + background-color: #b30000; +} + +.health-status-good { + @extend .health-status; + background-color: #047857; +} + +.health-status-warning { + @extend .health-status; + background-color: #e68a00; +} + +.health-status-default { + @extend .health-status; + background-color: #a8a29e; +} + +.box-header-dashboard-project { + padding: 10px; + // background-color: #a8a29e; + border-radius: 3px; + box-shadow: 0px 0px 3px 0px rgba(97, 96, 96, 0.4); + -webkit-box-shadow: 0px 0px 3px 0px rgba(97, 96, 96, 0.4); + -moz-box-shadow: 0px 0px 3px 0px rgba(97, 96, 96, 0.4); +} + +#map { + // position: absolute; + width: 100%; + height: 400px; +} + +.chat { + overflow: auto; + height: 80vh; +} + +.chat-content { + margin: 8px 0; + font-size: 0.8em; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; + border-top-right-radius: 10px; + + box-shadow: 0px 0px 3px 0px rgba(97, 96, 96, 0.4) inset; + -webkit-box-shadow: 0px 0px 3px 0px rgba(97, 96, 96, 0.4) inset; + -moz-box-shadow: 0px 0px 3px 0px rgba(97, 96, 96, 0.4) inset; + // pointer-events: none; +} + +.chat-header { + font-weight: bold; + color: rgb(53, 52, 52); +} + +.chat-body-content { + padding-top: 4px; + font-weight: 500; + color: rgb(78, 78, 78) +} + +.chat-footer { + padding-top: 5px; + font-weight: bold; + color: rgb(151, 151, 151); + font-size: 0.8em; + display: flex; + justify-content: flex-end; +} + +.chat-body { + padding: 6px 10px +} \ No newline at end of file diff --git a/src/scss/_ie-fix.scss b/src/scss/_ie-fix.scss new file mode 100644 index 0000000..8cacff8 --- /dev/null +++ b/src/scss/_ie-fix.scss @@ -0,0 +1,11 @@ +html body .app.flex-row.align-items-center { + height: 100vh; +} + +// ie11 floating footer temp fix, react only +@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + #root { + display: flex; + flex-direction: column; + } +} diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss new file mode 100644 index 0000000..a47111f --- /dev/null +++ b/src/scss/_variables.scss @@ -0,0 +1 @@ +// Variable overrides diff --git a/src/scss/style.scss b/src/scss/style.scss new file mode 100644 index 0000000..01a3be4 --- /dev/null +++ b/src/scss/style.scss @@ -0,0 +1,14 @@ +// If you want to override variables do it here +@import "variables"; + +// Import styles +@import "~@coreui/coreui/scss/coreui.scss"; + +// Temp fix for reactstrap +@import '~@coreui/coreui/scss/_dropdown-menu-right.scss'; + +// If you want to add something do it here +@import "custom"; + +// ie fixes +@import "ie-fix"; diff --git a/src/scss/vendors/.gitkeep b/src/scss/vendors/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/scss/vendors/_variables.scss b/src/scss/vendors/_variables.scss new file mode 100644 index 0000000..10e5ddb --- /dev/null +++ b/src/scss/vendors/_variables.scss @@ -0,0 +1,4 @@ +// Override Boostrap variables +@import "../variables"; +@import "~bootstrap/scss/mixins"; +@import "~@coreui/coreui/scss/variables"; diff --git a/src/serviceWorker.js b/src/serviceWorker.js new file mode 100644 index 0000000..2c5d31f --- /dev/null +++ b/src/serviceWorker.js @@ -0,0 +1,127 @@ +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://goo.gl/SC7cgQ' + ); + }); + } else { + // Is not local host. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log('New content is available; please refresh.'); + + // Execute callback + if (config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/src/setupTests.js b/src/setupTests.js new file mode 100644 index 0000000..5b7289a --- /dev/null +++ b/src/setupTests.js @@ -0,0 +1,15 @@ +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; + +configure({ adapter: new Adapter() }); + +if (global.document) { + document.createRange = () => ( { + setStart: () => {}, + setEnd: () => {}, + commonAncestorContainer: { + nodeName: 'BODY', + ownerDocument: document, + }, + }); +} diff --git a/src/views/Base/Breadcrumbs/Breadcrumbs.js b/src/views/Base/Breadcrumbs/Breadcrumbs.js new file mode 100644 index 0000000..fc5d1a1 --- /dev/null +++ b/src/views/Base/Breadcrumbs/Breadcrumbs.js @@ -0,0 +1,50 @@ +import React, { Component } from 'react'; +import { Breadcrumb, BreadcrumbItem, Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; + +class Breadcrumbs extends Component { + render() { + return ( +
+ + + + + Breadcrumbs + + + + + Home + + + {/*eslint-disable-next-line*/} + Home + Library + + + {/*eslint-disable-next-line*/} + Home + {/* eslint-disable-next-line*/} + Library + Data + + + Home + Library + Data + Bootstrap + + + + + +
+ ); + } +} + +export default Breadcrumbs; diff --git a/src/views/Base/Breadcrumbs/Breadcrumbs.test.js b/src/views/Base/Breadcrumbs/Breadcrumbs.test.js new file mode 100644 index 0000000..a9ab3a7 --- /dev/null +++ b/src/views/Base/Breadcrumbs/Breadcrumbs.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Breadcrumbs from './Breadcrumbs'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Breadcrumbs/package.json b/src/views/Base/Breadcrumbs/package.json new file mode 100644 index 0000000..4b439ca --- /dev/null +++ b/src/views/Base/Breadcrumbs/package.json @@ -0,0 +1,6 @@ +{ + "name": "Breadcrumbs", + "version": "0.0.0", + "private": true, + "main": "./Breadcrumbs.js" +} diff --git a/src/views/Base/Cards/Cards.js b/src/views/Base/Cards/Cards.js new file mode 100644 index 0000000..0393767 --- /dev/null +++ b/src/views/Base/Cards/Cards.js @@ -0,0 +1,416 @@ +import React, { Component } from 'react'; +import { Badge, Card, CardBody, CardFooter, CardHeader, Col, Row, Collapse, Fade } from 'reactstrap'; +import { AppSwitch } from '@coreui/react' + +class Cards extends Component { + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.toggleFade = this.toggleFade.bind(this); + this.state = { + collapse: true, + fadeIn: true, + timeout: 300 + }; + } + + toggle() { + this.setState({ collapse: !this.state.collapse }); + } + + toggleFade() { + this.setState((prevState) => { return { fadeIn: !prevState }}); + } + + render() { + return ( +
+ + + + + Card title + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + Card footer + + + + + + Card with icon +
+ +
+
+ + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + +
+ + + + + Card with switch +
+ +
+
+ + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + +
+ + + + + Card with label +
+ Success +
+
+ + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + +
+ + + + + Card with label +
+ 42 +
+
+ + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + +
+ +
+ + + + + Card outline primary + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card outline secondary + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card outline success + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card outline info + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card outline warning + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card outline danger + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + + + + Card with accent + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card with accent + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card with accent + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card with accent + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card with accent + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card with accent + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + + +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

+
Someone famous in Source Title
+
+
+
+ + + + +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

+
Someone famous in Source Title
+
+
+
+ + + + +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

+
Someone famous in Source Title
+
+
+
+ + + + +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

+
Someone famous in Source Title
+
+
+
+ + + + +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

+
Someone famous in Source Title
+
+
+
+ + + + +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

+
Someone famous in Source Title
+
+
+
+ +
+ + + + + Card title + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card title + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card title + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card title + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + Card title + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + + + + + + + Card actions +
+ {/*eslint-disable-next-line*/} + + {/*eslint-disable-next-line*/} + + {/*eslint-disable-next-line*/} + +
+
+ + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut + laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation + ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + + +
+
+ + +
+
+ ); + } +} + +export default Cards; diff --git a/src/views/Base/Cards/Cards.test.js b/src/views/Base/Cards/Cards.test.js new file mode 100644 index 0000000..b0f9c87 --- /dev/null +++ b/src/views/Base/Cards/Cards.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Cards from './Cards'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Cards/package.json b/src/views/Base/Cards/package.json new file mode 100644 index 0000000..ec9afb4 --- /dev/null +++ b/src/views/Base/Cards/package.json @@ -0,0 +1,6 @@ +{ + "name": "Cards", + "version": "0.0.0", + "private": true, + "main": "./Cards.js" +} diff --git a/src/views/Base/Carousels/Carousels.js b/src/views/Base/Carousels/Carousels.js new file mode 100644 index 0000000..3999e5a --- /dev/null +++ b/src/views/Base/Carousels/Carousels.js @@ -0,0 +1,124 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Carousel, CarouselCaption, CarouselControl, CarouselIndicators, CarouselItem, Col, Row } from 'reactstrap'; + +const items = [ + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_1607923e7e2%20text%20%7B%20fill%3A%23555%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1607923e7e2%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.9296875%22%20y%3D%22217.75625%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 1', + caption: 'Slide 1', + }, + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa20%20text%20%7B%20fill%3A%23444%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa20%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23666%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22247.3203125%22%20y%3D%22218.3%22%3ESecond%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 2', + caption: 'Slide 2', + }, + { + src: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa21%20text%20%7B%20fill%3A%23333%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa21%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23555%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22277%22%20y%3D%22218.3%22%3EThird%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + altText: 'Slide 3', + caption: 'Slide 3', + }, +]; + +class Carousels extends Component { + + constructor(props) { + super(props); + this.state = { activeIndex: 0 }; + this.next = this.next.bind(this); + this.previous = this.previous.bind(this); + this.goToIndex = this.goToIndex.bind(this); + this.onExiting = this.onExiting.bind(this); + this.onExited = this.onExited.bind(this); + } + + onExiting() { + this.animating = true; + } + + onExited() { + this.animating = false; + } + + next() { + if (this.animating) return; + const nextIndex = this.state.activeIndex === items.length - 1 ? 0 : this.state.activeIndex + 1; + this.setState({ activeIndex: nextIndex }); + } + + previous() { + if (this.animating) return; + const nextIndex = this.state.activeIndex === 0 ? items.length - 1 : this.state.activeIndex - 1; + this.setState({ activeIndex: nextIndex }); + } + + goToIndex(newIndex) { + if (this.animating) return; + this.setState({ activeIndex: newIndex }); + } + + render() { + const { activeIndex } = this.state; + + const slides = items.map((item) => { + return ( + + {item.altText} + + ); + }); + + const slides2 = items.map((item) => { + return ( + + {item.altText} + + + ); + }); + + return ( +
+ + + + + Carousel + + + + + {slides} + + + + + + + + Carousel + + + + + {slides2} + + + + + + + +
+ ); + } +} + +export default Carousels; \ No newline at end of file diff --git a/src/views/Base/Carousels/Carousels.test.js b/src/views/Base/Carousels/Carousels.test.js new file mode 100644 index 0000000..efee537 --- /dev/null +++ b/src/views/Base/Carousels/Carousels.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Carousels from './Carousels'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Carousels/package.json b/src/views/Base/Carousels/package.json new file mode 100644 index 0000000..5c16947 --- /dev/null +++ b/src/views/Base/Carousels/package.json @@ -0,0 +1,6 @@ +{ + "name": "Carousels", + "version": "0.0.0", + "private": true, + "main": "./Carousels.js" +} diff --git a/src/views/Base/Collapses/Collapses.js b/src/views/Base/Collapses/Collapses.js new file mode 100644 index 0000000..1a93021 --- /dev/null +++ b/src/views/Base/Collapses/Collapses.js @@ -0,0 +1,233 @@ +import React, { Component } from 'react'; +import { Badge, Button, Card, CardBody, CardFooter, CardHeader, Col, Collapse, Fade, Row } from 'reactstrap'; + +class Collapses extends Component { + + constructor(props) { + super(props); + this.onEntering = this.onEntering.bind(this); + this.onEntered = this.onEntered.bind(this); + this.onExiting = this.onExiting.bind(this); + this.onExited = this.onExited.bind(this); + this.toggle = this.toggle.bind(this); + this.toggleAccordion = this.toggleAccordion.bind(this); + this.toggleCustom = this.toggleCustom.bind(this); + this.toggleFade = this.toggleFade.bind(this); + this.state = { + collapse: false, + accordion: [true, false, false], + custom: [true, false], + status: 'Closed', + fadeIn: true, + timeout: 300, + }; + } + + onEntering() { + this.setState({ status: 'Opening...' }); + } + + onEntered() { + this.setState({ status: 'Opened' }); + } + + onExiting() { + this.setState({ status: 'Closing...' }); + } + + onExited() { + this.setState({ status: 'Closed' }); + } + + toggle() { + this.setState({ collapse: !this.state.collapse }); + } + + toggleAccordion(tab) { + + const prevState = this.state.accordion; + const state = prevState.map((x, index) => tab === index ? !x : false); + + this.setState({ + accordion: state, + }); + } + + toggleCustom(tab) { + + const prevState = this.state.custom; + const state = prevState.map((x, index) => tab === index ? !x : false); + + this.setState({ + custom: state, + }); + } + + toggleFade() { + this.setState({ fadeIn: !this.state.fadeIn }); + } + + render() { + return ( +
+ + + + + Collapse + + + + +

+ Anim pariatur cliche reprehenderit, + enim eiusmod high life accusamus terry richardson ad squid. Nihil + anim keffiyeh helvetica, craft beer labore wes anderson cred + nesciunt sapiente ea proident. +

+

+ Donec molestie odio id nisi malesuada, mattis tincidunt velit egestas. Sed non pulvinar risus. Aenean + elementum eleifend nunc, pellentesque dapibus arcu hendrerit fringilla. Aliquam in nibh massa. Cras + ultricies lorem non enim volutpat, a eleifend urna placerat. Fusce id luctus urna. In sed leo tellus. + Mauris tristique leo a nisl feugiat, eget vehicula leo venenatis. Quisque magna metus, luctus quis + sollicitudin vel, vehicula nec ipsum. Donec rutrum commodo lacus ut condimentum. Integer vel turpis + purus. Etiam vehicula, nulla non fringilla blandit, massa purus faucibus tellus, a luctus enim orci non + augue. Aenean ullamcorper nisl urna, non feugiat tortor volutpat in. Vivamus lobortis massa dolor, eget + faucibus ipsum varius eget. Pellentesque imperdiet, turpis sed sagittis lobortis, leo elit laoreet arcu, + vehicula sagittis elit leo id nisi. +

+
+
+ + +
+
Current state: {this.state.status}
+
+
+ + + Fade + + + + + This content will fade in and out as the button is pressed... + + + + + + + + + + + Collapse accordion +
+ NEW +
+
+ +
+ + + + + + + 1. Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non + cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird + on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred + nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft + beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. + + + + + + + + + + 2. Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non + cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird + on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred + nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft + beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. + + + + + + + + + + 3. Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non + cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird + on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred + nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft + beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. + + + +
+
+
+ + + Collapse custom accordion +
+ NEW +
+
+ +
+
+ + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pretium lorem non vestibulum scelerisque. Proin a vestibulum sem, eget + tristique massa. Aliquam lacinia rhoncus nibh quis ornare. +

+
+
+
+ + +

+ Donec at ipsum dignissim, rutrum turpis scelerisque, tristique lectus. Pellentesque habitant morbi tristique senectus et netus et + malesuada fames ac turpis egestas. Vivamus nec dui turpis. Orci varius natoque penatibus et magnis dis parturient montes, + nascetur ridiculus mus. +

+
+
+
+
+
+ +
+
+ ); + } +} + +export default Collapses; diff --git a/src/views/Base/Collapses/Collapses.test.js b/src/views/Base/Collapses/Collapses.test.js new file mode 100644 index 0000000..ccd31e0 --- /dev/null +++ b/src/views/Base/Collapses/Collapses.test.js @@ -0,0 +1,73 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Collapses from './Collapses'; +import {mount} from 'enzyme/build'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); + +describe('toggle clicks', function() { + it('collapse without crashing', () => { + const wrapper = mount(); + let collapse = wrapper.find('#toggleCollapse1').at(0); + collapse.simulate('click'); + expect(wrapper.state().collapse).toEqual(true); + collapse.simulate('click'); + expect(wrapper.state().collapse).toEqual(false); + collapse.simulate('click'); + expect(wrapper.state().collapse).toEqual(true); + wrapper.unmount() + }); + it('fade without crashing', () => { + const wrapper = mount(); + let fade = wrapper.find('#toggleFade1').at(0); + fade.simulate('click'); + expect(wrapper.state().fadeIn).toEqual(false); + fade.simulate('click'); + expect(wrapper.state().fadeIn).toEqual(true); + wrapper.unmount() + }); + it('accordion without crashing', () => { + const wrapper = mount(); + let accordion = wrapper.find('[aria-controls="collapseOne"]').at(0); + accordion.simulate('click'); + expect(wrapper.state().accordion[0]).toEqual(false); + expect(wrapper.state().accordion[1]).toEqual(false); + expect(wrapper.state().accordion[2]).toEqual(false); + accordion = wrapper.find('[aria-controls="collapseTwo"]').at(0); + accordion.simulate('click'); + expect(wrapper.state().accordion[0]).toEqual(false); + expect(wrapper.state().accordion[1]).toEqual(true); + expect(wrapper.state().accordion[2]).toEqual(false); + accordion = wrapper.find('[aria-controls="collapseThree"]').at(0); + accordion.simulate('click'); + expect(wrapper.state().accordion[0]).toEqual(false); + expect(wrapper.state().accordion[1]).toEqual(false); + expect(wrapper.state().accordion[2]).toEqual(true); + accordion = wrapper.find('[aria-controls="collapseOne"]').at(0); + accordion.simulate('click'); + expect(wrapper.state().accordion[0]).toEqual(true); + expect(wrapper.state().accordion[1]).toEqual(false); + expect(wrapper.state().accordion[2]).toEqual(false); + wrapper.unmount() + }); + it('custom without crashing', () => { + const wrapper = mount(); + let accordion = wrapper.find('[aria-controls="exampleAccordion1"]').at(0); + accordion.simulate('click'); + expect(wrapper.state().custom[0]).toEqual(false); + expect(wrapper.state().custom[1]).toEqual(false); + accordion = wrapper.find('[aria-controls="exampleAccordion1"]').at(0); + accordion.simulate('click'); + expect(wrapper.state().custom[0]).toEqual(true); + expect(wrapper.state().custom[1]).toEqual(false); + accordion = wrapper.find('[aria-controls="exampleAccordion2"]').at(0); + accordion.simulate('click'); + expect(wrapper.state().custom[0]).toEqual(false); + expect(wrapper.state().custom[1]).toEqual(true); + wrapper.unmount() + }); +}); diff --git a/src/views/Base/Collapses/package.json b/src/views/Base/Collapses/package.json new file mode 100644 index 0000000..60c3569 --- /dev/null +++ b/src/views/Base/Collapses/package.json @@ -0,0 +1,6 @@ +{ + "name": "Collapses", + "version": "0.0.0", + "private": true, + "main": "./Collapses.js" +} diff --git a/src/views/Base/Dropdowns/Dropdowns.js b/src/views/Base/Dropdowns/Dropdowns.js new file mode 100644 index 0000000..ea1dac9 --- /dev/null +++ b/src/views/Base/Dropdowns/Dropdowns.js @@ -0,0 +1,168 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Dropdown, DropdownItem, DropdownMenu, DropdownToggle, Row, UncontrolledDropdown } from 'reactstrap'; + +class Dropdowns extends Component { + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + dropdownOpen: new Array(6).fill(false), + }; + } + + toggle(i) { + const newArray = this.state.dropdownOpen.map((element, index) => { + return (index === i ? !element : false); + }); + this.setState({ + dropdownOpen: newArray, + }); + } + + render() { + return ( +
+ + + + + Dropdowns + + + + { + this.toggle(0); + }}> + + Dropdown + + + Header + Action + Another Action + + Another Action + + + + + + + Dropdowns + alignment + + + {this.toggle(1);}}> + + This dropdown's menu is right-aligned + + + Header + Action + Another Action + + Another Action + + + + + + + Dropdowns + sizing + + + {this.toggle(2);}} size="lg" className="mb-3"> + + Large Dropdown + + + Header + Action + Another Action + + Another Action + + + {this.toggle(3);}} className="mb-3"> + + Normal Dropdown + + + Header + Action + Another Action + + Another Action + + + {this.toggle(4);}} size="sm"> + + Small Dropdown + + + Header + Action + Another Action + + Another Action + + + + + + + Custom Dropdowns + + + {this.toggle(5);}}> + {this.toggle(5);}} + data-toggle="dropdown" + aria-expanded={this.state.dropdownOpen[5]} + > + Custom Dropdown Content * + + +
{this.toggle(5);}}>Custom dropdown item 1
+
{this.toggle(5);}}>Custom dropdown item 2
+
{this.toggle(5);}}>Custom dropdown text 3
+
+
{this.toggle(5);}}>Custom dropdown item 4
+
+
+
+
+ + + Uncontrolled Dropdown + + + + + Uncontrolled Dropdown + + + Header + Action + Another Action + + Another Action + + + + + +
+
+ ); + } +} + +export default Dropdowns; diff --git a/src/views/Base/Dropdowns/Dropdowns.test.js b/src/views/Base/Dropdowns/Dropdowns.test.js new file mode 100644 index 0000000..5d9776c --- /dev/null +++ b/src/views/Base/Dropdowns/Dropdowns.test.js @@ -0,0 +1,27 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { mount } from 'enzyme' +import Dropdowns from './Dropdowns'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); +it('toggle click without crashing', () => { + const wrapper = mount(); + for (let i=0; i<5; i++) { + let Dropdown = wrapper.find('button.dropdown-toggle').at(i); + Dropdown.simulate('click'); + expect(wrapper.state().dropdownOpen[i]).toEqual(true); + } + for (let i=0; i<2; i++) { + let Dropdown = wrapper.find('[data-toggle="dropdown"]').at(0); + Dropdown.simulate('click'); + expect(wrapper.state().dropdownOpen[5]).toEqual(true); + let DropdownItem = wrapper.find('div.dropdown-menu > .dropdown-item').at(i); + DropdownItem.simulate('click'); + expect(wrapper.state().dropdownOpen[5]).toEqual(false); + } + wrapper.unmount() +}); diff --git a/src/views/Base/Dropdowns/package.json b/src/views/Base/Dropdowns/package.json new file mode 100644 index 0000000..cc28d86 --- /dev/null +++ b/src/views/Base/Dropdowns/package.json @@ -0,0 +1,6 @@ +{ + "name": "Dropdowns", + "version": "0.0.0", + "private": true, + "main": "./Dropdowns.js" +} diff --git a/src/views/Base/Forms/Forms.js b/src/views/Base/Forms/Forms.js new file mode 100644 index 0000000..f0e5ad6 --- /dev/null +++ b/src/views/Base/Forms/Forms.js @@ -0,0 +1,1161 @@ +import React, { Component } from 'react'; +import { + Badge, + Button, + Card, + CardBody, + CardFooter, + CardHeader, + Col, + Collapse, + DropdownItem, + DropdownMenu, + DropdownToggle, + Fade, + Form, + FormGroup, + FormText, + FormFeedback, + Input, + InputGroup, + InputGroupAddon, + InputGroupButtonDropdown, + InputGroupText, + Label, + Row, +} from 'reactstrap'; + +class Forms extends Component { + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.toggleFade = this.toggleFade.bind(this); + this.state = { + collapse: true, + fadeIn: true, + timeout: 300 + }; + } + + toggle() { + this.setState({ collapse: !this.state.collapse }); + } + + toggleFade() { + this.setState((prevState) => { return { fadeIn: !prevState }}); + } + + render() { + return ( +
+ + + + + Credit Card + Form + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Company + Form + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Basic Form Elements + + +
+ + + + + +

Username

+ +
+ + + + + + + This is a help text + + + + + + + + + Please enter your email + + + + + + + + + Please enter a complex password + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ + + Inline Form + + +
+ + + + + + + + +
+
+ + + + +
+ + + + + Horizontal Form + + +
+ + + + + + + Please enter your email + + + + + + + + + Please enter your password + + +
+
+ + + + +
+ + + Normal Form + + +
+ + + + Please enter your email + + + + + Please enter your password + +
+
+ + + + +
+ + + Input Grid + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ + + Input Sizes + + +
+ + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+ + + + + Validation feedback Form + + + + + + Cool! Input is valid + + + + + Houston, we have a problem... + + + + + + + + Validation feedback Form + + +
+ + + + Non-required + + + + + Please provide a valid information + Input provided + +
+
+
+ +
+ + + + + Icon/Text Groups + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .00 + + + + +
+
+ + + + +
+ + + + + Button Groups + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ + + + + Dropdowns Groups + + +
+ + + + { this.setState({ first: !this.state.first }); }}> + + Dropdown + + + Action + Another Action + Something else here + + Separated link + + + + + + + + + + + { this.setState({ second: !this.state.second }); }}> + + Dropdown + + + Action + Another Action + Something else here + + Separated link + + + + + + + + + { this.setState({ third: !this.state.third }); }}> + Action + + Action + Another Action + Something else here + + Separated link + + + + { this.setState({ fourth: !this.state.fourth }); }}> + + Dropdown + + + Action + Another Action + Something else here + + Separated link + + + + + +
+
+ + + + +
+ +
+ + + + + Use the grid for big devices! + .col-lg-* .col-md-* .col-sm-* + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+ + + + + Input Grid for small devices! + .col-* + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+ +
+ + + + + Example Form + + +
+ + + + Username + + + + + + + + + + + Email + + + + + + + + + + + Password + + + + + + + + + + +
+
+
+ + + + + Example Form + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + Example Form + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + + + + + Form Elements +
+ + + +
+
+ + +
+ + +
+ + + @ + + + +

Here's some help text

+
+
+ + +
+ + + + .00 + + + Here's more help text +
+
+ + +
+ + + $ + + + + .00 + + +
+
+ + +
+ + + + + + +
+
+ + +
+ + + + + + + +
+
+
+ + +
+
+
+
+
+
+ +
+
+ ); + } +} + +export default Forms; diff --git a/src/views/Base/Forms/Forms.test.js b/src/views/Base/Forms/Forms.test.js new file mode 100644 index 0000000..c77a535 --- /dev/null +++ b/src/views/Base/Forms/Forms.test.js @@ -0,0 +1,39 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Forms from './Forms'; +import {mount} from 'enzyme/build'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); + +describe('toggle clicks', function() { + it('dropdowns without crashing', () => { + const wrapper = mount(); + for (let i = 0; i < 4; i++) { + let count = i === 0 ? 'first' : i === 1 ? 'second' : i === 2 ? 'third' : 'fourth' + let Dropdown = wrapper.find('button.dropdown-toggle').at(i); + Dropdown.simulate('click'); + expect(wrapper.state()[count]).toEqual(true); + } + wrapper.unmount() + }); + it('collapse without crashing', () => { + const wrapper = mount(); + let collapse = wrapper.find('button.btn-minimize').at(0); + collapse.simulate('click'); + expect(wrapper.state().collapse).toEqual(false); + collapse.simulate('click'); + expect(wrapper.state().collapse).toEqual(true); + wrapper.unmount() + }); + it('fade without crashing', () => { + const wrapper = mount(); + let fade = wrapper.find('button.btn-close').at(0); + fade.simulate('click'); + expect(wrapper.state().fadeIn).toEqual(false); + wrapper.unmount() + }); +}) diff --git a/src/views/Base/Forms/package.json b/src/views/Base/Forms/package.json new file mode 100644 index 0000000..6681319 --- /dev/null +++ b/src/views/Base/Forms/package.json @@ -0,0 +1,6 @@ +{ + "name": "Forms", + "version": "0.0.0", + "private": true, + "main": "./Forms.js" +} diff --git a/src/views/Base/Jumbotrons/Jumbotrons.js b/src/views/Base/Jumbotrons/Jumbotrons.js new file mode 100644 index 0000000..1941974 --- /dev/null +++ b/src/views/Base/Jumbotrons/Jumbotrons.js @@ -0,0 +1,56 @@ +import React, { Component } from 'react'; +import { Button, Card, CardBody, CardHeader, Col, Container, Jumbotron, Row } from 'reactstrap'; + +class Jumbotrons extends Component { + + render() { + return ( +
+ + + + + Jumbotron + + + + +

Hello, world!

+

This is a simple hero unit, a simple Jumbotron-style component for calling extra + attention to featured content or information.

+
+

It uses utility classes for typgraphy and spacing to space content out within the larger container.

+

+ +

+
+
+
+ + + + + Jumbotron + fluid + + + + +

Fluid jumbotron

+

This is a modified jumbotron that occupies the entire horizontal space of its parent.

+
+
+
+
+ +
+
+ ); + } +} + +export default Jumbotrons; diff --git a/src/views/Base/Jumbotrons/Jumbotrons.test.js b/src/views/Base/Jumbotrons/Jumbotrons.test.js new file mode 100644 index 0000000..8c00730 --- /dev/null +++ b/src/views/Base/Jumbotrons/Jumbotrons.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Jumbotrons from './Jumbotrons'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Jumbotrons/package.json b/src/views/Base/Jumbotrons/package.json new file mode 100644 index 0000000..ba7fb80 --- /dev/null +++ b/src/views/Base/Jumbotrons/package.json @@ -0,0 +1,6 @@ +{ + "name": "Jumbotrons", + "version": "0.0.0", + "private": true, + "main": "./Jumbotrons.js" +} diff --git a/src/views/Base/ListGroups/ListGroups.js b/src/views/Base/ListGroups/ListGroups.js new file mode 100644 index 0000000..c4fc57b --- /dev/null +++ b/src/views/Base/ListGroups/ListGroups.js @@ -0,0 +1,229 @@ +import React, { Component } from 'react'; +import { Badge, Card, CardBody, CardHeader, Col, ListGroup, ListGroupItem, ListGroupItemHeading, ListGroupItemText, Row, TabContent, TabPane } from 'reactstrap'; + +class ListGroups extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + activeTab: 1 + }; + } + + toggle(tab) { + if (this.state.activeTab !== tab) { + this.setState({ + activeTab: tab + }); + } + } + + render() { + return ( +
+ + + + + List Group + + + + + Cras justo odio + Dapibus ac facilisis in + Morbi leo risus + Porta ac consectetur ac + Vestibulum at eros + + + + + + + + List Group + tags + + + + Cras justo odio 14 + Dapibus ac facilisis in 2 + Morbi leo risus 1 + + + + + + + + + + List Group + disabled items + + + + Cras justo odio + Dapibus ac facilisis in + Morbi leo risus + Porta ac consectetur ac + Vestibulum at eros + + + + + + + + List Group + contextual classes + + + + Cras justo odio + Dapibus ac facilisis in + Morbi leo risus + Porta ac consectetur ac + + + + + + + + + + List Group + anchors + + +

Be sure to not use the standard .btn classes here.

+ + Cras justo odio + Dapibus ac facilisis in + Morbi leo risus + Porta ac consectetur ac + Vestibulum at eros + +

+ + + + + + + List Group + buttons + + + + Cras justo odio + Dapibus ac facilisis in + Morbi leo risus + Porta ac consectetur ac + Vestibulum at eros + + + + + + + + + + List Group + custom content + + + + + List group item heading + + Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit. + + + + List group item heading + + Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit. + + + + List group item heading + + Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit. + + + + + + + + + + + + List Group with TabPanes +

+ NEW +
+ + + + + + this.toggle(0)} action active={this.state.activeTab === 0} >Home + this.toggle(1)} action active={this.state.activeTab === 1} >Profile + this.toggle(2)} action active={this.state.activeTab === 2} >Messages + this.toggle(3)} action active={this.state.activeTab === 3} >Settings + + + + + +

Velit aute mollit ipsum ad dolor consectetur nulla officia culpa adipisicing exercitation fugiat tempor. Voluptate deserunt sit sunt + nisi aliqua fugiat proident ea ut. Mollit voluptate reprehenderit occaecat nisi ad non minim + tempor sunt voluptate consectetur exercitation id ut nulla. Ea et fugiat aliquip nostrud sunt incididunt consectetur culpa aliquip + eiusmod dolor. Anim ad Lorem aliqua in cupidatat nisi enim eu nostrud do aliquip veniam minim.

+
+ +

Cupidatat quis ad sint excepteur laborum in esse qui. Et excepteur consectetur ex nisi eu do cillum ad laborum. Mollit et eu officia + dolore sunt Lorem culpa qui commodo velit ex amet id ex. Officia anim incididunt laboris deserunt + anim aute dolor incididunt veniam aute dolore do exercitation. Dolor nisi culpa ex ad irure in elit eu dolore. Ad laboris ipsum + reprehenderit irure non commodo enim culpa commodo veniam incididunt veniam ad.

+
+ +

Ut ut do pariatur aliquip aliqua aliquip exercitation do nostrud commodo reprehenderit aute ipsum voluptate. Irure Lorem et laboris + nostrud amet cupidatat cupidatat anim do ut velit mollit consequat enim tempor. Consectetur + est minim nostrud nostrud consectetur irure labore voluptate irure. Ipsum id Lorem sit sint voluptate est pariatur eu ad cupidatat et + deserunt culpa sit eiusmod deserunt. Consectetur et fugiat anim do eiusmod aliquip nulla + laborum elit adipisicing pariatur cillum.

+
+ +

Irure enim occaecat labore sit qui aliquip reprehenderit amet velit. Deserunt ullamco ex elit nostrud ut dolore nisi officia magna + sit occaecat laboris sunt dolor. Nisi eu minim cillum occaecat aute est cupidatat aliqua labore + aute occaecat ea aliquip sunt amet. Aute mollit dolor ut exercitation irure commodo non amet consectetur quis amet culpa. Quis ullamco + nisi amet qui aute irure eu. Magna labore dolor quis ex labore id nostrud deserunt dolor + eiusmod eu pariatur culpa mollit in irure.

+
+
+ +
+
+
+ +
+
+ ); + } +} + +export default ListGroups; diff --git a/src/views/Base/ListGroups/ListGroups.test.js b/src/views/Base/ListGroups/ListGroups.test.js new file mode 100644 index 0000000..139eccc --- /dev/null +++ b/src/views/Base/ListGroups/ListGroups.test.js @@ -0,0 +1,19 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import ListGroups from './ListGroups'; +import {mount} from 'enzyme/build'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); +it('toggle click without crashing', () => { + const wrapper = mount(); + for (let i=0; i<4; i++) { + let ListGroup = wrapper.find('#list-tab .list-group-item-action.list-group-item').at(i); + ListGroup.simulate('click'); + expect(wrapper.state().activeTab).toEqual(i); + } + wrapper.unmount() +}); diff --git a/src/views/Base/ListGroups/package.json b/src/views/Base/ListGroups/package.json new file mode 100644 index 0000000..8c1e657 --- /dev/null +++ b/src/views/Base/ListGroups/package.json @@ -0,0 +1,6 @@ +{ + "name": "ListGroups", + "version": "0.0.0", + "private": true, + "main": "./ListGroups.js" +} diff --git a/src/views/Base/Navbars/Navbars.js b/src/views/Base/Navbars/Navbars.js new file mode 100644 index 0000000..5fc675b --- /dev/null +++ b/src/views/Base/Navbars/Navbars.js @@ -0,0 +1,118 @@ +import React, { Component } from 'react'; +import { + Card, + CardBody, + CardHeader, + Collapse, + DropdownItem, + DropdownMenu, + DropdownToggle, + Nav, + Navbar, + NavbarBrand, + NavbarToggler, + NavItem, + NavLink, + UncontrolledDropdown, +} from 'reactstrap'; + +class Navbars extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.toggleNavbar = this.toggleNavbar.bind(this); + this.state = { + isOpen: false, + collapsed: true, + }; + } + + toggle() { + this.setState({ + isOpen: !this.state.isOpen, + }); + } + + toggleNavbar() { + this.setState({ + collapsed: !this.state.collapsed, + }); + } + + render() { + return ( +
+ + + Navbar + + + + + Bootstrap + + + + + + + + + + Navbar Toggler + + + + Bootstrap + + + + + + + +
+ ); + } +} + +export default Navbars; \ No newline at end of file diff --git a/src/views/Base/Navbars/Navbars.test.js b/src/views/Base/Navbars/Navbars.test.js new file mode 100644 index 0000000..aa7aace --- /dev/null +++ b/src/views/Base/Navbars/Navbars.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Navbars from './Navbars'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Navbars/package.json b/src/views/Base/Navbars/package.json new file mode 100644 index 0000000..06907ae --- /dev/null +++ b/src/views/Base/Navbars/package.json @@ -0,0 +1,6 @@ +{ + "name": "Navbars", + "version": "0.0.0", + "private": true, + "main": "./Navbars.js" +} diff --git a/src/views/Base/Navs/Navs.js b/src/views/Base/Navs/Navs.js new file mode 100644 index 0000000..d132dbc --- /dev/null +++ b/src/views/Base/Navs/Navs.js @@ -0,0 +1,159 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Dropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, NavLink } from 'reactstrap'; + +class Navs extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + dropdownOpen: [false, false], + }; + } + + toggle(i) { + const newArray = this.state.dropdownOpen.map((element, index) => { + return (index === i ? !element : false); + }); + this.setState({ + dropdownOpen: newArray, + }); + } + + render() { + return ( +
+ + + Navs + + + +

List Based

+ +
+

Link Based

+ +
+
+ + + Navs Tabs + + + + + + + + Navs Pills + + + + + + + + Navs Vertical + + +

List Based

+ +
+

Link based

+ +
+
+
+ ); + } +} + +export default Navs; \ No newline at end of file diff --git a/src/views/Base/Navs/Navs.test.js b/src/views/Base/Navs/Navs.test.js new file mode 100644 index 0000000..380e873 --- /dev/null +++ b/src/views/Base/Navs/Navs.test.js @@ -0,0 +1,19 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Navs from './Navs'; +import {mount} from 'enzyme/build'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); +it('toggle click without crashing', () => { + const wrapper = mount(); + for (let i=0; i<2; i++) { + let Nav = wrapper.find('a.dropdown-toggle').at(i); + Nav.simulate('click'); + expect(wrapper.state().dropdownOpen[i]).toEqual(true); + } + wrapper.unmount() +}); diff --git a/src/views/Base/Navs/package.json b/src/views/Base/Navs/package.json new file mode 100644 index 0000000..5f7753d --- /dev/null +++ b/src/views/Base/Navs/package.json @@ -0,0 +1,6 @@ +{ + "name": "Navs", + "version": "0.0.0", + "private": true, + "main": "./Navs.js" +} diff --git a/src/views/Base/Paginations/Paginations.test.js b/src/views/Base/Paginations/Paginations.test.js new file mode 100644 index 0000000..02048b6 --- /dev/null +++ b/src/views/Base/Paginations/Paginations.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Paginations from './Pagnations'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Paginations/Pagnations.js b/src/views/Base/Paginations/Pagnations.js new file mode 100644 index 0000000..2489c9e --- /dev/null +++ b/src/views/Base/Paginations/Pagnations.js @@ -0,0 +1,177 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Pagination, PaginationItem, PaginationLink } from 'reactstrap'; + +class Paginations extends Component { + + render() { + return ( +
+ + + Pagination + + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + + + + + + + Pagination + disabled and active states + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + + + + + + + Pagination + sizing + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + + + + +
+ ); + } +} + +export default Paginations; diff --git a/src/views/Base/Paginations/package.json b/src/views/Base/Paginations/package.json new file mode 100644 index 0000000..1e89ea2 --- /dev/null +++ b/src/views/Base/Paginations/package.json @@ -0,0 +1,6 @@ +{ + "name": "Pagnations", + "version": "0.0.0", + "private": true, + "main": "./Pagnations.js" +} diff --git a/src/views/Base/Popovers/Popovers.js b/src/views/Base/Popovers/Popovers.js new file mode 100644 index 0000000..44748a2 --- /dev/null +++ b/src/views/Base/Popovers/Popovers.js @@ -0,0 +1,108 @@ +import React, { Component } from 'react'; +import { Button, Card, CardBody, CardHeader, Popover, PopoverBody, PopoverHeader } from 'reactstrap'; + +class PopoverItem extends Component { + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + popoverOpen: false, + }; + } + + toggle() { + this.setState({ + popoverOpen: !this.state.popoverOpen, + }); + } + + render() { + return ( + + + + Popover Title + Sed posuere consectetur est at lobortis. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. + + + ); + } +} + +class Popovers extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + popoverOpen: false, + popovers: [ + { + placement: 'top', + text: 'Top', + }, + { + placement: 'bottom', + text: 'Bottom', + }, + { + placement: 'left', + text: 'Left', + }, + { + placement: 'right', + text: 'Right', + }, + ], + }; + } + + toggle() { + this.setState({ + popoverOpen: !this.state.popoverOpen, + }); + } + + render() { + return ( +
+ + + Popovers + + + + + + Popover Title + Sed posuere consectetur est at lobortis. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. + + + + + + Popovers + list + + + {this.state.popovers.map((popover, i) => { + return ; + })} + + +
+ ); + } +} + +export default Popovers; diff --git a/src/views/Base/Popovers/Popovers.test.js b/src/views/Base/Popovers/Popovers.test.js new file mode 100644 index 0000000..ec191a7 --- /dev/null +++ b/src/views/Base/Popovers/Popovers.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Popovers from './Popovers'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + document.body.appendChild(div); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Popovers/package.json b/src/views/Base/Popovers/package.json new file mode 100644 index 0000000..37a3e47 --- /dev/null +++ b/src/views/Base/Popovers/package.json @@ -0,0 +1,6 @@ +{ + "name": "Popovers", + "version": "0.0.0", + "private": true, + "main": "./Popovers.js" +} diff --git a/src/views/Base/ProgressBar/ProgressBar.js b/src/views/Base/ProgressBar/ProgressBar.js new file mode 100644 index 0000000..a234b1d --- /dev/null +++ b/src/views/Base/ProgressBar/ProgressBar.js @@ -0,0 +1,167 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Progress } from 'reactstrap'; + +class ProgressBar extends Component { + + render() { + return ( +
+ + + Progress + + + +
0%
+ +
25%
+ +
50%
+ +
75%
+ +
100%
+ +
Multiple bars
+ + + + + + + +
+
+ + + Progress + color variants + + + + + + + + + + + + Progress + labels + + + 25% + 1/2 + You're almost there! + You did it! + + Meh + Wow! + Cool + 20% + !! + + + + + + Progress + striped + + + + + + + + + + + + + + + + + + Progress + animated + + + + + + + + + + + + + + + + + + Progress + multiple bars / stacked + + +
Plain
+ + + + + + + +
With Labels
+ + Meh + Wow! + 25% + LOOK OUT!! + +
Stripes and Animations
+ + Stripes + Animated Stripes + Plain + +
+
+ + + Progress + max value + + +
1 of 5
+ +
50 of 135
+ +
75 of 111
+ +
463 of 500
+ + +
Various (40) of 55
+ + 5 + 15 + 10 + 10 + +
+
+
+ ); + } +} + +export default ProgressBar; \ No newline at end of file diff --git a/src/views/Base/ProgressBar/ProgressBar.test.js b/src/views/Base/ProgressBar/ProgressBar.test.js new file mode 100644 index 0000000..1dffea7 --- /dev/null +++ b/src/views/Base/ProgressBar/ProgressBar.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import ProgressBar from './ProgressBar'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/ProgressBar/package.json b/src/views/Base/ProgressBar/package.json new file mode 100644 index 0000000..759527b --- /dev/null +++ b/src/views/Base/ProgressBar/package.json @@ -0,0 +1,6 @@ +{ + "name": "Progress Bar", + "version": "0.0.0", + "private": true, + "main": "./ProgressBar.js" +} diff --git a/src/views/Base/Switches/Switches.js b/src/views/Base/Switches/Switches.js new file mode 100644 index 0000000..730e94e --- /dev/null +++ b/src/views/Base/Switches/Switches.js @@ -0,0 +1,494 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table } from 'reactstrap'; +import { AppSwitch } from '@coreui/react' + +class Switches extends Component { + render() { + return ( +
+ + + + + + Switch default + + + + + + + + + + + + + + + + + + Switch pills + + + + + + + + + + + + + + + + + + 3d Switch + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + 3d Switch disabled + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + + 3d Switch outline="alt" + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + 3d Switch label + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + 3d Switch outline="alt" label + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + 3d Switch outline="alt" label + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch outline + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch outline pills + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch outline alternative + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + + Switch outline alternative - pills + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + + Switch with text + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch with text pills + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch with text outline + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch with text outline pills + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch with text outline alternative pills + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch with text outline alternative pills + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + + Switch with text outline alternative + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch with text outline alternative pills + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + + Switch with text outline alternative + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + Switch with text outline alternative pills + {' '}CoreUI Pro + + + + + + + + + + + + + + + + + + + Sizes + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SizeExampleProps
+ Large + + + + Add size={'lg'} +
+ Normal + + + + - +
+ Small + + + + Add size={'sm'} +
+
+
+ + +
+
+ + ); + } +} + +export default Switches; diff --git a/src/views/Base/Switches/Switches.test.js b/src/views/Base/Switches/Switches.test.js new file mode 100644 index 0000000..e48f76a --- /dev/null +++ b/src/views/Base/Switches/Switches.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Switches from './Switches'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Switches/package.json b/src/views/Base/Switches/package.json new file mode 100644 index 0000000..fa1799a --- /dev/null +++ b/src/views/Base/Switches/package.json @@ -0,0 +1,6 @@ +{ + "name": "Switches", + "version": "0.0.0", + "private": true, + "main": "./Switches.js" +} diff --git a/src/views/Base/Tables/Tables.js b/src/views/Base/Tables/Tables.js new file mode 100644 index 0000000..eae3ef1 --- /dev/null +++ b/src/views/Base/Tables/Tables.js @@ -0,0 +1,393 @@ +import React, { Component } from 'react'; +import { Badge, Card, CardBody, CardHeader, Col, Pagination, PaginationItem, PaginationLink, Row, Table } from 'reactstrap'; + +class Tables extends Component { + render() { + return ( +
+ + + + + Simple Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UsernameDate registeredRoleStatus
Samppa Nori2012/01/01Member + Active +
Estavan Lykos2012/02/01Staff + Banned +
Chetan Mohamed2012/02/01Admin + Inactive +
Derick Maximinus2012/03/01Member + Pending +
Friderik Dávid2012/01/21Staff + Active +
+ + + + + + 1 + + + 2 + + + 3 + + + 4 + + + + + +
+
+ + + + + + Striped Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UsernameDate registeredRoleStatus
Yiorgos Avraamu2012/01/01Member + Active +
Avram Tarasios2012/02/01Staff + Banned +
Quintin Ed2012/02/01Admin + Inactive +
Enéas Kwadwo2012/03/01Member + Pending +
Agapetus Tadeáš2012/01/21Staff + Active +
+ + Prev + + 1 + + 2 + 3 + 4 + Next + +
+
+ +
+ + + + + + + Condensed Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UsernameDate registeredRoleStatus
Carwyn Fachtna2012/01/01Member + Active +
Nehemiah Tatius2012/02/01Staff + Banned +
Ebbe Gemariah2012/02/01Admin + Inactive +
Eustorgios Amulius2012/03/01Member + Pending +
Leopold Gáspár2012/01/21Staff + Active +
+ + Prev + + 1 + + 2 + 3 + 4 + Next + +
+
+ + + + + + Bordered Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UsernameDate registeredRoleStatus
Pompeius René2012/01/01Member + Active +
Paĉjo Jadon2012/02/01Staff + Banned +
Micheal Mercurius2012/02/01Admin + Inactive +
Ganesha Dubhghall2012/03/01Member + Pending +
Hiroto Šimun2012/01/21Staff + Active +
+ + Prev + + 1 + + 2 + 3 + 4 + Next + +
+
+ + +
+ + + + + + Combined All Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UsernameDate registeredRoleStatus
Vishnu Serghei2012/01/01Member + Active +
Zbyněk Phoibos2012/02/01Staff + Banned +
Einar Randall2012/02/01Admin + Inactive +
Félix Troels2012/03/01Member + Pending +
Aulus Agmundr2012/01/21Staff + Active +
+ +
+
+ +
+
+ + ); + } +} + +export default Tables; diff --git a/src/views/Base/Tables/Tables.test.js b/src/views/Base/Tables/Tables.test.js new file mode 100644 index 0000000..602391b --- /dev/null +++ b/src/views/Base/Tables/Tables.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Tables from './Tables'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Tables/package.json b/src/views/Base/Tables/package.json new file mode 100644 index 0000000..b6ff309 --- /dev/null +++ b/src/views/Base/Tables/package.json @@ -0,0 +1,6 @@ +{ + "name": "Tables", + "version": "0.0.0", + "private": true, + "main": "./Tables.js" +} diff --git a/src/views/Base/Tabs/Tabs.js b/src/views/Base/Tabs/Tabs.js new file mode 100644 index 0000000..d99806a --- /dev/null +++ b/src/views/Base/Tabs/Tabs.js @@ -0,0 +1,183 @@ +import React, {Component} from 'react'; +import {Badge, Col, Nav, NavItem, NavLink, Row, TabContent, TabPane} from 'reactstrap'; +import classnames from 'classnames'; + +class Tabs extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + activeTab: new Array(4).fill('1'), + }; + } + + lorem() { + return 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit.' + } + + toggle(tabPane, tab) { + const newArray = this.state.activeTab.slice() + newArray[tabPane] = tab + this.setState({ + activeTab: newArray, + }); + } + + tabPane() { + return ( + <> + + {`1. ${this.lorem()}`} + + + {`2. ${this.lorem()}`} + + + {`3. ${this.lorem()}`} + + + ); + } + + render() { + return ( +
+ + + + + {this.tabPane()} + + + + + + {this.tabPane()} + + + + + + {this.tabPane()} + + + + + + {this.tabPane()} + + + +
+ ); + } +} + +export default Tabs; diff --git a/src/views/Base/Tabs/Tabs.test.js b/src/views/Base/Tabs/Tabs.test.js new file mode 100644 index 0000000..1293edd --- /dev/null +++ b/src/views/Base/Tabs/Tabs.test.js @@ -0,0 +1,21 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Tabs from './Tabs'; +import {mount} from 'enzyme/build'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); +it('toggle click without crashing', () => { + const wrapper = mount(); + for (let pane=0; pane<4; pane++) { + for( let tabId=1; tabId<4; tabId++) { + let Tab = wrapper.find('.nav-tabs .nav-item .nav-link').at((3*pane)+tabId-1); + Tab.simulate('click'); + expect(wrapper.state().activeTab[pane]).toEqual((tabId).toString()); + } + } + wrapper.unmount() +}); diff --git a/src/views/Base/Tabs/package.json b/src/views/Base/Tabs/package.json new file mode 100644 index 0000000..162e63b --- /dev/null +++ b/src/views/Base/Tabs/package.json @@ -0,0 +1,6 @@ +{ + "name": "Tabs", + "version": "0.0.0", + "private": true, + "main": "./Tabs.js" +} diff --git a/src/views/Base/Tooltips/Tooltips.js b/src/views/Base/Tooltips/Tooltips.js new file mode 100644 index 0000000..64be32c --- /dev/null +++ b/src/views/Base/Tooltips/Tooltips.js @@ -0,0 +1,134 @@ +import React, { Component } from 'react'; +import { Button, Card, CardBody, CardHeader, Tooltip, UncontrolledTooltip } from 'reactstrap'; + +class TooltipItem extends React.Component { + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + tooltipOpen: false, + }; + } + + toggle() { + this.setState({ + tooltipOpen: !this.state.tooltipOpen, + }); + } + + render() { + return ( + + + + Tooltip Content! + + + ); + } +} + +class Tooltips extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + tooltipOpen: [false, false], + tooltips: [ + { + placement: 'top', + text: 'Top', + }, + { + placement: 'bottom', + text: 'Bottom', + }, + { + placement: 'left', + text: 'Left', + }, + { + placement: 'right', + text: 'Right', + }, + ], + }; + } + + toggle(i) { + const newArray = this.state.tooltipOpen.map((element, index) => { + return (index === i ? !element : false); + }); + this.setState({ + tooltipOpen: newArray, + }); + } + + render() { + return ( +
+ + + Tooltips + + + + {/*eslint-disable-next-line*/} +

Somewhere in here is a tooltip.

+ {this.toggle(0);}}> + Hello world! + +
+
+ + + Tooltip + disable autohide + + + {/*eslint-disable-next-line*/} +

Sometimes you need to allow users to select text within a tooltip.

+ {this.toggle(1);}}> + Try to select this text! + +
+
+ + + Tooltip + list + + + {this.state.tooltips.map((tooltip, i) => { + return ; + })} + + + + + Tooltip + uncontrolled + + + {/*eslint-disable-next-line*/} +

Somewhere in here is a tooltip.

+ + Hello world! + +
+
+
+ ); + } +} + +export default Tooltips; diff --git a/src/views/Base/Tooltips/Tooltips.test.js b/src/views/Base/Tooltips/Tooltips.test.js new file mode 100644 index 0000000..cdbca71 --- /dev/null +++ b/src/views/Base/Tooltips/Tooltips.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Tooltips from './Tooltips'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + document.body.appendChild(div); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Base/Tooltips/package.json b/src/views/Base/Tooltips/package.json new file mode 100644 index 0000000..61be3db --- /dev/null +++ b/src/views/Base/Tooltips/package.json @@ -0,0 +1,6 @@ +{ + "name": "Tooltips", + "version": "0.0.0", + "private": true, + "main": "./Tooltips.js" +} diff --git a/src/views/Base/index.js b/src/views/Base/index.js new file mode 100644 index 0000000..a50ada9 --- /dev/null +++ b/src/views/Base/index.js @@ -0,0 +1,22 @@ +import Breadcrumbs from './Breadcrumbs'; +import Cards from './Cards'; +import Carousels from './Carousels'; +import Collapses from './Collapses'; +import Dropdowns from './Dropdowns'; +import Forms from './Forms'; +import Jumbotrons from './Jumbotrons'; +import ListGroups from './ListGroups'; +import Navbars from './Navbars'; +import Navs from './Navs'; +import Popovers from './Popovers'; +import Paginations from './Paginations'; +import ProgressBar from './ProgressBar'; +import Switches from './Switches'; +import Tables from './Tables'; +import Tabs from './Tabs'; +import Tooltips from './Tooltips'; + +export { + Breadcrumbs, Cards, Carousels, Collapses, Dropdowns, Forms, Jumbotrons, ListGroups, Navbars, Navs, Popovers, ProgressBar, Switches, Tables, Tabs, Tooltips, Paginations +}; + diff --git a/src/views/BaseLayers/BaseLayers.css b/src/views/BaseLayers/BaseLayers.css new file mode 100644 index 0000000..e69de29 diff --git a/src/views/BaseLayers/BaseLayers.js b/src/views/BaseLayers/BaseLayers.js new file mode 100644 index 0000000..4ded96c --- /dev/null +++ b/src/views/BaseLayers/BaseLayers.js @@ -0,0 +1,40 @@ +import React, { Component } from 'react' +// import './BaseLayers.css' +import DataTable from '../../components/DataTable' + +const data = [ + { + "id": 0, + "name": "Item name 0", + "price": 2100 + }, + { + "id": 1, + "name": "Item name 1", + "price": 2101 + }, + { + "id": 2, + "name": "Item name 2", + "price": 2102 + }, + { + "id": 3, + "name": "Item name 3", + "price": 2103 + }]; + +class BaseLayers extends Component { + render() { + return ( +
+ +
+ ) + } +} + +export default BaseLayers; \ No newline at end of file diff --git a/src/views/BaseLayers/package.json b/src/views/BaseLayers/package.json new file mode 100644 index 0000000..3ba7d28 --- /dev/null +++ b/src/views/BaseLayers/package.json @@ -0,0 +1,6 @@ +{ + "name": "BaseLayers", + "version": "0.0.0", + "private": true, + "main": "./BaseLayers.js" +} diff --git a/src/views/Buttons/BrandButtons/BrandButtons.js b/src/views/Buttons/BrandButtons/BrandButtons.js new file mode 100644 index 0000000..c8f60c2 --- /dev/null +++ b/src/views/Buttons/BrandButtons/BrandButtons.js @@ -0,0 +1,324 @@ +import React, { Component } from 'react'; +import { Button, Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; + +class BrandButtons extends Component { + render() { + return ( +
+ + + + + + Brand Button + Usage ex. + + <Button className="btn-facebook btn-brand"><i className="fa fa-facebook"></i><span>Facebook</span></Button> + + + +
Size Small + Add this class .btn-sm +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
Size Normal
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
Size Large + Add this class .btn-lg +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
+
+ + + + + + + Brand Button + Icons only. Usage ex. + + <Button className="btn-facebook btn-brand icon"><i className="fa fa-facebook"></i></Button> + + + +
Size Small + Add this class .btn-sm +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
Size Normal
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
Size Large + Add this class .btn-lg +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
+
+ + + + + + + Brand Button + Text only. Usage ex. + + <Button className="btn-facebook btn-brand text"><span>Facebook</span></Button> + + + +
Size Small + Add this class .btn-sm +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
Size Normal
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
Size Large + Add this class .btn-lg +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
+
+ +
+
+ + ); + } +} + +export default BrandButtons; diff --git a/src/views/Buttons/BrandButtons/BrandButtons.test.js b/src/views/Buttons/BrandButtons/BrandButtons.test.js new file mode 100644 index 0000000..24c7818 --- /dev/null +++ b/src/views/Buttons/BrandButtons/BrandButtons.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import BrandButtons from './'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Buttons/BrandButtons/package.json b/src/views/Buttons/BrandButtons/package.json new file mode 100644 index 0000000..cc1e6a0 --- /dev/null +++ b/src/views/Buttons/BrandButtons/package.json @@ -0,0 +1,6 @@ +{ + "name": "BrandButtons", + "version": "0.0.0", + "private": true, + "main": "./BrandButtons.js" +} diff --git a/src/views/Buttons/ButtonDropdowns/ButtonDropdowns.js b/src/views/Buttons/ButtonDropdowns/ButtonDropdowns.js new file mode 100644 index 0000000..4a00128 --- /dev/null +++ b/src/views/Buttons/ButtonDropdowns/ButtonDropdowns.js @@ -0,0 +1,290 @@ +import React, { Component } from 'react'; +import { Button, ButtonDropdown, Card, CardBody, CardHeader, Col, DropdownItem, DropdownMenu, DropdownToggle, Row } from 'reactstrap'; + +class ButtonDropdowns extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + dropdownOpen: new Array(19).fill(false), + }; + } + + toggle(i) { + const newArray = this.state.dropdownOpen.map((element, index) => { return (index === i ? !element : false); }); + this.setState({ + dropdownOpen: newArray, + }); + } + + render() { + return ( +
+ + + + + Button Dropdown + + + + { this.toggle(0); }}> + + Button Dropdown + + + Header + Action Disabled + Action + + Another Action + + + + + + + Single button dropdowns + + + { this.toggle(1); }}> + + Primary + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(2); }}> + + Secondary + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(3); }}> + + Success + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(4); }}> + + Info + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(5); }}> + + Warning + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(6); }}> + + Danger + + + Header + Action Disabled + Action + + Another Action + + + + + + + Split button dropdowns + + + { this.toggle(7); }}> + + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(8); }}> + + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(9); }}> + + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(10); }}> + + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(11); }}> + + + + Header + Action Disabled + Action + + Another Action + + + { this.toggle(12); }}> + + + + Header + Action Disabled + Action + + Another Action + + + + + + + Dropdown directions + + + { this.toggle(13); }}> + + Direction Up + + + Header + Action Disabled + Action + Another Action + + + { this.toggle(14); }}> + + Direction Left + + + Header + Action Disabled + Action + Another Action + + + { this.toggle(15); }}> + + Direction Right + + + Header + Action Disabled + Action + Another Action + + + { this.toggle(16); }}> + + Default Down + + + Header + Action Disabled + Action + Another Action + + + + + + + Button Dropdown sizing + + + { this.toggle(17); }}> + + Large Button + + + Header + Action Disabled + Action + Another Action + + + { this.toggle(18); }}> + + Small Button + + + Header + Action Disabled + Action + Another Action + + + + + + +
+ ); + } +} + +export default ButtonDropdowns; diff --git a/src/views/Buttons/ButtonDropdowns/ButtonDropdowns.test.js b/src/views/Buttons/ButtonDropdowns/ButtonDropdowns.test.js new file mode 100644 index 0000000..62f8f37 --- /dev/null +++ b/src/views/Buttons/ButtonDropdowns/ButtonDropdowns.test.js @@ -0,0 +1,20 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { mount } from 'enzyme' +import ButtonDropdowns from './ButtonDropdowns'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); +it('toggle click without crashing', () => { + const wrapper = mount(); + for (let i=0; i<19; i++) { + let ButtonDropdown = wrapper.find('button.dropdown-toggle').at(i); + ButtonDropdown.simulate('click'); + expect(wrapper.state().dropdownOpen[i]).toEqual(true); + } + wrapper.unmount() +}); + diff --git a/src/views/Buttons/ButtonDropdowns/package.json b/src/views/Buttons/ButtonDropdowns/package.json new file mode 100644 index 0000000..5bd5b4c --- /dev/null +++ b/src/views/Buttons/ButtonDropdowns/package.json @@ -0,0 +1,6 @@ +{ + "name": "ButtonDropdowns", + "version": "0.0.0", + "private": true, + "main": "./ButtonDropdowns.js" +} diff --git a/src/views/Buttons/ButtonGroups/ButtonGroups.js b/src/views/Buttons/ButtonGroups/ButtonGroups.js new file mode 100644 index 0000000..8663a67 --- /dev/null +++ b/src/views/Buttons/ButtonGroups/ButtonGroups.js @@ -0,0 +1,192 @@ +import React, { Component } from 'react'; +import { + Button, + ButtonDropdown, + ButtonGroup, + ButtonToolbar, + Card, + CardBody, + CardHeader, + Col, + DropdownItem, + DropdownMenu, + DropdownToggle, + Input, + InputGroup, + InputGroupAddon, + InputGroupText, + Row, +} from 'reactstrap'; + +class ButtonGroups extends Component { + + constructor(props) { + super(props); + + this.toggle = this.toggle.bind(this); + this.state = { + dropdownOpen: new Array(2).fill(false), + }; + } + + toggle(i) { + const newArray = this.state.dropdownOpen.map((element, index) => { return (index === i ? !element : false); }); + this.setState({ + dropdownOpen: newArray, + }); + } + + render() { + return ( +
+ + + + + Button Group + + + + + + + + + + + + + Vertical variation + + + + + + { this.toggle(0); }}> + + Dropdown + + + Dropdown Link + Dropdown Link + + + + + + + + Button Toolbar + + + + + + + + + + + + + + + + + + + + + + + + + Sizing + + + + + + + +
+ + + + + +
+ + + + + +
+
+ + + Nesting + + + + + + { this.toggle(1); }}> + + Dropdown + + + Dropdown Link + Dropdown Link + + + + + + +
+ + + + + Button Toolbar with input groups + + + + + + + + + + + @ + + + + + + + + + + + + @ + + + + + + + +
+ ); + } +} + +export default ButtonGroups; diff --git a/src/views/Buttons/ButtonGroups/ButtonGroups.test.js b/src/views/Buttons/ButtonGroups/ButtonGroups.test.js new file mode 100644 index 0000000..318de31 --- /dev/null +++ b/src/views/Buttons/ButtonGroups/ButtonGroups.test.js @@ -0,0 +1,19 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import ButtonGroups from './ButtonGroups'; +import {mount} from 'enzyme/build'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); +it('toggle click without crashing', () => { + const wrapper = mount(); + for (let i=0; i<2; i++) { + let ButtonGroup = wrapper.find('button.dropdown-toggle').at(i); + ButtonGroup.simulate('click'); + expect(wrapper.state().dropdownOpen[i]).toEqual(true); + } + wrapper.unmount() +}); diff --git a/src/views/Buttons/ButtonGroups/package.json b/src/views/Buttons/ButtonGroups/package.json new file mode 100644 index 0000000..0a7eb82 --- /dev/null +++ b/src/views/Buttons/ButtonGroups/package.json @@ -0,0 +1,6 @@ +{ + "name": "ButtonGroups", + "version": "0.0.0", + "private": true, + "main": "./ButtonGroups.js" +} diff --git a/src/views/Buttons/Buttons/Buttons.js b/src/views/Buttons/Buttons/Buttons.js new file mode 100644 index 0000000..40eb012 --- /dev/null +++ b/src/views/Buttons/Buttons/Buttons.js @@ -0,0 +1,669 @@ +import React, { Component } from 'react'; +import { Button, Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; + +class Buttons extends Component { + render() { + return ( +
+ + + Standard Buttons + + + + + Normal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Active State + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Outline Buttons + + +

+ Use outline prop +

+ + + Normal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Active State + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + Ghost Buttons + + +

+ Use + .btn-ghost-* class for ghost buttons. +

+ + + Normal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Active State + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + Square Buttons + + +

+ Use + .btn-square class for square buttons. +

+ + + Normal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Active State + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + Pill Buttons + + +

+ Use + .btn-pill class for pill buttons. +

+ + + Normal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Active State + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + Sizes + + +

Fancy larger or smaller buttons? Add size="lg" or size="sm" for additional sizes.

+ + + Small + + + + + + + + + + + + + + + + + + + + Normal + + + + + + + + + + + + + + + + + + + + Large + + + + + + + + + + + + + + + + + +
+
+ + + With Icons + + + + + + + + + + + + + + + + + + + + + + + + + + Block Level Buttons + + +

Add prop block

+ + + + + + + +
+
+ + + + + Block Level Buttons + + +

Add prop block

+ + + + + + + +
+
+ +
+
+ ); + } +} + +export default Buttons; diff --git a/src/views/Buttons/Buttons/Buttons.test.js b/src/views/Buttons/Buttons/Buttons.test.js new file mode 100644 index 0000000..9c7a2e2 --- /dev/null +++ b/src/views/Buttons/Buttons/Buttons.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Buttons from './Buttons'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Buttons/Buttons/package.json b/src/views/Buttons/Buttons/package.json new file mode 100644 index 0000000..f061146 --- /dev/null +++ b/src/views/Buttons/Buttons/package.json @@ -0,0 +1,6 @@ +{ + "name": "Buttons", + "version": "0.0.0", + "private": true, + "main": "./Buttons.js" +} diff --git a/src/views/Buttons/index.js b/src/views/Buttons/index.js new file mode 100644 index 0000000..7945b6f --- /dev/null +++ b/src/views/Buttons/index.js @@ -0,0 +1,8 @@ +import ButtonDropdowns from './ButtonDropdowns'; +import ButtonGroups from './ButtonGroups'; +import Buttons from './Buttons'; +import BrandButtons from './BrandButtons'; + +export { + ButtonDropdowns, ButtonGroups, Buttons, BrandButtons +} diff --git a/src/views/Charts/Charts.js b/src/views/Charts/Charts.js new file mode 100644 index 0000000..765a44f --- /dev/null +++ b/src/views/Charts/Charts.js @@ -0,0 +1,255 @@ +import React, { Component } from 'react'; +import { Bar, Doughnut, Line, Pie, Polar, Radar } from 'react-chartjs-2'; +import { Card, CardBody, CardColumns, CardHeader } from 'reactstrap'; +import { CustomTooltips } from '@coreui/coreui-plugin-chartjs-custom-tooltips'; + +const line = { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], + datasets: [ + { + label: 'My First dataset', + fill: false, + lineTension: 0.1, + backgroundColor: 'rgba(75,192,192,0.4)', + borderColor: 'rgba(75,192,192,1)', + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0.0, + borderJoinStyle: 'miter', + pointBorderColor: 'rgba(75,192,192,1)', + pointBackgroundColor: '#fff', + pointBorderWidth: 1, + pointHoverRadius: 5, + pointHoverBackgroundColor: 'rgba(75,192,192,1)', + pointHoverBorderColor: 'rgba(220,220,220,1)', + pointHoverBorderWidth: 2, + pointRadius: 1, + pointHitRadius: 10, + data: [65, 59, 80, 81, 56, 55, 40], + }, + ], +}; + +const bar = { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], + datasets: [ + { + label: 'My First dataset', + backgroundColor: 'rgba(255,99,132,0.2)', + borderColor: 'rgba(255,99,132,1)', + borderWidth: 1, + hoverBackgroundColor: 'rgba(255,99,132,0.4)', + hoverBorderColor: 'rgba(255,99,132,1)', + data: [65, 59, 80, 81, 56, 55, 40], + }, + ], +}; + +const doughnut = { + labels: [ + 'Red', + 'Green', + 'Yellow', + ], + datasets: [ + { + data: [300, 50, 100], + backgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56', + ], + hoverBackgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56', + ], + }], +}; + +const radar = { + labels: ['Eating', 'Drinking', 'Sleeping', 'Designing', 'Coding', 'Cycling', 'Running'], + datasets: [ + { + label: 'My First dataset', + backgroundColor: 'rgba(179,181,198,0.2)', + borderColor: 'rgba(179,181,198,1)', + pointBackgroundColor: 'rgba(179,181,198,1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(179,181,198,1)', + data: [65, 59, 90, 81, 56, 55, 40], + }, + { + label: 'My Second dataset', + backgroundColor: 'rgba(255,99,132,0.2)', + borderColor: 'rgba(255,99,132,1)', + pointBackgroundColor: 'rgba(255,99,132,1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(255,99,132,1)', + data: [28, 48, 40, 19, 96, 27, 100], + }, + ], +}; + +const pie = { + labels: [ + 'Red', + 'Green', + 'Yellow', + ], + datasets: [ + { + data: [300, 50, 100], + backgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56', + ], + hoverBackgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56', + ], + }], +}; + +const polar = { + datasets: [ + { + data: [ + 11, + 16, + 7, + 3, + 14, + ], + backgroundColor: [ + '#FF6384', + '#4BC0C0', + '#FFCE56', + '#E7E9ED', + '#36A2EB', + ], + label: 'My dataset' // for legend + }], + labels: [ + 'Red', + 'Green', + 'Yellow', + 'Grey', + 'Blue', + ], +}; + +const options = { + tooltips: { + enabled: false, + custom: CustomTooltips + }, + maintainAspectRatio: false +} + +class Charts extends Component { + render() { + return ( +
+ + + + Line Chart + + + +
+ +
+
+
+ + + Bar Chart + + + +
+ +
+
+
+ + + Doughnut Chart + + + +
+ +
+
+
+ + + Radar Chart + + + +
+ +
+
+
+ + + Pie Chart + + + +
+ +
+
+
+ + + Polar Area Chart + + + +
+ +
+
+
+
+
+ ); + } +} + +export default Charts; diff --git a/src/views/Charts/Charts.test.js b/src/views/Charts/Charts.test.js new file mode 100644 index 0000000..6f3cd70 --- /dev/null +++ b/src/views/Charts/Charts.test.js @@ -0,0 +1,18 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Charts from './Charts'; + +jest.mock('react-chartjs-2', () => ({ + Line: () => null, + Polar: () => null, + Pie: () => null, + Radar: () => null, + Bar: () => null, + Doughnut: () => null, +})); + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); \ No newline at end of file diff --git a/src/views/Charts/package.json b/src/views/Charts/package.json new file mode 100644 index 0000000..da44c70 --- /dev/null +++ b/src/views/Charts/package.json @@ -0,0 +1,6 @@ +{ + "name": "Charts", + "version": "0.0.0", + "private": true, + "main": "./Charts.js" +} diff --git a/src/views/ControlMonitoringGantt/Gantt.css b/src/views/ControlMonitoringGantt/Gantt.css new file mode 100644 index 0000000..1d7f3bf --- /dev/null +++ b/src/views/ControlMonitoringGantt/Gantt.css @@ -0,0 +1,3 @@ +.gantt_grid_scale .gantt_grid_head_cell{color:#000000;} +.gantt_task .gantt_task_scale .gantt_scale_cell{color:#000000;} +.gantt_grid_scale,.gantt_task_scale{color:#000000;} \ No newline at end of file diff --git a/src/views/ControlMonitoringGantt/GanttDhtmlx2.js b/src/views/ControlMonitoringGantt/GanttDhtmlx2.js new file mode 100644 index 0000000..b7bc285 --- /dev/null +++ b/src/views/ControlMonitoringGantt/GanttDhtmlx2.js @@ -0,0 +1,270 @@ +/*global gantt*/ +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; +import moment from 'moment'; +import axios from 'axios'; +import { Pagination, Table, Button, Tooltip } from 'antd'; +import 'dhtmlx-gantt'; +import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'; +import './Gantt.css'; + +let startDate = ""; +let endDate = ""; +let dataGantt = { data: [], links: [] }; +let idLoop = 1 + +export default class GanttFull extends Component { + + componentDidMount() { + + gantt.config.scale_unit = "day"; + gantt.config.step = 1; + gantt.config.date_scale = "%j"; + + gantt.config.scale_height = 75; + gantt.config.min_column_width = 30; + gantt.config.subscales = [{ unit: "month", step: 1, date: "%F, %Y" }]; + // gantt.config.start_date = new Date(this.props.config.startDate); + // gantt.config.end_date = new Date(this.props.config.endDate); + gantt.config.progress = false; + gantt.config.readonly = true; + gantt.config.drag_resize = true; + gantt.config.drag_links = false; + gantt.config.drag_progress = false; + gantt.config.select_task = false; + gantt.config.add_column = false; + gantt.config.grid_resize = true; + gantt.config.columns = [ + { name: "text", label: "Proyek", tree: true, width: 200 }, + // { + // name: "duration", align: "center", onrender: function (task, node) { + // node.setAttribute("title", task.text); + // } + // }, + { + name: "staff", align: "center", label: "Duration", template: function (obj) { + // console.log(obj) + return `${obj.duration} day` + } + }, + // { + // name: "external", + // label: "Add", + // align: "center", + // onrender: (task, node) => { + // return + // + // + // } + // } + ]; + + gantt.config.external_render = { + // checks the element is a React element + isElement: (element) => { + return React.isValidElement(element); + }, + // renders the React element into the DOM + renderElement: (element, container) => { + ReactDOM.render(element, container); + } + }; + // gantt.config.auto_scheduling = true; + // gantt.attachEvent("onTaskClick", function (id, e) { + // console.log({ id, e }) + + // return true; + // }); + + gantt.init(this.ganttContainer); + // dataGantt = {data: []}; + // this.loadData(); + idLoop = 1 + dataGantt = { data: [], links: [] }; + this.runData(); + }; + + handleOpenDialog = (param) => { + console.log(param) + } + + constructor(props, context) { + super(props, context); + + // this.state = { + // dataGantt : {data: []} + + // }; + }; + + + + runData = () => { + const { startDate, endDate } = this.props + console.log({ startDate, endDate }) + gantt.config.start_date = new Date(startDate); + gantt.config.end_date = new Date(endDate); + + // startDate = this.props.startDate; + // endDate = this.props.endDate; + // companyId = this.props.config.companyId; + // action = this.props.config.action; + this.parseData() + gantt.clearAll(); + // console.log('--------- format gantt --------') + // console.log(dataGantt) + gantt.parse(dataGantt); + } + + parseDataPlanning = (param, idParent) => { + + param.map(res => { + let obj = { + end_date: moment(res.target_planning).format("DD-MM-YYYY HH:mm"), + id: res.id, + parent: idParent, + progress: 1, + start_date: moment(res.created_at).format("DD-MM-YYYY HH:mm"), + text: res.nama, + color: '#ffff00' + } + dataGantt.data.push(obj) + let link = { + id: idLoop, + source: idParent.toString(), + target: res.id.toString(), + type: "1" + } + dataGantt.links.push(link) + idLoop++; + }) + } + + parseDataWaspang = (param, idparent) => { + + param.map(res => { + const id = res.id + 100 + let obj = { + // end_date: moment(res.target_planning).format("DD-MM-YYYY HH:mm"), + id: id, + parent: idparent, + progress: 1, + // start_date: moment(res.created_at).format("DD-MM-YYYY HH:mm"), + text: res.name, + color: '#217f91' + } + dataGantt.data.push(obj) + if (res.plannings) this.parseDataPlanning(res.plannings, id) + }) + } + + parseDataChild = param => { + param.map(res => { + + if (res.subproyeks) { + const color = res.color_progress == "green" ? '#4caf50' : res.color_progress == "orange" ? '#ffc107' : '#f44336' + let obj = { + id: res.id, + open: true, + parent_id: res.parent_id, + proyek_id: res.proyek_id, + start_date: moment(res.mulai_proyek).format("DD-MM-YYYY HH:mm"), + end_date: moment(res.akhir_proyek).format("DD-MM-YYYY HH:mm"), + parent: res.parent_id || res.proyek_id, + text: res.nama, + // ...res.parent_id && { color: color } + } + dataGantt.data.push(obj) + this.parseDataChild(res.subproyeks) + } else { + const color = res.color_progress == "green" ? '#4caf50' : res.color_progress == "orange" ? '#ffc107' : '#f44336' + let obj = { + end_date: moment(res.akhir_proyek).format("DD-MM-YYYY HH:mm"), + id: res.id, + parent_id: res.parent_id, + proyek_id: res.proyek_id, + parent: res.parent_id || res.proyek_id, + progress: 1, + start_date: moment(res.mulai_proyek).format("DD-MM-YYYY HH:mm"), + text: res.nama, + // ...res.parent_id && { color: color } + } + dataGantt.data.push(obj) + if (res.user_waspangs) this.parseDataWaspang(res.user_waspangs, res.id) + } + }) + } + + parseData = () => { + + this.props.data.map(res => { + if (res.subproyeks) { + let obj = { + id: res.id, + open: true, + text: res.nama, + start_date: moment(res.mulai_proyek).format("DD-MM-YYYY HH:mm"), + end_date: moment(res.akhir_proyek).format("DD-MM-YYYY HH:mm"), + parent_id: res.parent_id, + proyek_id: res.proyek_id ? res.proyek_id : res.id, + color: '#142952' + } + dataGantt.data.push(obj) + this.parseDataChild(res.subproyeks) + } else { + let obj = { + end_date: moment(res.akhir_proyek).format("DD-MM-YYYY HH:mm"), + id: res.id, + // parent: res.id, + parent_id: res.parent_id, + proyek_id: res.proyek_id ? res.proyek_id : res.id, + progress: 1, + start_date: moment(res.mulai_proyek).format("DD-MM-YYYY HH:mm"), + text: res.nama, + color: '#142952' + } + dataGantt.data.push(obj) + } + }) + } + + componentDidUpdate(prevProps, prevState) { + if (prevProps.data !== this.props.data) { + dataGantt = { data: [], links: [] }; + idLoop = 1 + this.runData() + } + if (prevProps.startDate !== this.props.startDate) { + dataGantt = { data: [], links: [] }; + idLoop = 1 + this.runData() + } + if (prevProps.endDate !== this.props.endDate) { + dataGantt = { data: [], links: [] }; + idLoop = 1 + this.runData() + } + } + + + + render() { + + + let height = window.innerHeight - 100; + + return ( + +
+
{ this.ganttContainer = input }} + style={{ width: '100%', height: '100%' }} + > +
+
+ + + ); + } +} + diff --git a/src/views/ControlMonitoringGantt/index.js b/src/views/ControlMonitoringGantt/index.js new file mode 100644 index 0000000..0dcfdb7 --- /dev/null +++ b/src/views/ControlMonitoringGantt/index.js @@ -0,0 +1,272 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import Timeline from 'react-calendar-timeline' +// make sure you include the timeline stylesheet or the timeline will not be styled +import 'react-calendar-timeline/lib/Timeline.css' +import moment from 'moment'; +import { BASE_SIMPRO, PROYEK_ADD, PROYEK_SEARCH_DETAIL, GANTT_CONTROL_MONITORING_SEARCH, PROYEK_EDIT, PROYEK_DELETE } from '../../const/ApiConst'; +import axios from 'axios'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Button } from 'antd'; +import { + Container, Col, Row, UncontrolledTooltip, + Card, + CardBody, + CardHeader, + Table, + Modal, ModalHeader, ModalBody, ModalFooter +} from 'reactstrap'; +// import GanttFull from './GanttDhtmlx'; +import GanttFull from './GanttDhtmlx2'; +import DialogForm from '../Master/Proyek/DialogForm'; +import DialogFormSub from '../Master/Proyek/DialogFormSub'; +import Iframe from "@nicholasadamou/react-iframe"; + +const getUrlParameter = (sParam) => { + var sPageURL = window.location.search.substring(1), + sURLVariables = sPageURL.split('&'), + sParameterName, + i; + + console.log("sPageURL", sPageURL); + for (i = 0; i < sURLVariables.length; i++) { + sParameterName = sURLVariables[i].split('='); + + if (sParameterName[0] === sParam) { + return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]); + } + } + return false; +}; + +const GanttTimeLine = () => { + const token = localStorage.getItem("token") + const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + } + } + + const [proyekId, setProyekId] = useState(15); + const [dataGantt, setDataGantt] = useState([]); + const [prevProyekId, setPrevProyekId] = useState(0); + const [dataGroupGantt, setDataGroupGantt] = useState([]); + const [maxDateGantt, setMaxDateGantt] = useState(null); + const [minDateGantt, setMinDateGantt] = useState(null); + const [dataAllTimeLine, setDataAllTimeLine] = useState([]); + const [prevDataAllTimeLine, setPrevDataAllTimeLine] = useState(null); + + const [idTask, setidTask] = useState(0); + const [dataTable, setDatatable] = useState([]) + const [search, setSearch] = useState('') + const [currentPage, setCurrentPage] = useState(1) + const [totalPage, setTotalPage] = useState(0) + const [openDialog, setOpenDialog] = useState(false) + const [openDialogSub, setOpenDialogSub] = useState(false) + const [typeDialog, setTypeDialog] = useState('Save'); + const [typeDialogSub, setTypeDialogSub] = useState('Save') + const [idDelete, setIdDelete] = useState(0) + const [alertDelete, setAlertDelete] = useState(false) + const [dataEdit, setDataEdit] = useState([]) + const [dataEditSub, setDataEditSub] = useState([]) + const [rowsPerPage, setRowsPerPage] = useState(10) + const [clickOpenModal, setClickOpenModal] = useState(false) + const [dataExport, setDataExport] = useState([]) + const [allDataMenu, setAllDataMenu] = useState([]) + const [idSubtask, setIdSubtask] = useState(0); + + const handleMappingDataGantt = (data) => { + const minDates = data.map(res => moment(res.mulai_proyek)), + minDate = moment.min(minDates) + const maxDates = data.map(res => moment(res.akhir_proyek)), + maxDate = moment.max(maxDates) + + console.log({ minDate, maxDate }) + let groups = [] + let items = [] + data.map((res, idx) => { + let group = { + id: res.id, + title: res.nama, + stackItems: true, + height: 50 + } + let item = { + id: res.id, + group: res.id, + title: res.pic, + start_time: moment(res.mulai_proyek), + end_time: moment(res.akhir_proyek), + // selectedBgColor: res.color_progress == "green" ? 'rgba(255, 99, 132)' : res.color_progress == "orange" ? 'rgba(255,146,3)' : 'rgba(164,7,120)', + // bgColor: res.color_progress == "green" ? 'rgba(255, 99, 132, 0.7)' : res.color_progress == "orange" ? 'rgba(255,146,3, 0.7)' : 'rgba(164,7,120, 0.7)', + // itemProps: { + // style: { + // background: res.color_progress == "green" ? 'rgba(255, 99, 132)' : res.color_progress == "orange" ? 'rgba(255,146,3)' : 'rgba(164,7,120)' + // } + // } + } + groups.push(group) + items.push(item) + }) + // console.log({ items, groups }) + setDataGantt(items) + setDataGroupGantt(groups) + setMaxDateGantt(maxDate) + setMinDateGantt(minDate) + } + + const handleGetDataProyek = async () => { + + const payload = { + "columns": [ + { "name": "nama", "logic_operator": "like", "value": "", "operator": "AND" } + ], + "joins": [ + { "name": "subproyeks.m_proyek", "column_join": "proyek_id", "column_results": ["nama", "biaya", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual"] }, + { "name": "subproyeks.m_subproyek", "column_join": "parent_id", "column_results": ["nama", "biaya", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual"] } + ], + "orders": { "columns": ["id"], "ascending": true }, + "paging": { "start": 0, "length": 25 } + } + + const result = await axios + .post(GANTT_CONTROL_MONITORING_SEARCH, payload, HEADER) + .then(res => res) + .catch((error) => error.response); + + console.log(result) + + if (result && result.data && result.data.code == 200) { + const { data } = result.data + setDataAllTimeLine(data) + setPrevDataAllTimeLine(data) + handleMappingDataGantt(data) + + } else { + NotificationManager.error('Gagal Export Data!!', 'Failed'); + } + } + + useEffect(() => { + let proyek_id = getUrlParameter('proyek_id'); + console.log('proyek_id', proyek_id); + if (proyek_id) { + setProyekId(proyek_id); + } + handleGetDataProyek() + }, []); + + const handleClickGroupGantt = param => { + const row = prevDataAllTimeLine.filter(res => res.id === param) + if (row.length > 0) { + const { subproyeks, proyek_id, parent_id } = row[0] + console.log(subproyeks) + // setDataAllTimeLine(row[0]) + setPrevProyekId(parent_id || proyek_id) + handleMappingDataGantt(subproyeks) + setDataAllTimeLine(subproyeks) + } + + } + + const handleOpenDialog = (type) => { + setOpenDialog(true) + setTypeDialog(type) + } + + const handleOpenDialogSub = (type, param) => { + console.log("sub proyek", param) + const { id, parent_id, proyek_id, parent } = param + const idParent = parent == 0 ? 0 : parent == proyek_id ? id : parent_id + // console.log({ proyek_id, idParent }) + setidTask(proyek_id) + setIdSubtask(idParent) + setOpenDialogSub(true) + setTypeDialogSub(type) + } + + const handleCloseDialog = (type, data) => { + if (type === "save") { + saveProyek(data); + } + + setDataEdit([]) + setOpenDialog(false) + } + + const handleCloseDialogSub = (type, data) => { + // if (type === "save") { + // saveLocation(data); + // } else if (type === "edit") { + // editLocation(data); + // } + setDataEditSub([]) + setOpenDialogSub(false) + if (type !== "cancel") { + handleGetDataProyek() + } + + } + + const toggleAddDialog = () => { + setOpenDialog(!openDialog) + } + + const toggleAddDialogSub = () => { + setOpenDialogSub(!openDialogSub) + } + + const saveProyek = async (data) => { + const formData = data + + const result = await axios.post(PROYEK_ADD, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + handleGetDataProyek() + NotificationManager.success(`Data proyek berhasil ditambah`, 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + } + + const RenderGantt = useMemo(() => ( + + ), [dataAllTimeLine, maxDateGantt, minDateGantt]) + + return ( +
+ {/* {RenderGantt} */} + {/* */} + + ) + + return ( +
+ +
+ ); +} + +export default Gantt; \ No newline at end of file diff --git a/src/views/DashboardProject/index.js b/src/views/DashboardProject/index.js new file mode 100644 index 0000000..652a9b6 --- /dev/null +++ b/src/views/DashboardProject/index.js @@ -0,0 +1,171 @@ +import React from 'react'; +import { Row, Col, Select, Divider } from 'antd'; +import moment from 'moment' +import Gantt from './ganttDashboard'; +import TableDashboard from './tableDashboard'; +import ChatDashboard from './chatDashboard'; + +function BoxDashboard({ value, title, secondaryTitle, icon, bgColor }) { + return ( +
+
+
{title}
+
{icon}
+
+
Rp. {value}
+
+ ) +} + +const DashboardProject = () => { + return ( +
+ + +
+
+
Project Status
+
+
+ + +
+
+
Project Manager
+
Mark Julius
+
+ + +
+
+
RO
+
Capitol Corp
+
+ + +
+
+
Date
+
{moment().format("DD-MM-YYYY")}
+
+ + +
+ + +
+
SCHEDULE
+
+ + + +
+
Start Date
+
April 1, 2022
+
+ +
+
+
+ + + +
+
Baseline Finish Date
+
September 30, 2022
+
+ +
+
+
+ + + +
+
Estimated Finish Date
+
April 1, 2023
+
+ +
+
+
+ + +
+
FINANCIALS
+
+ + + +
+
Budget To Date (BCWP)
+
Rp. 2.000.000.000
+
+ +
+
+
+ + + +
+
Actual To Date (ACWP)
+
Rp. 1.700.000.000
+
+ +
+
+
+ + + +
+
Variance
+
Rp. 300.000.000
+
+ +
+
+
+ + + + } + value="3.200.000.000" + bgColor="#059669" + title="Budget" /> + +
+
+
ICON SET
+
+
+
+
+
+
+
+ + + +
+
+ + + +
+
Car Status
+ +
+ + + + +
+
+
+ ); +} + +export default DashboardProject; \ No newline at end of file diff --git a/src/views/DashboardProject/tableDashboard.js b/src/views/DashboardProject/tableDashboard.js new file mode 100644 index 0000000..85d00c6 --- /dev/null +++ b/src/views/DashboardProject/tableDashboard.js @@ -0,0 +1,77 @@ +import React from 'react'; +import { Table, Tag, Space } from 'antd'; + +const columns = [ + { + title: 'No', + dataIndex: 'carNo', + key: 'carNo', + fixed: 'left', + }, + { + title: 'Description', + dataIndex: 'description', + key: 'description', + width: 350 + }, + { + title: 'Severity', + dataIndex: 'severity', + key: 'severity', + render: text => { + return text == 'low' ?
LOW
: + text == 'medium' ?
MEDIUM
: +
HIGH
+ }, + }, + { + title: 'Status', + dataIndex: 'status', + key: 'status', + render: text => { + return text == 'open' ?
OPEN
: + text == 'on-progress' ?
ON PROGRESS
: +
DONE
+ }, + }, + { + title: 'Assigned', + dataIndex: 'assigned', + key: 'assigned', + + }, + { + title: 'Due', + dataIndex: 'due', + key: 'due', + } +]; + +const data = [ + { + key: '1', + carNo: 'RIS001', + description: 'Test Analyst on unplanned leave, risk to project if leave extends pas 5/20', + severity: 'medium', + status: 'on-progress', + due: '05/04/2022', + assigned: 'John Lennon' + }, + { + key: '2', + carNo: 'RIS002', + description: 'Coffee machine broken, result in extra long breaks to find coffee', + severity: 'low', + status: 'open', + due: '14/011/2022', + assigned: 'Robert William' + }, +]; + +const TableDashboard = () => { + return ( + + ); +} + +export default TableDashboard; \ No newline at end of file diff --git a/src/views/DashboardSecurity/index.js b/src/views/DashboardSecurity/index.js new file mode 100644 index 0000000..0e587ac --- /dev/null +++ b/src/views/DashboardSecurity/index.js @@ -0,0 +1,124 @@ +import React from 'react'; +import { Row, Col, Select, DatePicker } from 'antd'; +import TableDashboard from './tableDashboard'; +import MapDashboard from './mapDashboard'; +const { Option } = Select; + +const DashboardSecurity = () => { + const onChange = (value) => { + console.log(`selected ${value}`); + } + + const onSearch = (val) => { + console.log('search:', val); + } + + const onChangeDate = (date, dateString) => { + console.log(date, dateString); + } + return ( +
+ +
+ + + + + + + + + + + + + + + + + + +
+
+ + +
+ + ); +} + +export default DashboardSecurity; \ No newline at end of file diff --git a/src/views/DashboardSecurity/mapDashboard.js b/src/views/DashboardSecurity/mapDashboard.js new file mode 100644 index 0000000..1ebece3 --- /dev/null +++ b/src/views/DashboardSecurity/mapDashboard.js @@ -0,0 +1,51 @@ +import React, { useEffect } from 'react'; +import L from 'leaflet'; + + +let map = null + +const MapDashboard = () => { + + useEffect(() => { + + // INITIALIZE Dashboard + if (!map) { + map = L.map('map', { zoomControl: false }).setView([-2.721044, 116.62442], 5); + L.tileLayer( + 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 18, + attribution: '© OpenStreetMap contributors' + } + ).addTo(map); + L.marker([-6.142719569327774, 106.55132520955486]).addTo(map) + .bindPopup(` + ICN 1 +
+ 8 Pencurian`) + .openPopup(); + + L.marker([-1.9277364358639633, 102.86230604426582]).addTo(map) + .bindPopup(` + ICN 2 +
+ 2 Demonstrasi`) + .openPopup(); + + L.marker([-1.4977487818768103, 114.87755236966134]).addTo(map) + .bindPopup(` + ICN 3 +
+ 1 Demonstrasi`) + .openPopup(); + } + }, []); + + return ( + <> +
+ + + ); +} + +export default MapDashboard; \ No newline at end of file diff --git a/src/views/DashboardSecurity/tableDashboard.js b/src/views/DashboardSecurity/tableDashboard.js new file mode 100644 index 0000000..f14b01b --- /dev/null +++ b/src/views/DashboardSecurity/tableDashboard.js @@ -0,0 +1,85 @@ +import React from 'react'; +import { Table, Tag, Space } from 'antd'; + +const columns = [ + { + title: 'No', + dataIndex: 'key', + key: 'key', + width: 50 + }, + { + title: 'ICN', + dataIndex: 'icn', + key: 'icn', + }, + { + title: 'Project Name', + dataIndex: 'project', + key: 'project', + width: 300 + }, + { + title: 'Project Location', + dataIndex: 'location', + key: 'location', + }, + { + title: 'Incident Date', + dataIndex: 'startDate', + key: 'startDate', + }, + { + title: 'Incident', + dataIndex: 'incident', + key: 'incident', + }, + { + title: 'Status', + dataIndex: 'status', + key: 'status', + render: text => { + return text == 'open' ?
OPEN
:
CLOSE
+ }, + } +]; + +const data = [ + { + key: '1', + icn: 'ICN 1', + project: 'Banten International Airport', + status: 'close', + location: 'Banten', + startDate: '17/01/2022', + incident: 'Pencurian', + }, + { + key: '2', + icn: 'ICN 2', + project: 'Jambi Bridge', + location: 'Jambi', + status: 'open', + startDate: '04/2/2022', + incident: 'Demonstrasi', + }, + { + key: '3', + icn: 'ICN 3', + project: 'Bay Plaza', + location: 'Kalimantan', + status: 'open', + startDate: '28/3/2022', + incident: 'Demonstrasi', + } + + +]; + +const TableDashboard = () => { + return ( +
+ ); +} + +export default TableDashboard; \ No newline at end of file diff --git a/src/views/DashboardSimpro/BarChart.js b/src/views/DashboardSimpro/BarChart.js new file mode 100644 index 0000000..0695ba1 --- /dev/null +++ b/src/views/DashboardSimpro/BarChart.js @@ -0,0 +1,60 @@ +import React from 'react'; +import { Bar } from 'react-chartjs-2'; + +const data = { + labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], + datasets: [ + { + label: '# of Votes', + data: [12, 19, 3, 5, 2, 3], + backgroundColor: [ + 'rgba(255, 99, 132, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(75, 192, 192, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(255, 159, 64, 0.2)', + ], + borderColor: [ + 'rgba(255, 99, 132, 1)', + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(75, 192, 192, 1)', + 'rgba(153, 102, 255, 1)', + 'rgba(255, 159, 64, 1)', + ], + borderWidth: 1, + }, + ], +}; + +const options = { + scales: { + yAxes: [ + { + ticks: { + beginAtZero: true, + }, + }, + ], + }, +}; + +const BarChart = () => ( + <> + {/*
+

Vertical Bar Chart

+ +
*/} + + +); + +export default BarChart; \ No newline at end of file diff --git a/src/views/DashboardSimpro/Dashboard.css b/src/views/DashboardSimpro/Dashboard.css new file mode 100644 index 0000000..f57e9a6 --- /dev/null +++ b/src/views/DashboardSimpro/Dashboard.css @@ -0,0 +1,128 @@ +.number-asset { + font-size: 50px; + font-weight: bold; + text-align: center; +} + +.no-padding { + +} + +.text-bold { + font-weight: bold; +} + +.view-rectangle { + display: flex; + flex-direction: row; + width: 100%; + justify-content: space-between; +} + +.view1 { + width: 220px; + height: 100px; + padding: 10px; + margin-bottom: -20px; + + justify-content: center; + align-items: center; + text-align: center; + background-color: #008B8B; +} +.view2 { + width: 220px; + height: 100px; + padding: 10px; + + justify-content: center; + align-items: center; + text-align: center; + background-color: #8B008B; +} +.view3 { + width: 220px; + height: 100px; + padding: 10px; + + justify-content: center; + align-items: center; + text-align: center; + background-color: #7CFC00; +} +.view4 { + width: 220px; + height: 100px; + padding: 10px; + + justify-content: center; + align-items: center; + text-align: center; + background-color: #FF0000; +} +.view5 { + width: 220px; + height: 100px; + padding: 10px; + + justify-content: center; + align-items: center; + text-align: center; + background-color: #4682B4; +} + +.number-style { + font-size: 30px; + color: black; + font-weight: bold; + margin-bottom:15px; +} + +.number-style1 { + font-size: 30px; + color: #FFFFFF; + font-weight: bold; + margin-bottom:15px; +} + +.daily-info-card { + min-width: 130px; + width: 220px; + height: 100px; + padding: 10px; + justify-content: center; + align-items: center; + text-align: center; + margin: 4px; + border-radius: 8px; +} + + +.dashboard-container { + overflow-x: auto; +} + +.maptable-window-button-container { + float: right; + right: 0px; +} + +.maptable-close, .maptable-maximize, .maptable-minimize { + cursor: pointer; + padding: 4px; +} + +.maptable-header { + margin-bottom: -10px; +} + +.maptable-title { + font-size: 24px; + font-weight: 700; + text-align: left; + margin-left: 20px; +} + +.maptable-close:hover, .maptable-maximize:hover, .maptable-minimize:hover { + color: #20a8d8; +} diff --git a/src/views/DashboardSimpro/LineChart.js b/src/views/DashboardSimpro/LineChart.js new file mode 100644 index 0000000..2696fef --- /dev/null +++ b/src/views/DashboardSimpro/LineChart.js @@ -0,0 +1,31 @@ +import React from 'react'; +import { Line } from 'react-chartjs-2'; + +const MultiAxisLine = ({ data, handleClickChart, optionsAdded }) => { + const options = { ...optionsAdded }; + + return ( + <> + {/*
+

Multi Axis Line Chart

+ +
*/} + + + ); +} + +export default MultiAxisLine; \ No newline at end of file diff --git a/src/views/DashboardSimpro/PieChart.js b/src/views/DashboardSimpro/PieChart.js new file mode 100644 index 0000000..614a48e --- /dev/null +++ b/src/views/DashboardSimpro/PieChart.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { Pie } from 'react-chartjs-2'; + +const options = { + responsive: true, + maintainAspectRatio: false, + title: { + display: true, + text: 'Status Proyek' + }, + legend: { + display: true, + position: 'bottom' + // labels: { + // fontColor: 'rgb(255, 99, 132)' + // } + } +} + +const PieChart = ({data}) => ( + <> + + +); + +export default PieChart; \ No newline at end of file diff --git a/src/views/DashboardSimpro/index 16.js b/src/views/DashboardSimpro/index 16.js new file mode 100644 index 0000000..27e2cc4 --- /dev/null +++ b/src/views/DashboardSimpro/index 16.js @@ -0,0 +1,531 @@ +import { DatePicker } from 'antd'; +import Chart from 'chart.js'; +import ChartDataLabels from 'chartjs-plugin-datalabels'; +import moment from 'moment'; +import React, { useEffect, useState } from 'react'; +import { + Card, Modal, ModalHeader, ModalBody, ModalFooter, Button, + CardBody, CardHeader, Col, Dropdown, DropdownItem, DropdownMenu, DropdownToggle, Row +} from 'reactstrap'; +import { Pagination, Tooltip, Tree, List, Checkbox } from 'antd'; +import '../../../node_modules/react-grid-layout/css/styles.css'; +import '../../../node_modules/react-resizable/css/styles.css'; +import { + DONE_COLOR, IZIN_COLOR, NOT_YET_COLOR, + PRESENT_COLOR, TOTAL_COLOR, + RED_COLOR, + ORANGE_COLOR, + GREEN_COLOR, + DARK_GREY_COLOR, + BLUE_COLOR, + PURPLE_COLOR +} from '../../const/AppConst.js'; +import '../Map/CustomScroll.css'; +import './Dashboard.css'; +import { BASE_SIMPRO } from '../../const/ApiConst'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import axios from 'axios' +import numeral from 'numeral'; +import ContentLoader from "react-content-loader" +import LineChart from './LineChart'; +import PieChart from './PieChart'; +import { projectTreeConst } from '../../const/LayerTreeConst.js'; +import { PLANNING_REALISASI_SEARCH, PLANNING_SEARCH, PROYEK_SEARCH_DETAIL, PROYEK_EDIT } from '../../const/ApiConst.js'; +import { getChildrenTree, formatRupiah, DATE_TIME_FORMAT } from '../../const/CustomFunc.js'; + +const id_org = window.localStorage.getItem('id_org'); +const roleName = window.localStorage.getItem('role_name'); + +Chart.plugins.register(ChartDataLabels); +const { RangePicker } = DatePicker; + +let menu = [ + // { + // "id": 1, + // "title": "PLANNING", + // "key": "total karyawan", + // // "total": this.state.sumEmployee, + // "color": TOTAL_COLOR + // }, + // { + // "id": 2, + // "title": "REALISASI", + // "key": "presensi", + // // "total": this.state.sumPresensi, + // "color": PRESENT_COLOR + // }, + { + "id": 3, + "title": "PANIC BUTTON", + "key": "absent", + // "total": this.state.sumAbsensi, + "color": IZIN_COLOR + }, + { + "id": 4, + "title": "WASPANG ACTIVE", + "key": "karyawan telat", + // "total": this.state.sumTelat, + "color": GREEN_COLOR + }, + { + "id": 5, + "title": "WASPANG ABSENT", + "key": "karyawan tanpa keterangan", + // "total": this.state.sumTanpaKet, + "color": ORANGE_COLOR + } + // { + // "id": 6, + // "title": "PANIK BUTTON", + // "key": "panic button", + // "total": this.state.sumPanicBtn, + // "color": ABSENT_COLOR + // } +] + +const defaultPersentaseProyek = { + labels: [], + datasets: [ + { + label: 'Progress', + data: [], + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultCostProyek = { + labels: [], + datasets: [ + { + label: 'Perencanaan', + data: [], + fill: false, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgba(255, 99, 132, 0.2)', + yAxisID: 'y-axis-1', + }, + { + label: 'Realisasi', + data: [], + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultStatusProyek = { + labels: ['Aman', 'Alert', 'Critical'], + datasets: [ + { + label: '# of Votes', + data: [], + backgroundColor: [ + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(255, 99, 132, 0.2)', + ], + borderColor: [ + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(255, 99, 132, 1)', + ], + borderWidth: 1, + }, + ], +}; + +const DashboardSimpro = () => { + const token = localStorage.getItem("token") + const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + } + } + const [openPlanRealisasi, setopenPlanRealisasi] = useState(false); + const [openCostPlanRealisasi, setopenCostPlanRealisasi] = useState(false); + const [dataChart, setDataChart] = useState([]); + const [projectTree, setProjectTree] = useState([]); + const [allProyek, setAllProyek] = useState(true); + const [dataStatusProyek, setDataStatusProyek] = useState(null); + const [dataPersentaseProyek, setDataPersentaseProyek] = useState(null); + const [dataCostProyek, setdataCostProyek] = useState(null); + const [projectTreeVisible, setProjectTreeVisible] = useState(false); + const [checkedKeysProjectTree, setCheckedKeysProjectTree] = useState([]); + const [openModalTable, setOpenModalTable] = useState(false); + const [dataDashboard, setDataDashboard] = useState(null); + + const handleGetDataDashboard = async () => { + const URL = `${BASE_SIMPRO}/dashboard-proyek/search` + const payload = { + "columns": [ + { "name": "created_at", "logic_operator": "range", "value": "2021-11-06 00:00:00", "value1": "2021-11-06 23:59:59", "operator": "AND" } + ], + "paging": { "start": 0, "length": -1 } + } + // console.log(payload) + const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response) + if (result.data.code === 200) { + setDataDashboard(result.data.data); + } else { + NotificationManager.error('Gaga Menambah Data!!', 'Failed'); + } + + } + + const handleGetDataDashboardChart = async () => { + const URL = `${BASE_SIMPRO}/dashboard-status/search` + let str = '' + checkedKeysProjectTree.map((res, idx) => { + if (idx == 0) str += `${res}` + if (idx != 0) str += `,${res}` + }) + console.log(str) + const payload = { + "columns": [ + { "name": "id", "logic_operator": "in", "value": str ? str : "0", "operator": "AND" } + ], + "orders": { "columns": ["nama"], "ascending": true } + } + // console.log(payload) + const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response) + if (result.data.code === 200) { + + const { persentase_progress, progress_cost_planning, progress_cost_realisasi, status_proyek } = result.data.data + + const labelPersentaseProyek = persentase_progress ? persentase_progress.map(res => res.label) : [] + const valuePersentaseProyek = persentase_progress ? persentase_progress.map(res => res.total) : [] + const persentaseProyek = { + labels: labelPersentaseProyek, + datasets: [ + { + label: 'Progress', + data: valuePersentaseProyek, + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], + }; + setDataPersentaseProyek(persentaseProyek) + const labelCostPlaning = progress_cost_planning ? progress_cost_planning.map(res => res.label) : [] + const valueCostPlaning = progress_cost_planning ? progress_cost_planning.map(res => res.total) : [] + const valueCostRealisasi = progress_cost_realisasi ? progress_cost_realisasi.map(res => res.total) : [] + + const costProyek = { + labels: labelCostPlaning, + datasets: [ + { + label: 'Perencanaan', + data: valueCostPlaning, + fill: false, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgba(255, 99, 132, 0.2)', + yAxisID: 'y-axis-1', + }, + { + label: 'Realisasi', + data: valueCostRealisasi, + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], + }; + setdataCostProyek(costProyek) + + const valueStatusProyek = status_proyek ? status_proyek.map(res => res.total) : [] + + const statusProyek = { + labels: ['Aman', 'Alert', 'Critical'], + datasets: [ + { + label: '# of Votes', + data: valueStatusProyek, + backgroundColor: [ + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(255, 99, 132, 0.2)', + ], + borderColor: [ + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(255, 99, 132, 1)', + ], + borderWidth: 1, + }, + ], + }; + + setDataStatusProyek(statusProyek) + + } else { + NotificationManager.error('Gaga Menambah Data!!', 'Failed'); + } + + } + + const getProyek = async () => { + const URL = `${BASE_SIMPRO}/proyek/list?start=0&length=-1&orderby=nama&asc=true` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + if (result.data.code === 200) { + setProjectTree(result.data.data); + const arr = result.data.data.map(res => res.id) + // console.log("checked all", data) + setCheckedKeysProjectTree(arr) + } else { + NotificationManager.error('Gaga Menambah Data!!', 'Failed'); + } + } + useEffect(() => { + handleGetDataDashboardChart() + // console.log(checkedKeysProjectTree) + }, [checkedKeysProjectTree]); + + useEffect(() => { + if (allProyek) { + const data = projectTree.map(res => res.id) + console.log("checked all", data) + setCheckedKeysProjectTree(data) + } + }, [allProyek]); + + useEffect(() => { + handleGetDataDashboard() + getProyek() + // return () => { + // cleanup + // }; + }, []); + + const renderDailyInfo = () => { + + return ( + <> + {/*
+

{dataDashboard ? dataDashboard.planning : 0}

+

PLANNING

+
+
+

{dataDashboard ? dataDashboard.realisasi : 0}

+

REALISASI

+
*/} +
+

{dataDashboard ? dataDashboard.panic_button : 0}

+

PANIC BUTTON

+
+
+

{dataDashboard ? dataDashboard.waspang_status.presensi : 0}

+

WASPANG ACTIVE

+
+
+

{dataDashboard ? dataDashboard.waspang_status.absensi : 0}

+

WASPANG ABSENT

+
+ + ) + } + + const handleClickChart = param => { + if (!param.length) return; + console.log(param) + const { _datasetIndex, _index } = param[0]; + const data = dataPersentaseProyek.datasets[_datasetIndex].label + console.log(data) + setOpenModalTable(true) + } + + const handleClickProyek = id => { + const arr = [...checkedKeysProjectTree] + const idx = arr.indexOf(id) + if (idx == -1) { + arr.push(id) + } else { + arr.splice(idx, 1) + } + setCheckedKeysProjectTree(arr) + } + + return ( +
+ setOpenModalTable(!openModalTable)} + > + setOpenModalTable(!openModalTable)}> + Modal title + + + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + + + + {' '} + + + + +
+ +
+ + + +
Proyek
+ {/* +
+ + + +
+ */} + + + +
+ setAllProyek(!allProyek)}>Semua + Header
} + // footer={
Footer
} + // bordered + dataSource={projectTree} + renderItem={item => ( + + handleClickProyek(item.id)}>{item.nama} + + )} + /> + +
+ + + + + {/* + + Waspang Status + + */} +
+ {renderDailyInfo()} +
+ {/*
+
*/} + + +
+ +
+ + + + +
+ +
+ {/*
*/} + + {/*
*/} + + + {/*
*/} + + {/*
*/} + + + + + + ); +} + +export default DashboardSimpro; \ No newline at end of file diff --git a/src/views/DashboardSimpro/index.js b/src/views/DashboardSimpro/index.js new file mode 100644 index 0000000..c9574a2 --- /dev/null +++ b/src/views/DashboardSimpro/index.js @@ -0,0 +1,366 @@ +import { DatePicker } from 'antd'; +import Chart from 'chart.js'; +import ChartDataLabels from 'chartjs-plugin-datalabels'; +import moment from 'moment'; +import React, { useEffect, useState } from 'react'; +import { + Card, Modal, ModalHeader, ModalBody, ModalFooter, Button, + CardBody, CardHeader, Col, Dropdown, DropdownItem, DropdownMenu, DropdownToggle, Row +} from 'reactstrap'; +import { Pagination, Tooltip, Tree, List, Checkbox } from 'antd'; +import '../../../node_modules/react-grid-layout/css/styles.css'; +import '../../../node_modules/react-resizable/css/styles.css'; +import { + DONE_COLOR, IZIN_COLOR, NOT_YET_COLOR, + PRESENT_COLOR, TOTAL_COLOR, + RED_COLOR, + ORANGE_COLOR, + GREEN_COLOR, + DARK_GREY_COLOR, + BLUE_COLOR, + PURPLE_COLOR +} from '../../const/AppConst.js'; +import '../Map/CustomScroll.css'; +import './Dashboard.css'; +import { BASE_SIMPRO } from '../../const/ApiConst'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import axios from 'axios' +import numeral from 'numeral'; +import ContentLoader from "react-content-loader" +import LineChart from './LineChart'; +import PieChart from './PieChart'; +import { projectTreeConst } from '../../const/LayerTreeConst.js'; +import { PLANNING_REALISASI_SEARCH, PLANNING_SEARCH, PROYEK_SEARCH_DETAIL, PROYEK_EDIT } from '../../const/ApiConst.js'; +import { getChildrenTree, formatRupiah, DATE_TIME_FORMAT } from '../../const/CustomFunc.js'; +import SiopasMap from '../Map/Map_16.js'; +const id_org = window.localStorage.getItem('id_org'); +const roleName = window.localStorage.getItem('role_name'); + +Chart.plugins.register(ChartDataLabels); +const { RangePicker } = DatePicker; + +let menu = [ + // { + // "id": 1, + // "title": "PLANNING", + // "key": "total karyawan", + // // "total": this.state.sumEmployee, + // "color": TOTAL_COLOR + // }, + // { + // "id": 2, + // "title": "REALISASI", + // "key": "presensi", + // // "total": this.state.sumPresensi, + // "color": PRESENT_COLOR + // }, + { + "id": 3, + "title": "PANIC BUTTON", + "key": "absent", + // "total": this.state.sumAbsensi, + "color": IZIN_COLOR + }, + { + "id": 4, + "title": "WASPANG ACTIVE", + "key": "karyawan telat", + // "total": this.state.sumTelat, + "color": GREEN_COLOR + }, + { + "id": 5, + "title": "WASPANG ABSENT", + "key": "karyawan tanpa keterangan", + // "total": this.state.sumTanpaKet, + "color": ORANGE_COLOR + } + // { + // "id": 6, + // "title": "PANIK BUTTON", + // "key": "panic button", + // "total": this.state.sumPanicBtn, + // "color": ABSENT_COLOR + // } +] + +const defaultPersentaseProyek = { + labels: [], + datasets: [ + { + label: 'Progress', + data: [], + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultCostProyek = { + labels: [], + datasets: [ + { + label: 'Perencanaan', + data: [], + fill: false, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgba(255, 99, 132, 0.2)', + yAxisID: 'y-axis-1', + }, + { + label: 'Realisasi', + data: [], + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultStatusProyek = { + labels: ['Aman', 'Alert', 'Critical'], + datasets: [ + { + label: '# of Votes', + data: [], + backgroundColor: [ + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(255, 99, 132, 0.2)', + ], + borderColor: [ + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(255, 99, 132, 1)', + ], + borderWidth: 1, + }, + ], +}; + +const DashboardSimpro = () => { + const token = localStorage.getItem("token") + const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + } + } + const [openPlanRealisasi, setopenPlanRealisasi] = useState(false); + const [openCostPlanRealisasi, setopenCostPlanRealisasi] = useState(false); + const [dataChart, setDataChart] = useState([]); + const [projectTree, setProjectTree] = useState([]); + const [allProyek, setAllProyek] = useState(true); + const [dataStatusProyek, setDataStatusProyek] = useState(null); + const [dataPersentaseProyek, setDataPersentaseProyek] = useState(null); + const [dataCostProyek, setdataCostProyek] = useState(null); + const [projectTreeVisible, setProjectTreeVisible] = useState(false); + const [checkedKeysProjectTree, setCheckedKeysProjectTree] = useState([]); + const [openModalTable, setOpenModalTable] = useState(false); + const [dataDashboard, setDataDashboard] = useState(null); + + const handleGetDataDashboard = async () => { + const URL = `${BASE_SIMPRO}/dashboard-proyek/search` + const payload = { + "columns": [ + { "name": "created_at", "logic_operator": "range", "value": "2021-11-06 00:00:00", "value1": "2021-11-06 23:59:59", "operator": "AND" } + ], + "paging": { "start": 0, "length": -1 } + } + // console.log(payload) + const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response) + if (result.data.code === 200) { + setDataDashboard(result.data.data); + } else { + NotificationManager.error('Gaga Menambah Data!!', 'Failed'); + } + + } + + const handleGetDataDashboardChart = async () => { + const URL = `${BASE_SIMPRO}/dashboard-status/search` + let str = '' + checkedKeysProjectTree.map((res, idx) => { + if (idx == 0) str += `${res}` + if (idx != 0) str += `,${res}` + }) + console.log(str) + const payload = { + "columns": [ + { "name": "id", "logic_operator": "in", "value": str ? str : "0", "operator": "AND" } + ], + "orders": { "columns": ["nama"], "ascending": true } + } + // console.log(payload) + const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response) + if (result.data.code === 200) { + + const { persentase_progress, progress_cost_planning, progress_cost_realisasi, status_proyek } = result.data.data + + const labelPersentaseProyek = persentase_progress ? persentase_progress.map(res => res.label) : [] + const valuePersentaseProyek = persentase_progress ? persentase_progress.map(res => res.total) : [] + const persentaseProyek = { + labels: labelPersentaseProyek, + datasets: [ + { + label: 'Progress', + data: valuePersentaseProyek, + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], + }; + setDataPersentaseProyek(persentaseProyek) + const labelCostPlaning = progress_cost_planning ? progress_cost_planning.map(res => res.label) : [] + const valueCostPlaning = progress_cost_planning ? progress_cost_planning.map(res => res.total) : [] + const valueCostRealisasi = progress_cost_realisasi ? progress_cost_realisasi.map(res => res.total) : [] + + const costProyek = { + labels: labelCostPlaning, + datasets: [ + { + label: 'Perencanaan', + data: valueCostPlaning, + fill: false, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgba(255, 99, 132, 0.2)', + yAxisID: 'y-axis-1', + }, + { + label: 'Realisasi', + data: valueCostRealisasi, + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], + }; + setdataCostProyek(costProyek) + + const valueStatusProyek = status_proyek ? status_proyek.map(res => res.total) : [] + + const statusProyek = { + labels: ['Aman', 'Alert', 'Critical'], + datasets: [ + { + label: '# of Votes', + data: valueStatusProyek, + backgroundColor: [ + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(255, 99, 132, 0.2)', + ], + borderColor: [ + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(255, 99, 132, 1)', + ], + borderWidth: 1, + }, + ], + }; + + setDataStatusProyek(statusProyek) + + } else { + NotificationManager.error('Gaga Menambah Data!!', 'Failed'); + } + + } + + const getProyek = async () => { + const URL = `${BASE_SIMPRO}/proyek/list?start=0&length=-1&orderby=nama&asc=true` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + if (result.data.code === 200) { + setProjectTree(result.data.data); + const arr = result.data.data.map(res => res.id) + // console.log("checked all", data) + setCheckedKeysProjectTree(arr) + } else { + NotificationManager.error('Gaga Menambah Data!!', 'Failed'); + } + } + // useEffect(() => { + // handleGetDataDashboardChart() + // // console.log(checkedKeysProjectTree) + // }, [checkedKeysProjectTree]); + + // useEffect(() => { + // if (allProyek) { + // const data = projectTree.map(res => res.id) + // console.log("checked all", data) + // setCheckedKeysProjectTree(data) + // } + // }, [allProyek]); + + // useEffect(() => { + // handleGetDataDashboard() + // getProyek() + // // return () => { + // // cleanup + // // }; + // }, []); + + const renderDailyInfo = () => { + + return ( + <> + {/*
+

{dataDashboard ? dataDashboard.planning : 0}

+

PLANNING

+
+
+

{dataDashboard ? dataDashboard.realisasi : 0}

+

REALISASI

+
*/} +
+

{dataDashboard ? dataDashboard.panic_button : 0}

+

PANIC BUTTON

+
+
+

{dataDashboard ? dataDashboard.waspang_status.presensi : 0}

+

WASPANG ACTIVE

+
+
+

{dataDashboard ? dataDashboard.waspang_status.absensi : 0}

+

WASPANG ABSENT

+
+ + ) + } + + const handleClickChart = param => { + if (!param.length) return; + console.log(param) + const { _datasetIndex, _index } = param[0]; + const data = dataPersentaseProyek.datasets[_datasetIndex].label + console.log(data) + setOpenModalTable(true) + } + + const handleClickProyek = id => { + const arr = [...checkedKeysProjectTree] + const idx = arr.indexOf(id) + if (idx == -1) { + arr.push(id) + } else { + arr.splice(idx, 1) + } + setCheckedKeysProjectTree(arr) + } + + return ( + <> + + + + ); +} + +export default DashboardSimpro; \ No newline at end of file diff --git a/src/views/Icons/CoreUIIcons/CoreUIIcons.js b/src/views/Icons/CoreUIIcons/CoreUIIcons.js new file mode 100644 index 0000000..12115cf --- /dev/null +++ b/src/views/Icons/CoreUIIcons/CoreUIIcons.js @@ -0,0 +1,428 @@ +import React, { Component } from 'react'; +import { Badge, Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; + +class CoreUIIcons extends Component { + render() { + return ( +
+ + + CoreUI Icons{' '} New + + + + +
+ +
account-logout
+ + + +
action-redo
+ + + +
action-undo
+ + + +
align-center
+ + + +
align-left
+ + + +
align-right
+ + + +
arrow-bottom
+ + + +
arrow-left
+ + + +
arrow-right
+ + + +
arrow-top
+ + + +
ban
+ + + +
basket-loaded
+ + + +
bell
+ + + +
bold
+ + + +
bookmark
+ + + +
briefcase
+ + + +
british-pound
+ + + +
brush
+ + + +
calculator
+ + + +
calendar
+ + + +
cart
+ + + +
chart
+ + + +
check
+ + + +
chevron-bottom
+ + + +
chevron-left
+ + + +
chevron-right
+ + + +
chevron-top
+ + + +
circle-check
+ + + +
circle-x
+ + + +
cloud
+ + + +
cloud-download
+ + + +
cloud-upload
+ + + +
code
+ + + +
cog
+ + + +
comment-square
+ + + +
credit-card
+ + + +
cursor
+ + + +
dashboard
+ + + +
delete
+ + + +
dollar
+ + + +
drop
+ + + +
envelope-closed
+ + + +
envelope-letter
+ + + +
envelope-open
+ + + +
euro
+ + + +
file
+ + + +
globe
+ + + +
graph
+ + + +
home
+ + + +
inbox
+ + + +
info
+ + + +
italic
+ + + +
justify-center
+ + + +
justify-left
+ + + +
justify-right
+ + + +
laptop
+ + + +
layers
+ + + +
lightbulb
+ + + +
list
+ + + +
location-pin
+ + + +
lock-locked
+ + + +
lock-unlocked
+ + + +
magnifying-glass
+ + + +
map
+ + + +
monitor
+ + + +
moon
+ + + +
note
+ + + +
options
+ + + +
paperclip
+ + + +
pencil
+ + + +
people
+ + + +
phone
+ + + +
pie-chart
+ + + +
print
+ + + +
puzzle
+ + + +
rss
+ + + +
screen-desktop
+ + + +
screen-smartphone
+ + + +
settings
+ + + +
share
+ + + +
shield
+ + + +
sort-ascending
+ + + +
sort-descending
+ + + +
speech
+ + + +
speedometer
+ + + +
star
+ + + +
sun
+ + + +
tablet
+ + + +
tags
+ + + +
task
+ + + +
thumb-down
+ + + +
thumb-up
+ + + +
trash
+ + + +
underline
+ + + +
user
+ + + +
user-female
+ + + +
user-follow
+ + + +
user-unfollow
+ + + +
wrench
+ + + +
yen
+ + + + + + ); + } +} + +export default CoreUIIcons; + diff --git a/src/views/Icons/CoreUIIcons/CoreUIIcons.test.js b/src/views/Icons/CoreUIIcons/CoreUIIcons.test.js new file mode 100644 index 0000000..4058e12 --- /dev/null +++ b/src/views/Icons/CoreUIIcons/CoreUIIcons.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import CoreUIIcons from './CoreUIIcons'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Icons/CoreUIIcons/package.json b/src/views/Icons/CoreUIIcons/package.json new file mode 100644 index 0000000..2ceef5d --- /dev/null +++ b/src/views/Icons/CoreUIIcons/package.json @@ -0,0 +1,6 @@ +{ + "name": "CoreUIIcons", + "version": "0.0.0", + "private": true, + "main": "./CoreUIIcons.js" +} diff --git a/src/views/Icons/Flags/Flags.js b/src/views/Icons/Flags/Flags.js new file mode 100644 index 0000000..cd97985 --- /dev/null +++ b/src/views/Icons/Flags/Flags.js @@ -0,0 +1,1022 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; + +class Flags extends Component { + render() { + return ( +
+ + + Flags + + + +
+ For using the flags inline with text add the classes .flag-icon and .flag-icon-xx (where xx is the ISO 3166-1-alpha-2 + code of a country) to an empty span. If you want to have a squared version flag then add the class flag-icon-squared as well. + + + +
flag-icon-ad
+ + + +
flag-icon-ae
+ + + +
flag-icon-af
+ + + +
flag-icon-ag
+ + + +
flag-icon-ai
+ + + +
flag-icon-al
+ + + +
flag-icon-am
+ + + +
flag-icon-ao
+ + + +
flag-icon-aq
+ + + +
flag-icon-ar
+ + + +
flag-icon-as
+ + + +
flag-icon-at
+ + + +
flag-icon-au
+ + + +
flag-icon-aw
+ + + +
flag-icon-ax
+ + + +
flag-icon-az
+ + + +
flag-icon-ba
+ + + +
flag-icon-bb
+ + + +
flag-icon-bd
+ + + +
flag-icon-be
+ + + +
flag-icon-bf
+ + + +
flag-icon-bg
+ + + +
flag-icon-bh
+ + + +
flag-icon-bi
+ + + +
flag-icon-bj
+ + + +
flag-icon-bl
+ + + +
flag-icon-bm
+ + + +
flag-icon-bn
+ + + +
flag-icon-bo
+ + + +
flag-icon-bq
+ + + +
flag-icon-br
+ + + +
flag-icon-bs
+ + + +
flag-icon-bt
+ + + +
flag-icon-bv
+ + + +
flag-icon-bw
+ + + +
flag-icon-by
+ + + +
flag-icon-bz
+ + + +
flag-icon-ca
+ + + +
flag-icon-cc
+ + + +
flag-icon-cd
+ + + +
flag-icon-cf
+ + + +
flag-icon-cg
+ + + +
flag-icon-ch
+ + + +
flag-icon-ci
+ + + +
flag-icon-ck
+ + + +
flag-icon-cl
+ + + +
flag-icon-cm
+ + + +
flag-icon-cn
+ + + +
flag-icon-co
+ + + +
flag-icon-cr
+ + + +
flag-icon-cu
+ + + +
flag-icon-cv
+ + + +
flag-icon-cw
+ + + +
flag-icon-cx
+ + + +
flag-icon-cy
+ + + +
flag-icon-cz
+ + + +
flag-icon-de
+ + + +
flag-icon-dj
+ + + +
flag-icon-dk
+ + + +
flag-icon-dm
+ + + +
flag-icon-do
+ + + +
flag-icon-dz
+ + + +
flag-icon-ec
+ + + +
flag-icon-ee
+ + + +
flag-icon-eg
+ + + +
flag-icon-eh
+ + + +
flag-icon-er
+ + + +
flag-icon-es
+ + + +
flag-icon-et
+ + + +
flag-icon-fi
+ + + +
flag-icon-fj
+ + + +
flag-icon-fk
+ + + +
flag-icon-fm
+ + + +
flag-icon-fo
+ + + +
flag-icon-fr
+ + + +
flag-icon-ga
+ + + +
flag-icon-gb
+ + + +
flag-icon-gd
+ + + +
flag-icon-ge
+ + + +
flag-icon-gf
+ + + +
flag-icon-gg
+ + + +
flag-icon-gh
+ + + +
flag-icon-gi
+ + + +
flag-icon-gl
+ + + +
flag-icon-gm
+ + + +
flag-icon-gn
+ + + +
flag-icon-gp
+ + + +
flag-icon-gq
+ + + +
flag-icon-gr
+ + + +
flag-icon-gs
+ + + +
flag-icon-gt
+ + + +
flag-icon-gu
+ + + +
flag-icon-gw
+ + + +
flag-icon-gy
+ + + +
flag-icon-hk
+ + + +
flag-icon-hm
+ + + +
flag-icon-hn
+ + + +
flag-icon-hr
+ + + +
flag-icon-ht
+ + + +
flag-icon-hu
+ + + +
flag-icon-id
+ + + +
flag-icon-ie
+ + + +
flag-icon-il
+ + + +
flag-icon-im
+ + + +
flag-icon-in
+ + + +
flag-icon-io
+ + + +
flag-icon-iq
+ + + +
flag-icon-ir
+ + + +
flag-icon-is
+ + + +
flag-icon-it
+ + + +
flag-icon-je
+ + + +
flag-icon-jm
+ + + +
flag-icon-jo
+ + + +
flag-icon-jp
+ + + +
flag-icon-ke
+ + + +
flag-icon-kg
+ + + +
flag-icon-kh
+ + + +
flag-icon-ki
+ + + +
flag-icon-km
+ + + +
flag-icon-kn
+ + + +
flag-icon-kp
+ + + +
flag-icon-kr
+ + + +
flag-icon-kw
+ + + +
flag-icon-ky
+ + + +
flag-icon-kz
+ + + +
flag-icon-la
+ + + +
flag-icon-lb
+ + + +
flag-icon-lc
+ + + +
flag-icon-li
+ + + +
flag-icon-lk
+ + + +
flag-icon-lr
+ + + +
flag-icon-ls
+ + + +
flag-icon-lt
+ + + +
flag-icon-lu
+ + + +
flag-icon-lv
+ + + +
flag-icon-ly
+ + + +
flag-icon-ma
+ + + +
flag-icon-mc
+ + + +
flag-icon-md
+ + + +
flag-icon-me
+ + + +
flag-icon-mf
+ + + +
flag-icon-mg
+ + + +
flag-icon-mh
+ + + +
flag-icon-mk
+ + + +
flag-icon-ml
+ + + +
flag-icon-mm
+ + + +
flag-icon-mn
+ + + +
flag-icon-mo
+ + + +
flag-icon-mp
+ + + +
flag-icon-mq
+ + + +
flag-icon-mr
+ + + +
flag-icon-ms
+ + + +
flag-icon-mt
+ + + +
flag-icon-mu
+ + + +
flag-icon-mv
+ + + +
flag-icon-mw
+ + + +
flag-icon-mx
+ + + +
flag-icon-my
+ + + +
flag-icon-mz
+ + + +
flag-icon-na
+ + + +
flag-icon-nc
+ + + +
flag-icon-ne
+ + + +
flag-icon-nf
+ + + +
flag-icon-ng
+ + + +
flag-icon-ni
+ + + +
flag-icon-nl
+ + + +
flag-icon-no
+ + + +
flag-icon-np
+ + + +
flag-icon-nr
+ + + +
flag-icon-nu
+ + + +
flag-icon-nz
+ + + +
flag-icon-om
+ + + +
flag-icon-pa
+ + + +
flag-icon-pe
+ + + +
flag-icon-pf
+ + + +
flag-icon-pg
+ + + +
flag-icon-ph
+ + + +
flag-icon-pk
+ + + +
flag-icon-pl
+ + + +
flag-icon-pm
+ + + +
flag-icon-pn
+ + + +
flag-icon-pr
+ + + +
flag-icon-ps
+ + + +
flag-icon-pt
+ + + +
flag-icon-pw
+ + + +
flag-icon-py
+ + + +
flag-icon-qa
+ + + +
flag-icon-re
+ + + +
flag-icon-ro
+ + + +
flag-icon-rs
+ + + +
flag-icon-ru
+ + + +
flag-icon-rw
+ + + +
flag-icon-sa
+ + + +
flag-icon-sb
+ + + +
flag-icon-sc
+ + + +
flag-icon-sd
+ + + +
flag-icon-se
+ + + +
flag-icon-sg
+ + + +
flag-icon-sh
+ + + +
flag-icon-si
+ + + +
flag-icon-sj
+ + + +
flag-icon-sk
+ + + +
flag-icon-sl
+ + + +
flag-icon-sm
+ + + +
flag-icon-sn
+ + + +
flag-icon-so
+ + + +
flag-icon-sr
+ + + +
flag-icon-ss
+ + + +
flag-icon-st
+ + + +
flag-icon-sv
+ + + +
flag-icon-sx
+ + + +
flag-icon-sy
+ + + +
flag-icon-sz
+ + + +
flag-icon-tc
+ + + +
flag-icon-td
+ + + +
flag-icon-tf
+ + + +
flag-icon-tg
+ + + +
flag-icon-th
+ + + +
flag-icon-tj
+ + + +
flag-icon-tk
+ + + +
flag-icon-tl
+ + + +
flag-icon-tm
+ + + +
flag-icon-tn
+ + + +
flag-icon-to
+ + + +
flag-icon-tr
+ + + +
flag-icon-tt
+ + + +
flag-icon-tv
+ + + +
flag-icon-tw
+ + + +
flag-icon-tz
+ + + +
flag-icon-ua
+ + + +
flag-icon-ug
+ + + +
flag-icon-um
+ + + +
flag-icon-us
+ + + +
flag-icon-uy
+ + + +
flag-icon-uz
+ + + +
flag-icon-va
+ + + +
flag-icon-vc
+ + + +
flag-icon-ve
+ + + +
flag-icon-vg
+ + + +
flag-icon-vi
+ + + +
flag-icon-vn
+ + + +
flag-icon-vu
+ + + +
flag-icon-wf
+ + + +
flag-icon-ws
+ + + +
flag-icon-ye
+ + + +
flag-icon-yt
+ + + +
flag-icon-za
+ + + +
flag-icon-zm
+ + + +
flag-icon-zw
+ + + + + + ); + } +} + +export default Flags; diff --git a/src/views/Icons/Flags/Flags.test.js b/src/views/Icons/Flags/Flags.test.js new file mode 100644 index 0000000..ff2fd0e --- /dev/null +++ b/src/views/Icons/Flags/Flags.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Flags from './Flags'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/views/Icons/Flags/package.json b/src/views/Icons/Flags/package.json new file mode 100644 index 0000000..807c68d --- /dev/null +++ b/src/views/Icons/Flags/package.json @@ -0,0 +1,6 @@ +{ + "name": "Flags", + "version": "0.0.0", + "private": true, + "main": "./Flags.js" +} diff --git a/src/views/Icons/FontAwesome/FontAwesome.js b/src/views/Icons/FontAwesome/FontAwesome.js new file mode 100644 index 0000000..92295dc --- /dev/null +++ b/src/views/Icons/FontAwesome/FontAwesome.js @@ -0,0 +1,3702 @@ +import React, { Component } from 'react'; +import { Alert, Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; + +class FontAwesome extends Component { + render() { + return ( +
+ + + 55 New Icons in 4.7 + + + +
+
address-book + + +
address-book-o + + +
address-card + + +
address-card-o + + +
bandcamp + + +
bath + + +
bathtub (alias) + + +
drivers-license (alias) + + +
drivers-license-o (alias) + + +
eercast + + +
envelope-open + + +
envelope-open-o + + +
etsy + + +
free-code-camp + + +
grav + + +
handshake-o + + +
id-badge + + +
id-card + + +
id-card-o + + +
imdb + + +
linode + + +
meetup + + +
microchip + + +
podcast + + +
quora + + +
ravelry + + +
s15 (alias) + + +
shower + + +
snowflake-o + + +
superpowers + + +
telegram + + +
thermometer (alias) + + +
thermometer-0 (alias) + + +
thermometer-1 (alias) + + +
thermometer-2 (alias) + + +
thermometer-3 (alias) + + +
thermometer-4 (alias) + + +
thermometer-empty + + +
thermometer-full + + +
thermometer-half + + +
thermometer-quarter + + +
thermometer-three-quarters + + +
times-rectangle (alias) + + +
times-rectangle-o (alias) + + +
user-circle + + +
user-circle-o + + +
user-o + + +
vcard (alias) + + +
vcard-o (alias) + + +
window-close + + +
window-close-o + + +
window-maximize + + +
window-minimize + + +
window-restore + + +
wpexplorer + + + + + + 20 New Icons in 4.5 + + + + +
bluetooth + + + +
bluetooth-b + + + +
codiepie + + + +
credit-card-alt + + + +
edge + + + +
fort-awesome + + + +
hashtag + + + +
mixcloud + + + +
modx + + + +
pause-circle + + + +
pause-circle-o + + + +
percent + + + +
product-hunt + + + +
reddit-alien + + + +
scribd + + + +
shopping-bag + + + +
shopping-basket + + + +
stop-circle + + + +
stop-circle-o + + + +
usb + + + + + + + + + Web Application Icons + + + + +
adjust + + + +
anchor + + + +
archive + + + +
area-chart + + + +
arrows + + + +
arrows-h + + + +
arrows-v + + + +
asterisk + + + +
at + + + +
automobile (alias) + + + +
balance-scale + + + +
ban + + + +
bank (alias) + + + +
bar-chart + + + +
bar-chart-o (alias) + + + +
barcode + + + +
bars + + + +
battery-0 (alias) + + + +
battery-1 (alias) + + + +
battery-2 (alias) + + + +
battery-3 (alias) + + + +
battery-4 (alias) + + + +
battery-empty + + + +
battery-full + + + +
battery-half + + + +
battery-quarter + + + +
battery-three-quarters + + + +
bed + + + +
beer + + + +
bell + + + +
bell-o + + + +
bell-slash + + + +
bell-slash-o + + + +
bicycle + + + +
binoculars + + + +
birthday-cake + + + +
bluetooth + + + +
bluetooth-b + + + +
bolt + + + +
bomb + + + +
book + + + +
bookmark + + + +
bookmark-o + + + +
briefcase + + + +
bug + + + +
building + + + +
building-o + + + +
bullhorn + + + +
bullseye + + + +
bus + + + +
cab (alias) + + + +
calculator + + + +
calendar + + + +
calendar-check-o + + + +
calendar-minus-o + + + +
calendar-o + + + +
calendar-plus-o + + + +
calendar-times-o + + + +
camera + + + +
camera-retro + + + +
car + + + +
caret-square-o-down + + + +
caret-square-o-left + + + +
caret-square-o-right + + + +
caret-square-o-up + + + +
cart-arrow-down + + + +
cart-plus + + + +
cc + + + +
certificate + + + +
check + + + +
check-circle + + + +
check-circle-o + + + +
check-square + + + +
check-square-o + + + +
child + + + +
circle + + + +
circle-o + + + +
circle-o-notch + + + +
circle-thin + + + +
clock-o + + + +
clone + + + +
close (alias) + + + +
cloud + + + +
cloud-download + + + +
cloud-upload + + + +
code + + + +
code-fork + + + +
coffee + + + +
cog + + + +
cogs + + + +
comment + + + +
comment-o + + + +
commenting + + + +
commenting-o + + + +
comments + + + +
comments-o + + + +
compass + + + +
copyright + + + +
creative-commons + + + +
credit-card + + + +
credit-card-alt + + + +
crop + + + +
crosshairs + + + +
cube + + + +
cubes + + + +
cutlery + + + +
dashboard (alias) + + + +
database + + + +
desktop + + + +
diamond + + + +
dot-circle-o + + + +
download + + + +
edit (alias) + + + +
ellipsis-h + + + +
ellipsis-v + + + +
envelope + + + +
envelope-o + + + +
envelope-square + + + +
eraser + + + +
exchange + + + +
exclamation + + + +
exclamation-circle + + + +
exclamation-triangle + + + +
external-link + + + +
external-link-square + + + +
eye + + + +
eye-slash + + + +
eyedropper + + + +
fax + + + +
feed (alias) + + + +
female + + + +
fighter-jet + + + +
file-archive-o + + + +
file-audio-o + + + +
file-code-o + + + +
file-excel-o + + + +
file-image-o + + + +
file-movie-o (alias) + + + +
file-pdf-o + + + +
file-photo-o (alias) + + + +
file-picture-o (alias) + + + +
file-powerpoint-o + + + +
file-sound-o (alias) + + + +
file-video-o + + + +
file-word-o + + + +
file-zip-o (alias) + + + +
film + + + +
filter + + + +
fire + + + +
fire-extinguisher + + + +
flag + + + +
flag-checkered + + + +
flag-o + + + +
flash (alias) + + + +
flask + + + +
folder + + + +
folder-o + + + +
folder-open + + + +
folder-open-o + + + +
frown-o + + + +
futbol-o + + + +
gamepad + + + +
gavel + + + +
gear (alias) + + + +
gears (alias) + + + +
gift + + + +
glass + + + +
globe + + + +
graduation-cap + + + +
group (alias) + + + +
hand-grab-o (alias) + + + +
hand-lizard-o + + + +
hand-paper-o + + + +
hand-peace-o + + + +
hand-pointer-o + + + +
hand-rock-o + + + +
hand-scissors-o + + + +
hand-spock-o + + + +
hand-stop-o (alias) + + + +
hashtag + + + +
hdd-o + + + +
headphones + + + +
heart + + + +
heart-o + + + +
heartbeat + + + +
history + + + +
home + + + +
hotel (alias) + + + +
hourglass + + + +
hourglass-1 (alias) + + + +
hourglass-2 (alias) + + + +
hourglass-3 (alias) + + + +
hourglass-end + + + +
hourglass-half + + + +
hourglass-o + + + +
hourglass-start + + + +
i-cursor + + + +
image (alias) + + + +
inbox + + + +
industry + + + +
info + + + +
info-circle + + + +
institution (alias) + + + +
key + + + +
keyboard-o + + + +
language + + + +
laptop + + + +
leaf + + + +
legal (alias) + + + +
lemon-o + + + +
level-down + + + +
level-up + + + +
life-bouy (alias) + + + +
life-buoy (alias) + + + +
life-ring + + + +
life-saver (alias) + + + +
lightbulb-o + + + +
line-chart + + + +
location-arrow + + + +
lock + + + +
magic + + + +
magnet + + + +
mail-forward (alias) + + + +
mail-reply (alias) + + + +
mail-reply-all (alias) + + + +
male + + + +
map + + + +
map-marker + + + +
map-o + + + +
map-pin + + + +
map-signs + + + +
meh-o + + + +
microphone + + + +
microphone-slash + + + +
minus + + + +
minus-circle + + + +
minus-square + + + +
minus-square-o + + + +
mobile + + + +
mobile-phone (alias) + + + +
money + + + +
moon-o + + + +
mortar-board (alias) + + + +
motorcycle + + + +
mouse-pointer + + + +
music + + + +
navicon (alias) + + + +
newspaper-o + + + +
object-group + + + +
object-ungroup + + + +
paint-brush + + + +
paper-plane + + + +
paper-plane-o + + + +
paw + + + +
pencil + + + +
pencil-square + + + +
pencil-square-o + + + +
percent + + + +
phone + + + +
phone-square + + + +
photo (alias) + + + +
picture-o + + + +
pie-chart + + + +
plane + + + +
plug + + + +
plus + + + +
plus-circle + + + +
plus-square + + + +
plus-square-o + + + +
power-off + + + +
print + + + +
puzzle-piece + + + +
qrcode + + + +
question + + + +
question-circle + + + +
quote-left + + + +
quote-right + + + +
random + + + +
recycle + + + +
refresh + + + +
registered + + + +
remove (alias) + + + +
reorder (alias) + + + +
reply + + + +
reply-all + + + +
retweet + + + +
road + + + +
rocket + + + +
rss + + + +
rss-square + + + +
search + + + +
search-minus + + + +
search-plus + + + +
send (alias) + + + +
send-o (alias) + + + +
server + + + +
share + + + +
share-alt + + + +
share-alt-square + + + +
share-square + + + +
share-square-o + + + +
shield + + + +
ship + + + +
shopping-bag + + + +
shopping-basket + + + +
shopping-cart + + + +
sign-in + + + +
sign-out + + + +
signal + + + +
sitemap + + + +
sliders + + + +
smile-o + + + +
soccer-ball-o (alias) + + + +
sort + + + +
sort-alpha-asc + + + +
sort-alpha-desc + + + +
sort-amount-asc + + + +
sort-amount-desc + + + +
sort-asc + + + +
sort-desc + + + +
sort-down (alias) + + + +
sort-numeric-asc + + + +
sort-numeric-desc + + + +
sort-up (alias) + + + +
space-shuttle + + + +
spinner + + + +
spoon + + + +
square + + + +
square-o + + + +
star + + + +
star-half + + + +
star-half-empty (alias) + + + +
star-half-full (alias) + + + +
star-half-o + + + +
star-o + + + +
sticky-note + + + +
sticky-note-o + + + +
street-view + + + +
suitcase + + + +
sun-o + + + +
support (alias) + + + +
tablet + + + +
tachometer + + + +
tag + + + +
tags + + + +
tasks + + + +
taxi + + + +
television + + + +
terminal + + + +
thumb-tack + + + +
thumbs-down + + + +
thumbs-o-down + + + +
thumbs-o-up + + + +
thumbs-up + + + +
ticket + + + +
times + + + +
times-circle + + + +
times-circle-o + + + +
tint + + + +
toggle-down (alias) + + + +
toggle-left (alias) + + + +
toggle-off + + + +
toggle-on + + + +
toggle-right (alias) + + + +
toggle-up (alias) + + + +
trademark + + + +
trash + + + +
trash-o + + + +
tree + + + +
trophy + + + +
truck + + + +
tty + + + +
tv (alias) + + + +
umbrella + + + +
university + + + +
unlock + + + +
unlock-alt + + + +
unsorted (alias) + + + +
upload + + + +
user + + + +
user-plus + + + +
user-secret + + + +
user-times + + + +
users + + + +
video-camera + + + +
volume-down + + + +
volume-off + + + +
volume-up + + + +
warning (alias) + + + +
wheelchair + + + +
wifi + + + +
wrench + + + + + + + + + Hand Icons + + + + +
hand-grab-o (alias) + + + +
hand-lizard-o + + + +
hand-o-down + + + +
hand-o-left + + + +
hand-o-right + + + +
hand-o-up + + + +
hand-paper-o + + + +
hand-peace-o + + + +
hand-pointer-o + + + +
hand-rock-o + + + +
hand-scissors-o + + + +
hand-spock-o + + + +
hand-stop-o (alias) + + + +
thumbs-down + + + +
thumbs-o-down + + + +
thumbs-o-up + + + +
thumbs-up + + + + + + + + + Transportation Icons + + + + +
ambulance + + + +
automobile (alias) + + + +
bicycle + + + +
bus + + + +
cab (alias) + + + +
car + + + +
fighter-jet + + + +
motorcycle + + + +
plane + + + +
rocket + + + +
ship + + + +
space-shuttle + + + +
subway + + + +
taxi + + + +
train + + + +
truck + + + +
wheelchair + + + + + + + + Gender Icons + + + + +
genderless + + + +
intersex (alias) + + + +
mars + + + +
mars-double + + + +
mars-stroke + + + +
mars-stroke-h + + + +
mars-stroke-v + + + +
mercury + + + +
neuter + + + +
transgender + + + +
transgender-alt + + + +
venus + + + +
venus-double + + + +
venus-mars + + + + + + + + File Type Icons + + + + +
file + + + +
file-archive-o + + + +
file-audio-o + + + +
file-code-o + + + +
file-excel-o + + + +
file-image-o + + + +
file-movie-o (alias) + + + +
file-o + + + +
file-pdf-o + + + +
file-photo-o (alias) + + + +
file-picture-o (alias) + + + +
file-powerpoint-o + + + +
file-sound-o (alias) + + + +
file-text + + + +
file-text-o + + + +
file-video-o + + + +
file-word-o + + + +
file-zip-o (alias) + + + + + + + + Spinner Icons + + +
    +
  • + + These icons work great with the fa-spin class. +
  • +
+
+ + +
+
circle-o-notch + + + +
cog + + + +
gear (alias) + + + +
refresh + + + +
spinner + + + + + + + + Form Control Icons + + + + +
check-square + + + +
check-square-o + + + +
circle + + + +
circle-o + + + +
dot-circle-o + + + +
minus-square + + + +
minus-square-o + + + +
plus-square + + + +
plus-square-o + + + +
square + + + +
square-o + + + + + + + + Payment Icons + + + + +
cc-amex + + + +
cc-diners-club + + + +
cc-discover + + + +
cc-jcb + + + +
cc-mastercard + + + +
cc-paypal + + + +
cc-stripe + + + +
cc-visa + + + +
credit-card + + + +
credit-card-alt + + + +
google-wallet + + + +
paypal + + + + + + + + Chart Icons + + + + +
area-chart + + + +
bar-chart + + + +
bar-chart-o (alias) + + + +
line-chart + + + +
pie-chart + + + + + + + + Currency Icons + + + + +
bitcoin (alias) + + + +
btc + + + +
cny (alias) + + + +
dollar (alias) + + + +
eur + + + +
euro (alias) + + + +
gbp + + + +
gg + + + +
gg-circle + + + +
ils + + + +
inr + + + +
jpy + + + +
krw + + + +
money + + + +
rmb (alias) + + + +
rouble (alias) + + + +
rub + + + +
ruble (alias) + + + +
rupee (alias) + + + +
shekel (alias) + + + +
sheqel (alias) + + + +
try + + + +
turkish-lira (alias) + + + +
usd + + + +
won (alias) + + + +
yen (alias) + + + + + + + + Text Editor Icons + + + + +
align-center + + + +
align-justify + + + +
align-left + + + +
align-right + + + +
bold + + + +
chain (alias) + + + +
chain-broken + + + +
clipboard + + + +
columns + + + +
copy (alias) + + + +
cut (alias) + + + +
dedent (alias) + + + +
eraser + + + +
file + + + +
file-o + + + +
file-text + + + +
file-text-o + + + +
files-o + + + +
floppy-o + + + +
font + + + +
header + + + +
indent + + + +
italic + + + +
link + + + +
list + + + +
list-alt + + + +
list-ol + + + +
list-ul + + + +
outdent + + + +
paperclip + + + +
paragraph + + + +
paste (alias) + + + +
repeat + + + +
rotate-left (alias) + + + +
rotate-right (alias) + + + +
save (alias) + + + +
scissors + + + +
strikethrough + + + +
subscript + + + +
superscript + + + +
table + + + +
text-height + + + +
text-width + + + +
th + + + +
th-large + + + +
th-list + + + +
underline + + + +
undo + + + +
unlink (alias) + + + + + + + + Directional Icons + + + + +
angle-double-down + + + +
angle-double-left + + + +
angle-double-right + + + +
angle-double-up + + + +
angle-down + + + +
angle-left + + + +
angle-right + + + +
angle-up + + + +
arrow-circle-down + + + +
arrow-circle-left + + + +
arrow-circle-o-down + + + +
arrow-circle-o-left + + + +
arrow-circle-o-right + + + +
arrow-circle-o-up + + + +
arrow-circle-right + + + +
arrow-circle-up + + + +
arrow-down + + + +
arrow-left + + + +
arrow-right + + + +
arrow-up + + + +
arrows + + + +
arrows-alt + + + +
arrows-h + + + +
arrows-v + + + +
caret-down + + + +
caret-left + + + +
caret-right + + + +
caret-square-o-down + + + +
caret-square-o-left + + + +
caret-square-o-right + + + +
caret-square-o-up + + + +
caret-up + + + +
chevron-circle-down + + + +
chevron-circle-left + + + +
chevron-circle-right + + + +
chevron-circle-up + + + +
chevron-down + + + +
chevron-left + + + +
chevron-right + + + +
chevron-up + + + +
exchange + + + +
hand-o-down + + + +
hand-o-left + + + +
hand-o-right + + + +
hand-o-up + + + +
long-arrow-down + + + +
long-arrow-left + + + +
long-arrow-right + + + +
long-arrow-up + + + +
toggle-down (alias) + + + +
toggle-left (alias) + + + +
toggle-right (alias) + + + +
toggle-up (alias) + + + + + + + + Video Player Icons + + + + +
arrows-alt + + + +
backward + + + +
compress + + + +
eject + + + +
expand + + + +
fast-backward + + + +
fast-forward + + + +
forward + + + +
pause + + + +
pause-circle + + + +
pause-circle-o + + + +
play + + + +
play-circle + + + +
play-circle-o + + + +
random + + + +
step-backward + + + +
step-forward + + + +
stop + + + +
stop-circle + + + +
stop-circle-o + + + +
youtube-play + + + + + + + + Brand Icons + + +

Warning!

+ Apparently, Adblock Plus can remove Font Awesome brand icons with their "Remove Social + Media Buttons" setting. We will not use hacks to force them to display. Please + report an issue with Adblock Plus if + you believe this to be + an error. To work around this, you'll need to modify the social icon class names. +
+ + +
+
500px + + + +
adn + + + +
amazon + + + +
android + + + +
angellist + + + +
apple + + + +
behance + + + +
behance-square + + + +
bitbucket + + + +
bitbucket-square + + + +
bitcoin (alias) + + + +
black-tie + + + +
bluetooth + + + +
bluetooth-b + + + +
btc + + + +
buysellads + + + +
cc-amex + + + +
cc-diners-club + + + +
cc-discover + + + +
cc-jcb + + + +
cc-mastercard + + + +
cc-paypal + + + +
cc-stripe + + + +
cc-visa + + + +
chrome + + + +
codepen + + + +
codiepie + + + +
connectdevelop + + + +
contao + + + +
css3 + + + +
dashcube + + + +
delicious + + + +
deviantart + + + +
digg + + + +
dribbble + + + +
dropbox + + + +
drupal + + + +
edge + + + +
empire + + + +
expeditedssl + + + +
facebook + + + +
facebook-f (alias) + + + +
facebook-official + + + +
facebook-square + + + +
firefox + + + +
flickr + + + +
fonticons + + + +
fort-awesome + + + +
forumbee + + + +
foursquare + + + +
ge (alias) + + + +
get-pocket + + + +
gg + + + +
gg-circle + + + +
git + + + +
git-square + + + +
github + + + +
github-alt + + + +
github-square + + + +
gittip (alias) + + + +
google + + + +
google-plus + + + +
google-plus-square + + + +
google-wallet + + + +
gratipay + + + +
hacker-news + + + +
houzz + + + +
html5 + + + +
instagram + + + +
internet-explorer + + + +
ioxhost + + + +
joomla + + + +
jsfiddle + + + +
lastfm + + + +
lastfm-square + + + +
leanpub + + + +
linkedin + + + +
linkedin-square + + + +
linux + + + +
maxcdn + + + +
meanpath + + + +
medium + + + +
mixcloud + + + +
modx + + + +
odnoklassniki + + + +
odnoklassniki-square + + + +
opencart + + + +
openid + + + +
opera + + + +
optin-monster + + + +
pagelines + + + +
paypal + + + +
pied-piper + + + +
pied-piper-alt + + + +
pinterest + + + +
pinterest-p + + + +
pinterest-square + + + +
product-hunt + + + +
qq + + + +
ra (alias) + + + +
rebel + + + +
reddit + + + +
reddit-alien + + + +
reddit-square + + + +
renren + + + +
safari + + + +
scribd + + + +
sellsy + + + +
share-alt + + + +
share-alt-square + + + +
shirtsinbulk + + + +
simplybuilt + + + +
skyatlas + + + +
skype + + + +
slack + + + +
slideshare + + + +
soundcloud + + + +
spotify + + + +
stack-exchange + + + +
stack-overflow + + + +
steam + + + +
steam-square + + + +
stumbleupon + + + +
stumbleupon-circle + + + +
tencent-weibo + + + +
trello + + + +
tripadvisor + + + +
tumblr + + + +
tumblr-square + + + +
twitch + + + +
twitter + + + +
twitter-square + + + +
usb + + + +
viacoin + + + +
vimeo + + + +
vimeo-square + + + +
vine + + + +
vk + + + +
wechat (alias) + + + +
weibo + + + +
weixin + + + +
whatsapp + + + +
wikipedia-w + + + +
windows + + + +
wordpress + + + +
xing + + + +
xing-square + + + +
y-combinator + + + +
y-combinator-square (alias) + + + +
yahoo + + + +
yc (alias) + + + +
yc-square (alias) + + + +
yelp + + + +
youtube + + + +
youtube-play + + + +
youtube-square + + + +
    +
  • All brand icons are trademarks of their respective owners.
  • +
  • The use of these trademarks does not indicate endorsement of the trademark holder by Font Awesome, + nor vice versa. +
  • +
  • Brand icons should only be used to represent the company or product to which they refer.
  • +
+
+ + + + + Medical Icons + + + + +
ambulance + + + +
h-square + + + +
heart + + + +
heart-o + + + +
heartbeat + + + +
hospital-o + + + +
medkit + + + +
plus-square + + + +
stethoscope + + + +
user-md + + + +
wheelchair + + + + + + + + ); + } +} + +export default FontAwesome; diff --git a/src/views/Icons/FontAwesome/FontAwesome.test.js b/src/views/Icons/FontAwesome/FontAwesome.test.js new file mode 100644 index 0000000..977d642 --- /dev/null +++ b/src/views/Icons/FontAwesome/FontAwesome.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import FontAwesome from './FontAwesome'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); \ No newline at end of file diff --git a/src/views/Icons/FontAwesome/package.json b/src/views/Icons/FontAwesome/package.json new file mode 100644 index 0000000..0b3cfaf --- /dev/null +++ b/src/views/Icons/FontAwesome/package.json @@ -0,0 +1,6 @@ +{ + "name": "FontAwesome", + "version": "0.0.0", + "private": true, + "main": "./FontAwesome.js" +} diff --git a/src/views/Icons/SimpleLineIcons/SimpleLineIcons.js b/src/views/Icons/SimpleLineIcons/SimpleLineIcons.js new file mode 100644 index 0000000..7c7f03e --- /dev/null +++ b/src/views/Icons/SimpleLineIcons/SimpleLineIcons.js @@ -0,0 +1,755 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; + +class SimpleLineIcons extends Component { + render() { + return ( +
+ + + Simple Line Icons + + + + +
+ icon-user + + + + icon-people + + + + icon-user-female + + + + icon-user-follow + + + + icon-user-following + + + + icon-user-unfollow + + + + icon-login + + + + icon-logout + + + + icon-emotsmile + + + + icon-phone + + + + icon-call-end + + + + icon-call-in + + + + icon-call-out + + + + icon-map + + + + icon-location-pin + + + + icon-direction + + + + icon-directions + + + + icon-compass + + + + icon-layers + + + + icon-menu + + + + icon-list + + + + icon-options-vertical + + + + icon-options + + + + icon-arrow-down + + + + icon-arrow-left + + + + icon-arrow-right + + + + icon-arrow-up + + + + icon-arrow-up-circle + + + + icon-arrow-left-circle + + + + icon-arrow-right-circle + + + + icon-arrow-down-circle + + + + icon-check + + + + icon-clock + + + + icon-plus + + + + icon-close + + + + icon-trophy + + + + icon-screen-smartphone + + + + icon-screen-desktop + + + + icon-plane + + + + icon-notebook + + + + icon-mustache + + + + icon-mouse + + + + icon-magnet + + + + icon-energy + + + + icon-disc + + + + icon-cursor + + + + icon-cursor-move + + + + icon-crop + + + + icon-chemistry + + + + icon-speedometer + + + + icon-shield + + + + icon-screen-tablet + + + + icon-magic-wand + + + + icon-hourglass + + + + icon-graduation + + + + icon-ghost + + + + icon-game-controller + + + + icon-fire + + + + icon-eyeglass + + + + icon-envelope-open + + + + icon-envelope-letter + + + + icon-bell + + + + icon-badge + + + + icon-anchor + + + + icon-wallet + + + + icon-vector + + + + icon-speech + + + + icon-puzzle + + + + icon-printer + + + + icon-present + + + + icon-playlist + + + + icon-pin + + + + icon-picture + + + + icon-handbag + + + + icon-globe-alt + + + + icon-globe + + + + icon-folder-alt + + + + icon-folder + + + + icon-film + + + + icon-feed + + + + icon-drop + + + + icon-drawer + + + + icon-docs + + + + icon-doc + + + + icon-diamond + + + + icon-cup + + + + icon-calculator + + + + icon-bubbles + + + + icon-briefcase + + + + icon-book-open + + + + icon-basket-loaded + + + + icon-basket + + + + icon-bag + + + + icon-action-undo + + + + icon-action-redo + + + + icon-wrench + + + + icon-umbrella + + + + icon-trash + + + + icon-tag + + + + icon-support + + + + icon-frame + + + + icon-size-fullscreen + + + + icon-size-actual + + + + icon-shuffle + + + + icon-share-alt + + + + icon-share + + + + icon-rocket + + + + icon-question + + + + icon-pie-chart + + + + icon-pencil + + + + icon-note + + + + icon-loop + + + + icon-home + + + + icon-grid + + + + icon-graph + + + + icon-microphone + + + + icon-music-tone-alt + + + + icon-music-tone + + + + icon-earphones-alt + + + + icon-earphones + + + + icon-equalizer + + + + icon-like + + + + icon-dislike + + + + icon-control-start + + + + icon-control-rewind + + + + icon-control-play + + + + icon-control-pause + + + + icon-control-forward + + + + icon-control-end + + + + icon-volume-1 + + + + icon-volume-2 + + + + icon-volume-off + + + + icon-calendar + + + + icon-bulb + + + + icon-chart + + + + icon-ban + + + + icon-bubble + + + + icon-camrecorder + + + + icon-camera + + + + icon-cloud-download + + + + icon-cloud-upload + + + + icon-envelope + + + + icon-eye + + + + icon-flag + + + + icon-heart + + + + icon-info + + + + icon-key + + + + icon-link + + + + icon-lock + + + + icon-lock-open + + + + icon-magnifier + + + + icon-magnifier-add + + + + icon-magnifier-remove + + + + icon-paper-clip + + + + icon-paper-plane + + + + icon-power + + + + icon-refresh + + + + icon-reload + + + + icon-settings + + + + icon-star + + + + icon-symbol-female + + + + icon-symbol-male + + + + icon-target + + + + icon-credit-card + + + + icon-paypal + + + + icon-social-tumblr + + + + icon-social-twitter + + + + icon-social-facebook + + + + icon-social-instagram + + + + icon-social-linkedin + + + + icon-social-pinterest + + + + icon-social-github + + + + icon-social-gplus + + + + icon-social-reddit + + + + icon-social-skype + + + + icon-social-dribbble + + + + icon-social-behance + + + + icon-social-foursqare + + + + icon-social-soundcloud + + + + icon-social-spotify + + + + icon-social-stumbleupon + + + + icon-social-youtube + + + + icon-social-dropbox + + + + + + + ); + } +} + +export default SimpleLineIcons; diff --git a/src/views/Icons/SimpleLineIcons/SimpleLineIcons.test.js b/src/views/Icons/SimpleLineIcons/SimpleLineIcons.test.js new file mode 100644 index 0000000..b775582 --- /dev/null +++ b/src/views/Icons/SimpleLineIcons/SimpleLineIcons.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import SimpleLineIcons from './SimpleLineIcons'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); \ No newline at end of file diff --git a/src/views/Icons/SimpleLineIcons/package.json b/src/views/Icons/SimpleLineIcons/package.json new file mode 100644 index 0000000..a3aeb4a --- /dev/null +++ b/src/views/Icons/SimpleLineIcons/package.json @@ -0,0 +1,6 @@ +{ + "name": "SimpleLineIcons", + "version": "0.0.0", + "private": true, + "main": "./SimpleLineIcons.js" +} diff --git a/src/views/Icons/index.js b/src/views/Icons/index.js new file mode 100644 index 0000000..098b863 --- /dev/null +++ b/src/views/Icons/index.js @@ -0,0 +1,8 @@ +import CoreUIIcons from './CoreUIIcons'; +import Flags from './Flags'; +import FontAwesome from './FontAwesome'; +import SimpleLineIcons from './SimpleLineIcons'; + +export { + CoreUIIcons, Flags, FontAwesome, SimpleLineIcons +}; diff --git a/src/views/LayerSwitcherExample/LayerSwitcherExample.js b/src/views/LayerSwitcherExample/LayerSwitcherExample.js new file mode 100644 index 0000000..4fbd6cd --- /dev/null +++ b/src/views/LayerSwitcherExample/LayerSwitcherExample.js @@ -0,0 +1,62 @@ +import * as React from 'react'; + +import OlMap from 'ol/Map'; +import OlView from 'ol/View'; +import OlLayerTile from 'ol/layer/Tile'; +import OlSourceStamen from 'ol/source/Stamen'; +import OlSourceOsm from 'ol/source/OSM'; + +import { + LayerSwitcher +} from '@terrestris/react-geo'; + +class LayerSwitcherExample extends React.Component { + + constructor(props) { + + super(props); + + this.mapDivId = `map-${Math.random()}`; + + this.layers = [ + new OlLayerTile({ + name: 'OSM', + source: new OlSourceOsm() + }), + new OlLayerTile({ + name: 'Stamen', + source: new OlSourceStamen({ + layer: 'watercolor' + }) + }), + ]; + + this.map = new OlMap({ + view: new OlView({ + center: [801045, 6577113], + zoom: 9 + }), + layers: this.layers + }); + } + + componentDidMount() { + this.map.setTarget(this.mapDivId); + } + + render() { + return( +
+ +
+ ) + } +} + +export default LayerSwitcherExample; \ No newline at end of file diff --git a/src/views/LayerSwitcherExample/package.json b/src/views/LayerSwitcherExample/package.json new file mode 100644 index 0000000..a30155d --- /dev/null +++ b/src/views/LayerSwitcherExample/package.json @@ -0,0 +1,6 @@ +{ + "name": "LayerSwitcherExample", + "version": "0.0.0", + "private": true, + "main": "./LayerSwitcherExample.js" +} diff --git a/src/views/Layers/Layer.js b/src/views/Layers/Layer.js new file mode 100644 index 0000000..055333e --- /dev/null +++ b/src/views/Layers/Layer.js @@ -0,0 +1,908 @@ +import React, { Component, Suspense, Fragment } from 'react' +import { Link } from 'react-router-dom'; +import './Layers.css' +import { API_LAYER_LIST, API_LAYER_DETAIL, API_LAYER_ATTRIBUTE, API_LAYER_LABEL, API_LAYER_SEARCH_LABEL, + API_LAYER_DETAIL_UPDATE, API_LAYER_ATTRIBUTE_UPDATE, API_LAYER_SEARCH_LABEL_UPDATE } from '../../const/ApiConst.js'; +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import cellEditFactory, { Type } from 'react-bootstrap-table2-editor'; +import { AppSwitch } from '@coreui/react'; +import { UncontrolledTooltip } from 'reactstrap'; +import { Badge, Card, CardBody, CardHeader, Col, Row, Table, Button, + Modal, ModalHeader, ModalBody, ModalFooter, + Form, FormGroup, Label, Input, FormText, + TabContent, TabPane, Nav, NavItem, NavLink, +} from 'reactstrap'; +import { Icon, InlineIcon } from '@iconify/react'; +import createOutline from '@iconify/icons-ion/create-outline'; +import archiveOutline from '@iconify/icons-ion/archive-outline'; +import addCircleOutline from '@iconify/icons-ion/add-circle-outline'; +import saveOutline from '@iconify/icons-ion/save-outline'; +import classnames from 'classnames'; +import { findWhere } from 'underscore'; +import SweetAlert from 'react-bootstrap-sweetalert'; + +const { SearchBar } = Search; +const { ExportCSVButton } = CSVExport; + +const layers = [ + { + "id": 1, + "layer_name": "Tanah Sekolah", + "image_src": 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa1d%20text%20%7B%20fill%3A%23555%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa1d%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.921875%22%20y%3D%22218.3%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + "image_altText": 'Slide 1', + }, + { + "id": 2, + "layer_name": "Tanah Kantor Desa", + "image_src": 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa20%20text%20%7B%20fill%3A%23444%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa20%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23666%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22247.3203125%22%20y%3D%22218.3%22%3ESecond%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + "image_altText": 'Slide 2', + }, + { + "id": 3, + "layer_name": "Tanah Pasar / Pertokoan", + "image_src": 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa21%20text%20%7B%20fill%3A%23333%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa21%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23555%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22277%22%20y%3D%22218.3%22%3EThird%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + "image_altText": 'Slide 3', + } +] + +const dataColumns = [{ + dataField: 'id', + text: 'Id' +}]; + +class Layer extends Component { + constructor(props) { + super(props); + this.state = { + layer_id: null, + layer_details: [], + layerAttribute: [], + columnsAttribute: [ + { + dataField: 'attribute', + text: 'Attribute', + sort: true, + editable: false + }, + { + dataField: 'attribute_label', + text: 'Attribute Label', + sort: true + }, + { + dataField: 'attribute_type', + text: 'Attribute Type', + sort: true, + editable: false + }, + { + dataField: 'description', + text: 'Description', + sort: true + }, + // { + // dataField: 'visible', + // text: 'Visible', + // sort: true, + // // editor: { + // // type: Type.CHECKBOX, + // // value: 'true:false' + // // } + // }, + // { + // dataField: 'display_order', + // text: 'display_order', + // sort: true, + // editable: false + // }, + ], + // columns: [], + dataAttribute: [], + selectRow: { + mode: 'checkbox', + clickToSelect: true, + clickToEdit: false, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e), + }, + editCellMode: false, + onSelectAllMode: false, + selectedRows: [], + activeTab: '1', + modalEditLayerDetailsVisible: false, + layer_details_contents: [], + updateAttributeBtnVisible: false, + alertResponse: false, + successAlert: false, + dangerAlert: false, + alertResponseMsg: false, + alertConfirm: false, + alertConfirmBtnText: false, + alertConfirmType: '', + alertConfirmMsg: false, + confirmParam: '', + selectedSearchLabelId: '', + searchLabelData: [] + } + } + + componentDidMount() { + // this.getLayerList(); + // console.log(this.props.match.params.id); + this.setState({layer_id: this.props.match.params.id}, () => { + const { layer_id } = this.state; + this.getLayerDetails(layer_id); + this.getLayerAttribute(layer_id); + this.getLayerSearchLabel(layer_id); + }); + } + + componentDidUpdate(prevProps, prevState) { + // this.state.selectedRowData && !editCellMode + /*if ((prevState.dataAttribute !== this.state.dataAttribute) && (this.state.editCellMode == true)) { + this.setState({updateAttributeBtnVisible: true}); + }*/ + } + + loading = () =>
Loading...
+ + toggleActiveTab = (tab) => { + const {activeTab} = this.state; + if (activeTab !== tab) { + this.setState({activeTab: tab}); + } + } + + /*getLayerList = async () => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_LIST, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + this.setState({layers: result.data}, () => { + console.log(this.state.layers); + }); + } + } else { + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + } + }*/ + + getLayerDetails = async(layer_id) => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_DETAIL+layer_id, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + /*let layer_details_contents = { + "Layer Title (label on map)": result.data[0].title_en, + "Layer Name": result.data[0].name, + "Abstract": result.data[0].abstract_en, + "Workspace": result.data[0].workspace, + "Data Store": result.data[0].store + }*/ + let layer_details_contents = [ + { + "name": "layer_title", + "label": "Layer Title (label on Map)", + "value": result.data[0].title_en, + "type": "text", + "disabled": false + }, + { + "name": "layer_name", + "label": "Layer Name", + "value": result.data[0].name, + "type": "text", + "disabled": true + }, + { + "name": "abstract", + "label": "Abstract", + "value": result.data[0].abstract_en, + "type": "text", + "disabled": false + }, + { + "name": "workspace", + "label": "Workspace", + "value": result.data[0].workspace, + "type": "text", + "disabled": true + }, + { + "name": "datastore", + "label": "Data Store", + "value": result.data[0].store, + "type": "text", + "disabled": true + }, + ] + this.setState({layer_details: result.data, layer_details_contents: layer_details_contents}, () => { + // console.log('layer_details', this.state.layer_details); + // console.log('layer_details_contents', this.state.layer_details_contents); + }); + } + } else { + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + } + } + + getLayerAttribute = async(layer_id) => { + // const { layer_id } = this.state; + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_ATTRIBUTE+layer_id, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + + let columns = []; + /*attribute: "tgl_pembuk" + description: null + attribute_label: null + attribute_type: "xsd:string" + visible: "t" + display_order: "15" + */ + let data = []; + + /*for (let key in result.data[0]) { + columns.push({ + dataField: key, + text: key, + sort: true + }) + }*/ + for (let i=0; i < result.data.length; i++) { + data.push({ + "attribute": result.data[i].attribute, + "attribute_label": result.data[i].attribute_label, + "attribute_type": result.data[i].attribute_type, + "description": result.data[i].description, + "visible": result.data[i].visible, + // "display_order": result.data[i].display_order + }); + } + + console.log('result data getLayerAttribute', result.data); + + this.setState({ + // columns: result.data.length !== 0 ? columns:dataColumns, + dataAttribute: result.data + }) + + // this.setState({layerAttribute: result.data}, () => { + // console.log(this.state.layerAttribute); + // const { layerAttribute } = this.state; + + // }); + } + } else { + + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + } + } + + getLayerSearchLabel = async(layer_id) => { + const { selectedSearchLabelId } = this.state; + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL+layer_id, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + console.log('getLayerSearchLabel result', result); + this.setState({ + selectedSearchLabelId: result.data[0].attribute_id, + searchLabelData: result.data + }) + } + } else { + + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + } + } + + onSelectRowTable = () => { + + } + + onSelectAllRowTable = () => { + + } + + toggleEditCell = () => { + this.setState({ + editCellMode: !this.state.editCellMode, + updateAttributeBtnVisible: !this.state.updateAttributeBtnVisible + }); + /*let { editCellMode } = this.state; + console.log("editCellMode ", editCellMode) + // when editCellMode === true -> false (deactive) + editCellMode ? + this.setState({ + editCellMode: false, + selectRow: { + mode: 'checkbox', + clickToSelect: true, + clickToEdit: false, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e), + }, + selectedRows: [] + }) : + // when editCellMode === false -> true (activate) + this.setState({ + editCellMode: true, + selectRow: { + mode: 'checkbox', + clickToSelect: false, + clickToEdit: true, + bgColor: '#e4e5e6', + onSelect: (row, isSelect, rowIndex, e) => this.onSelectRowTable({row, isSelect, e, rowIndex}), + onSelectAll: (isSelect, rows, e) => this.onSelectAllRowTable(isSelect, rows, e), + } + });*/ + } + + handleChangeInputLayerDetails = (idx, event) => { + let { layer_details_contents } = this.state; + let changedItem = findWhere(layer_details_contents, {name: idx}); + let value = event.target.value; + changedItem.value = value; + console.log('changedItem', changedItem); + this.setState({layer_details_contents: layer_details_contents}); + console.log('layer_details_contents', this.state.layer_details_contents); + } + + handleChangeSelectSearchLabel = (event) => { + const { selectedSearchLabelId } = this.state; + let value = event.target.value; + this.setState({selectedSearchLabelId: value}, () => { + console.log(this.state.selectedSearchLabelId); + }) + } + + renderModalEditLayerDetails = () => { + + const {layer_details_contents} = this.state + return ( +
+ this.toggleEditLayerDetails()} > + this.toggleEditLayerDetails()}>Edit Layer Details + +
+ { + layer_details_contents.length > 0 ? + layer_details_contents.map((item, i) => { + return ( + + + {item.disabled ? + + : + + } + + + ) + }) + : null + } +
+
+ + + + {' '} + +
+
+ ) + } + + editLayerDetails = () => { + this.toggleEditLayerDetails(); + } + + toggleEditLayerDetails = () => { + this.setState({modalEditLayerDetailsVisible: !this.state.modalEditLayerDetailsVisible}); + } + + // updateLayerDetail = () => { + + // } + + updateAttribute = () => { + this.setState({confirmParam: 'update_attribute'}, () => this.showConfirm(this.state.confirmParam)); + } + + updateSearchLabel = () => { + const { selectedSearchLabelId } = this.state; + if (selectedSearchLabelId === '') { + this.showWarning('empty_selectedSearchLabelId'); + } + else { + this.setState({confirmParam: 'update_search_label'}, () => this.showConfirm(this.state.confirmParam)); + } + + } + + showWarning = (param) => { + if (param === 'empty_selectedSearchLabelId') { + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: 'Please select the attribute label'}); + } + } + + showConfirm = (param) => { + if (param === 'update_attribute') { + this.setState({alertConfirm: true, alertConfirmBtnText: 'Update', alertConfirmType: 'success', alertConfirmMsg: 'Save the updates'}); + } + else if (param === 'update_search_label') { + this.setState({alertConfirm: true, alertConfirmBtnText: 'Update', alertConfirmType: 'success', alertConfirmMsg: 'Update Search Label'}); + } + } + + confirmOnClose = () => { + this.setState({alertConfirm: false, alertConfirmBtnText: '', alertConfirmType: '', alertConfirmMsg: '', confirmParam: ''}); + } + + closeAlertResponse = () => { + this.setState({ + alertResponse: false, successAlert: false, dangerAlert: false, alertResponseMsg: '', + // saveMode: false, editCellMode: false, updatedData: [] + }); + } + + confirmYes = async () => { + const { confirmParam, dataAttribute } = this.state; + if (confirmParam === 'update_attribute') { + console.log('dataAttribute', dataAttribute); + this.updateAttributePost(); + this.confirmOnClose(); + } + else if (confirmParam === 'update_search_label') { + this.updateSearchLabelPost(); + this.confirmOnClose(); + } + } + + updateLayerDetailPost = async () => { + const { layer_details_contents, layer_id } = this.state; + let payload = { + "layer_id": layer_id, + "layer_detail": { + "title_en": findWhere(layer_details_contents, {name: "layer_title"}).value, + "abstract_en": findWhere(layer_details_contents, {name: "abstract"}).value, + } + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(payload) + } + + console.log('updateLayerDetailPost param', param); + + try { + const result = await fetch(API_LAYER_DETAIL_UPDATE, param).then(response => response.json()).then(res => res) + console.log('updateLayerDetailPost result',result); + if (result.code_type == 'success'){ + this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: result.code_message}, () => { + this.getLayerDetails(layer_id); + this.toggleEditLayerDetails(); + }); + } else if (result.code_type == 'error') { + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: result.code_message}); + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: err.message.toString()}); + } + } + + updateAttributePost = async () => { + const { dataAttribute, layer_id } = this.state; + let payload = { + "layer_id": layer_id, + "layer_attribute": dataAttribute + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(payload) + } + console.log('updateAttributePost param',param); + + try { + const result = await fetch(API_LAYER_ATTRIBUTE_UPDATE, param).then(response => response.json()).then(res => res) + // console.log('updateAttributePost result',result); + if (result.code_type == 'success') { + this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: result.code_message}, () => { + this.getLayerAttribute(layer_id); + this.toggleEditCell(); + }); + } else if (result.code_type == 'error') { + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: result.code_message}); + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: err.message.toString()}); + } + } + + updateSearchLabelPost = async () => { + const { selectedSearchLabelId, layer_id } = this.state; + let payload = { + "layer_id": layer_id, + "attribute_id": selectedSearchLabelId + } + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(payload) + } + console.log('updateAttributePost param',param); + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL_UPDATE, param).then(response => response.json()).then(res => res) + // console.log('updateAttributePost result',result); + if (result.code_type == 'success') { + this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: result.code_message}, () => { + this.getLayerSearchLabel(layer_id); + // this.toggleEditCell(); + }); + } else if (result.code_type == 'error') { + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: result.code_message}); + } + } catch(err) { + console.log(err); + this.setState({alertResponse: true, successAlert: false, dangerAlert: true, alertResponseMsg: err.message.toString()}); + } + + // this.setState({alertResponse: true, successAlert: true, dangerAlert: false, alertResponseMsg: "Search Label has been updated"}); + + } + + renderLayerDetails = () => { + const { layer_details } = this.state; + + if (layer_details.length > 0) { + return( + + {this.renderLayerDetailsContent()} + +
+
+ +
+ + + + ) + } + else { + return null; + } + } + + renderLayerDetailsContent = () => { + const { layer_details_contents } = this.state; + return layer_details_contents.map((item, i) => { + return( + + + {item.label} + + + : {item.value} + + + ) + }) + } + + renderLayerAttribute = () => { + const { columnsAttribute, dataAttribute, editCellMode, selectedRows, selectRow } = this.state; + return( + + { + props => ( +
+ +
+
+ +
+ + +
+ + + { this.state.updateAttributeBtnVisible ? +
+ + + Update Attribute + +
+ : null + } + +
+ Edit
+ + this.toggleEditCell()} checked={editCellMode} /> +
+ +
+ {/*{selectedRows.length > 0 && !editCellMode && }*/} + {/* + + + + Export CSV + */} +
+
+
+ + +
+ + { editCellMode ? + this.updateFunction(oldValue, newValue, row, column), + }) } + /> : + + } +
+ + ) + } + + ) + } + + + renderSearchLabel = () => { + return ( + + + + + + + + {this.renderSearchLabelSelect()} + + + + + + +
+ +
+ + + + ) + } + + renderSearchLabelSelect = () => { + const { dataAttribute } = this.state; + const { searchLabelData } = this.state; + console.log('searchLabelData', searchLabelData); + return dataAttribute.map((item, index) => { + if (searchLabelData.length > 0) { + if (searchLabelData[0].attribute_id === item.id) { + if (item.attribute_label !== null && item.attribute_label !== '') { + return + } + else { + if (item.attribute !== 'the_geom') { + return + } + } + } + else { + if (item.attribute_label !== null && item.attribute_label !== '') { + return + } + else { + if (item.attribute !== 'the_geom') { + return + } + } + } + } + else { + if (item.attribute_label !== null && item.attribute_label !== '') { + return + } + else { + if (item.attribute !== 'the_geom') { + return + } + } + } + }) + } + + render() { + const { layer_details, layer_details_contents, columnsAttribute, activeTab, + alertResponse, successAlert, dangerAlert, alertResponseMsg, + alertConfirm, alertConfirmBtnText, alertConfirmType, alertConfirmMsg, confirmParam } = this.state; + console.log('layer_details_contents', layer_details_contents); + return ( + + this.closeAlertResponse()}> + {alertResponseMsg} + + + this.confirmYes()} + onCancel={() => this.confirmOnClose()} + focusCancelBtn + > + {alertConfirmMsg} + + + + { this.renderModalEditLayerDetails() } + + + + + +

{ layer_details_contents.length > 0 ? layer_details_contents[0].value : "Layer Title" }

+
+ + + + + + + + + { this.renderLayerDetails() } + + + + + + + { columnsAttribute.length > 0 ? this.renderLayerAttribute() : null } + + + + + + + { this.renderSearchLabel() } + + + + + + + + + + + + + + + ) + } +} + +export default Layer; \ No newline at end of file diff --git a/src/views/Layers/Layers.css b/src/views/Layers/Layers.css new file mode 100644 index 0000000..8e23307 --- /dev/null +++ b/src/views/Layers/Layers.css @@ -0,0 +1,62 @@ +.layer-list { + padding: 10px 10px; + /*background-color: #ffffff;*/ + margin-bottom: 15px; + border-radius: 10px; + /*border-color: #000000;*/ + /*border-width: 3px;*/ +} + +.img-wrap { + width: 225px; + height: 150px; + position: relative; + display: inline-block; + overflow: hidden; + margin: 0; + border-radius: 10px; +} + +.layer-image { + display: block; + position: absolute; + top: 50%; + left: 50%; + min-height: 100%; + min-width: 100%; + transform: translate(-50%, -50%); +} + +.layer-title { + font-size: 18px; + font-weight: bold; +} + +.switch-button-edit { + margin-top: -5px; + margin-right: 20px; +} + +.layer-details-button-group { + float: right; + right: 0; +} + +.modal-body-container{ + max-height: 300px; + overflow: auto; +} + +.option-button-wrapper { + margin-right: 10px; + margin-top: 5px; +} + +.input-search-layer-wrapper { + margin-bottom: 10px; +} + +.input-search-layer { + padding: 10px; + padding-bottom: 10px; +} \ No newline at end of file diff --git a/src/views/Layers/Layers.js b/src/views/Layers/Layers.js new file mode 100644 index 0000000..e35b460 --- /dev/null +++ b/src/views/Layers/Layers.js @@ -0,0 +1,172 @@ +import React, { Component, Suspense } from 'react' +import { Row, Col, Card, CardBody, Input } from 'reactstrap' +import { Link } from 'react-router-dom'; +import './Layers.css' +import { API_LAYER_LIST, API_LAYER_LABEL } from '../../const/ApiConst.js'; +import { layerThumbnailUrl } from '../../const/MapConst.js'; + +const layers = [ + { + "id": 1, + "layer_name": "Tanah Sekolah", + "image_src": 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa1d%20text%20%7B%20fill%3A%23555%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa1d%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.921875%22%20y%3D%22218.3%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + "image_altText": 'Slide 1', + }, + { + "id": 2, + "layer_name": "Tanah Kantor Desa", + "image_src": 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa20%20text%20%7B%20fill%3A%23444%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa20%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23666%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22247.3203125%22%20y%3D%22218.3%22%3ESecond%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + "image_altText": 'Slide 2', + }, + { + "id": 3, + "layer_name": "Tanah Pasar / Pertokoan", + "image_src": 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa21%20text%20%7B%20fill%3A%23333%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa21%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23555%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22277%22%20y%3D%22218.3%22%3EThird%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', + "image_altText": 'Slide 3', + } +] + +class Layers extends Component { + constructor(props) { + super(props); + this.state = { + layers: [], + searchInput: '' + } + } + + componentDidMount() { + this.getLayerList(); + } + + loading = () =>
Loading...
+ + getLayerList = async () => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + let layers = []; + + try { + const result = await fetch(API_LAYER_LIST, param).then(response => response.json()).then(res => res) + // console.log(result); + if (result.data){ + if (result.data.length > 0) { + // for(let i=0; i < result.data.length; i++) { + + // } + this.setState({layers: result.data}, () => { + // console.log(this.state.layers); + }); + } + } else { + // this.setState({chartTypeGeometry: doughnut}); + } + } catch(err) { + console.log(err); + alert(err.message.toString()); + // this.setState({alert: true, messageAlert: err.message.toString(), successAlert: false, dangerAlert: true}) + } + } + + handleChangeSearchLayer = (event) => { + event.preventDefault(); + let value = event.target.value.toLowerCase(); + this.setState({searchInput: value}, () => { + this.returnSearchData(this.state.searchInput); + }); + } + + returnSearchData = (value) => { + let filteredFeatures = []; + if (value === '') { + return this.state.layers; + } + else { + filteredFeatures = this.state.layers.filter((result) => { + /*if (result.id.toLowerCase().includes(this.state.searchInput)) { + return result.id.toLowerCase().includes(this.state.searchInput); + } + else { + for (let key in result.properties) { + if (result.properties[key] !== null ) { + if (result.properties[key].toString().toLowerCase().includes(this.state.searchInput)) { + return result.properties[key].toString().toLowerCase().includes(this.state.searchInput); + } + } + } + }*/ + for (let key in result) { + if (result[key] !== null) { + if (result[key].toString().toLowerCase().includes(this.state.searchInput)) { + return result[key].toString().toLowerCase().includes(this.state.searchInput); + } + } + } + // console.log(result); + }); + return filteredFeatures; + } + } + + renderList = () => { + let searchData = this.returnSearchData(this.state.searchInput); + console.log('searchData', searchData); + if (searchData.length > 0) { + return searchData.map((item, index) => { + let layerDetailUrl = `layers/${item.resourcebase_ptr_id}`; + let layerThumbnailSrc = layerThumbnailUrl(item.name); + return ( + + + + + + +
+ {item.image_altText} +
+ + + {item.title_en} + + + + + + + ) + }) + } + else { + // return No layer found + return ( + + No layer found + + ) + } + } + + render() { + const { searchInput } = this.state; + return ( + + + +
+ +
+ + + + { this.renderList() } + + + ) + } +} + +export default Layers; \ No newline at end of file diff --git a/src/views/Layers/package.json b/src/views/Layers/package.json new file mode 100644 index 0000000..c2595a3 --- /dev/null +++ b/src/views/Layers/package.json @@ -0,0 +1,6 @@ +{ + "name": "Layers", + "version": "0.0.0", + "private": true, + "main": "./Layers.js" +} diff --git a/src/views/Map/CustomScroll.css b/src/views/Map/CustomScroll.css new file mode 100644 index 0000000..c26a064 --- /dev/null +++ b/src/views/Map/CustomScroll.css @@ -0,0 +1,19 @@ +.custom-scroll::-webkit-scrollbar-track { + /*-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);*/ + border-radius: 10px; + background-color: #F5F5F5; +} + +.custom-scroll::-webkit-scrollbar { + width: 10px; + height: 10px; + background-color: #F5F5F5; +} + +.custom-scroll::-webkit-scrollbar-thumb { + border-radius: 5px; + /*-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);*/ + -webkit-box-shadow: 0 0 3px rgba(0,0,0,0.3); + /*background-color: #ccc;*/ + background-color: #eaeaea; +} \ No newline at end of file diff --git a/src/views/Map/Map.css b/src/views/Map/Map.css new file mode 100644 index 0000000..3a689a1 --- /dev/null +++ b/src/views/Map/Map.css @@ -0,0 +1,270 @@ +.App { + text-align: center; +} + +.map { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; +} + +.App-logo { + animation: App-logo-spin infinite 20s linear; + height: 40vmin; + pointer-events: none; +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +/*Override openlayers css*/ +.ol-zoom { + /*left: inherit !important;*/ + /*right: 10px !important;*/ + font-size: 20px; + left: 10px !important; + top: 10px !important; + display: none !important; +} + +.ol-zoom-in { + margin-bottom: 2px !important; +} + +.ol-control { + background-color: inherit !important; +} + +.ol-control button { + background-color: #1890ff !important; + /*min-width: 32px !important;*/ + padding-right: 0 !important; + padding-left: 0 !important; + text-align: center !important; + border-radius: 50% !important; +} + +.ol-control button:hover, .ol-control button:focus { + color: #fff !important; + background-color: #40a9ff !important; + border-color: #ffffff !important; + outline: 0; +} + +.ol-mouse-position { + top: inherit !important; + right: inherit !important; + left: 13px; + position: fixed !important; + bottom: 5px !important; + display: none !important; +} + +.ol-scale-line { + bottom: 25px !important; + left: 13px !important; + display: none !important; +} + +.ol-popup { + /*display: none;*/ +} + +#popup-closer { + cursor: pointer; +} + +.print-img { + visibility: hidden; +} + +@media print +{ + .no-print, .no-print * + { + display: none !important; + } +} + +.loader-container { + text-align: center; + align-content: center; + top: 100px; + position: fixed; + z-index: 999999; + align-self: center; + margin-left: 45%; + +} + +.site-drawer-render-in-current-wrapper { + /* position: relative; */ + height: 200px; + /* padding: 48px; */ + overflow: hidden; + text-align: center; + background: #fafafa; + border: 1px solid #ebedf0; + border-radius: 2px; +} + +.first-row { + height: calc(100vh - (100vh / 2)); +} + +.full-row { + height: 100vh; +} + +.remove-gap-bs-card { + margin-bottom: 0 !important; + width: 100%; +} + +.panel-title { + font-weight: bold; + text-align: center; + font-size: 14px; + padding: 4px; +} + +.not-left-col { + padding-left: 0px !important; + /* margin-left: -15px !important; */ + /* margin-right: 15px !important; */ + padding-right: 0px !important +} + +.carousel-control-prev > .carousel-control-prev-icon { + background-color: #282c34 !important; +} +.carousel-control-next > .carousel-control-next-icon { + background-color: #282c34 !important; +} + +.chart-doughnut-carousel-item { + height: 300px; +} + +.chart-doughnut-carousel-item-max { + height: 600px; +} + +.chart-doughnut-carousel { + height: 300px; + /*top: -10px;*/ +} + +.chart-doughnut-carousel-max { + height: 600px; +} + +.chart-doughnut-caption { + height: 50px; + margin-bottom: -50px; + top: -100px; +} + +.chart-doughnut-chart { + height: 200px; + margin-top: 30px; +} + +.chart-doughnut-chart-max { + height: 400px; + margin-top: 30px; +} + +.no-padding { + padding: 0px !important; +} + +.small-padding { + padding: 4px !important; +} + +.table-sp-container { + justify-content: center; + /* align-items: center; */ + /* display: flex; + justify-content: center; */ + width: 100%; + height: 100%; +} + +.mb-caption { + margin-bottom: -15px; +} + +.table-sp { + margin: 0 auto; + /* align-self: center; */ +} + +.td-sp { + border: 0.5px solid black; + /* height: 50px; */ + border-right: solid 0.5px black; + border-left: solid 0.5px black; +} + +.td-key-sp { + width: 60%; + font-weight: bold; + padding-right: 5px; + padding-left: 5px; +} + +.td-value-sp { + width: 40%; + text-align: right; + padding-right: 5px; + padding-left: 5px; +} + +.text-red { + color: red; +} + +.text-green { + color: green; +} + +.text-orange { + color: orange; +} + +.text-bold { + font-weight: bold; +} + +.text-center { + text-align: center; +} + +.bg-blue { + background-color: #9BC1E6; +} \ No newline at end of file diff --git a/src/views/Map/Map.js b/src/views/Map/Map.js new file mode 100644 index 0000000..1f08201 --- /dev/null +++ b/src/views/Map/Map.js @@ -0,0 +1,2568 @@ +import React, { Component, Suspense, Fragment } from 'react'; +import ReactDOM from 'react-dom'; +import { Container, Col, Row, Button, UncontrolledTooltip, + Card, + CardBody, + CardHeader } from 'reactstrap'; +import './Map.css'; +import './Popup.css'; +import './CustomScroll.css'; +import 'ol/ol.css'; +import 'antd/dist/antd.css'; +import './react-geo.css'; + +import OlMap from 'ol/Map'; +import OlView from 'ol/View'; +import OlLayerTile from 'ol/layer/Tile'; +import ImageLayer from 'ol/layer/Image'; +import OlSourceOsm from 'ol/source/OSM'; +import OlSourceTileJson from 'ol/source/TileJSON'; +import OlLayerGroup from 'ol/layer/Group'; +import OlSourceTileWMS from 'ol/source/TileWMS'; +import OlSourceImageWMS from 'ol/source/ImageWMS'; +// import OlLayerSwitcher from 'ol/control/LayerSwitcher'; +import {fromLonLat, transformExtent, transform} from 'ol/proj'; +import {Vector as VectorSource, XYZ as XYZSource, Cluster} from 'ol/source'; +import Overlay from 'ol/Overlay'; +import { Draw, Select, Modify } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Circle as CircleStyle, Fill, Stroke, Style, Text, Icon as IconOl} from 'ol/style'; +import {defaults as defaultControls, MousePosition, ScaleLine} from 'ol/control'; +import {createStringXY, toStringXY} from 'ol/coordinate'; +import GeoJSON from 'ol/format/GeoJSON'; +import WMSCapabilities from 'ol/format/WMSCapabilities'; + +import { Drawer } from 'antd'; +import { + SimpleButton, + MapComponent, + NominatimSearch, + MeasureButton, + // LayerTree, + MapProvider, + mappify, + onDropAware, + // AddWmsPanel +} from '@terrestris/react-geo'; + +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import LayerSwitcher from '@terrestris/react-geo/dist/LayerSwitcher/LayerSwitcher'; +import axios from 'axios'; +import PopupContainer from '../../components/PopupContainer'; +import { + AppHeader +} from '@coreui/react'; +import MapHeader from '../../components/MapHeader'; +import MapToolbar from '../../components/MapToolbar'; +import DrawingTool from '../../components/DrawingTool'; +import ImagePopup from '../../components/ImagePopup'; +import ImageSlider from '../../components/ImageSlider'; +import RoutingBar from '../../components/RoutingBar'; +// import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import { appConfig, setRequestMapHeader, layerStyleUrl, BMD_DENPASAR_MAPSERVICE_URL, IU_MAPSERVICE_URL, MAP_ID, emptyConstants, + WMS_CAPABILITIES_URL_2 } from '../../const/MapConst.js'; +import { Icon } from '@iconify/react'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import country_indonesia from '../../assets/json/indonesia.json'; +import { test, getGeomType, updateMap, getLayerAttribute, getLayerColor, getRandomColor } from '../../const/GeoserverFunc.js'; +import { API_UPDATE_MAP, API_LOAD_MAP, API_LAYER_SEARCH_LABEL, API_GET_CHART_KATEGORI, + USERPROYEK_SEARCH, DASHBOARD_STATUS_SEARCH, DASHBOARD_PROYEK_SEARCH } from '../../const/ApiConst.js'; +import { getSalesRoutingApi, getSalesFeatures, getOfficeFeatures, getCustomerFeatures, getEmployeeFeatures, getEmployeeRoutingApi, getWaspangRoutingApi } from '../../const/GeohrApiFunc.js'; +import { SALES_FEATURES_STYLE, CUSTOMER_FEATURES_STYLE, OFFICE_FEATURES_STYLE, ROUTE_MAP_STYLES, EMPLOYEE_FEATURES_STYLE, PROJECT_FEATURES_STYLE, WASPANG_FEATURES_STYLE } from '../../const/GeohrMapStyles.js'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import moment from "moment"; +import salesGeojson from '../../dummy_data/sales.geojson'; +import routeDummy from '../../dummy_data/route2.json'; +import { demografiTree, analisaTree } from '../../const/LayerTreeConst.js' +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import pinRouteStart from '../../assets/img/map/pin_route_green.png'; +import pinRouteEnd from '../../assets/img/map/pin_route_red.png'; +import domtoimage from 'dom-to-image'; +import Loader from 'react-loader-spinner' +import "react-loader-spinner/dist/loader/css/react-spinner-loader.css" +import * as alasql from 'alasql'; +import * as lodash from 'lodash'; +// import LineChart from './LineChart' +// import PieChart from './PieChart' +import { Pie, Line } from 'react-chartjs-2'; +import numeral from 'numeral'; + +const MappifiedNominatimSearch = mappify(NominatimSearch); +const MappifiedMeasureButton = mappify(MeasureButton); +// const MappifiedLayerTree = mappify(LayerTree); +const Map = mappify(onDropAware(MapComponent)); + +// const center = [ 788453.4890155146, 6573085.729161344 ]; +const projection = 'EPSG:3857'; //default +const projection4326 = 'EPSG:4326'; +// const projection = 'EPSG:4326'; // lat long +// Indonesia +// const lat = -2.6000285; +// const lon = 118.015776; +// const zoom = 5; +const lat = -0.1240; +const lon = 103.5173; +const zoom = 4; +const Indonesia = new fromLonLat([lon, lat], projection); +const Bali_bbox = [115.178638994694, -8.71934970794214, 115.269238650799, -8.59763413248024]; + +const osmLayer = new OlLayerTile({ + source: new OlSourceOsm(), + name: 'OSM', + type: 'base', + imageName: 'osm.PNG' +}); + +const esriLayer = new OlLayerTile({ + name: "ESRI", + source: new XYZSource({ + url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + projection: projection, + maxZoom: 18 + }), + type: 'base', + imageName: 'esri.PNG' +}); + +const googleLayer = new OlLayerTile({ + name: "Google", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 18 + }), + type: 'base', + imageName: 'google.PNG' +}) + +const googleStreetLayer = new OlLayerTile({ + name: "Google Street", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 18 + }), + type: 'base', + imageName: 'google_street.PNG' +}) + + +// let baseLayers = [ +// osmLayer, +// esriLayer, +// // googleLayer, +// // googleStreetLayer, +// denpasarLayer, +// citraDenpasarLayer, +// iuLayerGroup +// ]; + +let baseLayers = []; + +if (localStorage.getItem('u_group') == 'kominfo') { + baseLayers = [ + osmLayer, + esriLayer, + googleLayer, + googleStreetLayer, + // denpasarLayer, + // citraDenpasarLayer, + // iuLayerGroup + ]; +} +else { + baseLayers = [ + osmLayer, + esriLayer, + googleLayer, + googleStreetLayer, + // denpasarLayer, + // citraDenpasarLayer, + // iuLayerGroup + ]; +} + +const baseLayerGroup = new OlLayerGroup({ + name: 'Base Layers', + layers: [ + osmLayer, + esriLayer + ], + type: 'baseGroup' +}); + +const defaultPersentaseProyek = { + labels: [], + datasets: [ + { + label: 'Perencanaan', + data: [], + fill: false, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgba(255, 99, 132, 0.2)', + yAxisID: 'y-axis-1', + }, + { + label: 'Realisasi', + data: [], + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultCostProyek = { + labels: [], + datasets: [ + { + label: 'Perencanaan', + data: [], + fill: false, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgba(255, 99, 132, 0.2)', + yAxisID: 'y-axis-1', + }, + { + label: 'Realisasi', + data: [], + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultStatusProyek = { + labels: ['Aman', 'Alert', 'Critical'], + datasets: [ + { + label: '# of Votes', + data: [], + backgroundColor: [ + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(255, 99, 132, 0.2)', + ], + borderColor: [ + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(255, 99, 132, 1)', + ], + borderWidth: 1, + }, + ], +}; + +const defaultStatusWaspang = { + labels: ['Hadir', 'Izin', 'Panic Button'], + datasets: [ + { + label: '# of Votes', + data: [], + backgroundColor: [ + 'rgba(77,232,0, 0.2)', + 'rgba(255,146,3, 0.2)', + 'rgba(164,7,120, 0.2)', + ], + borderColor: [ + 'rgba(77,232,0, 1)', + 'rgba(255,146,3, 1)', + 'rgba(164,7,120, 1)', + ], + borderWidth: 1, + }, + ], +}; + +class SiopasMap extends Component { + constructor(props) { + super(props); + + this.state = { + mapZoom: null, + mapCenter: null, + mapProjection: null, + drawerLayerVisible: false, + drawerSearchVisible: false, + wmsLayers: [], + totalLayerHasFeature: 0, + countGetFeature: 0, + countNotGetFeature: 0, + popupDataTemp: [], + activeListFeatureId: '', + evtCoordinate: null, + visibleLS: false, + visibleLSProp: 'hide', + popupRightVisible: false, + imagePopupVisible: false, + alert: false, + successAlert: false, + dangerAlert: false, + messageAlert: "", + editGeometryVisible: false, + layerNameDraw: '', + geomTypeDraw: '', + activeStateAddGeometry: false, + layer_attribute: [], + searchLabelData: [], + layerInfo: [], + // LayerTree Panel + checkedKeysSales: [], + checkedKeysCustomer: [], + checkedKeysOffice: [], + checkedKeysDemografi: [], + checkedKeysAnalisa: [], + checkedKeysEmployeeDivision: [], + checkedKeysProjectTree: [], + salesGroupTree: null, + employeeDivisionTree: null, + projectTree: null, + routingBarVisible: false, + isSearchingRoute: false, + isProcessing: false, + queryBuilderOutput:'', + queryBuilderType:'', + currentQbTree:'', + currentQbType:'', + routeType: '', + chosenProyekIds: [], + dataStatusProyek: null, + dataPersentaseProyek: null, + dataCostProyek: null, + dataStatusWaspang: null, + waspangData: [], + planning: 0, + realisasi: 0, + status_project: { + good: 0, + warning: 0, + critical: 0 + }, + waspang_status: { + presensi: 0, + absensi: 0 + }, + panic_button: 0, + statusRight:true, + proggressBottom:true + }; + + this.layers = [ + osmLayer, + // layerGroupSekolah, + // layerGroupRS, + // layerGroupKantorDesa, + // layerGroupSarPras, + // tanah_kantor_instansi_pemerintah, + // point_sekolah, + // polygon_rumah_sakit + // paxel + // testLayer + ]; + + this.overlay = new Overlay({ + element: document.getElementById('popup'), + autoPan: true, + autoPanAnimation: { + duration: 250 + } + }); + + this.scaleLineControl = new ScaleLine({ + units: 'metric', + bar: true, + steps: 4, + text: true, + // minWidth: 140 + }) + + this.mousePositionControl = new MousePosition({ + coordinateFormat: createStringXY(4), + projection: 'EPSG:4326', + // className: 'custom-mouse-position', + target: document.getElementById('custom-mouse-position'), + }); + + this.olmap = new OlMap({ + view: new OlView({ + center: Indonesia, + zoom: zoom, + projection: projection + }), + layers: this.layers, + overlays: [this.overlay], + controls: defaultControls().extend([this.scaleLineControl, this.mousePositionControl]) + }); + + this.changeBaseLayer = this.changeBaseLayer.bind(this); + this.openPopupRight = this.openPopupRight.bind(this); + this.closePopupRight = this.closePopupRight.bind(this); + + this.projectFeatures = []; + this.waspangFeatures = []; + this.chosenProyekTemp = []; + } + + componentDidMount = () => { + // this.loadMap(); + // setRequestMapHeader(); + // this.getLayerSearchLabel(); + // this.getLayerInfo(); + this.olmap.on("singleclick", (evt) => { + this.mapOnClick(evt); + }); + this.setState({mapProjection: this.olmap.getView().getProjection()}, () => console.log('mapProjection', this.state.mapProjection)); + } + + componentDidUpdate = (prevProps, prevState) => { + + if (!this.state.popupRightVisible && this.state.popupDataTemp.length > 0) { + // this.showPopup(); + this.openPopupRight(); + // this.setActiveListFeature(); + // console.log('popupDataTemp > 0'); + // console.log('popupDataTemp ', this.state.popupDataTemp); + } + else if (this.state.popupRightVisible && this.state.popupDataTemp.length < 1) { + // this.closePopup(); + this.closePopupRight(); + // console.log('popupDataTemp < 1'); + // console.log('popupDataTemp ', this.state.popupDataTemp); + } + + if (prevState.checkedKeysProjectTree !== this.state.checkedKeysProjectTree) { + this.setLayer('checkedKeysProjectTree'); + } + + if (prevState.routingBarVisible !== this.state.routingBarVisible) { + if (!this.state.routingBarVisible) { + this.removeLayerByName('routeLayer'); + } + } + + if (this.state.chosenProyekIds !== prevState.chosenProyekIds) { + this.getDataUserToProyek(); + this.getChartData(); + this.getDailyInfo(); + } + } + + loading = () =>
Loading...
+ + // ngerequest API buat ngepush ke this.waspangFeatures + getDataUserToProyek = async () => { + const { chosenProyekIds } = this.state; + let payload = { + "columns": [], + "joins": [ + { + "name": "m_proyek", + "column_join": "proyek_id", + "column_results": [ + "nama", + "biaya", + "color_progress", + "jumlah_pekerja", + "pic", + "mulai_proyek", + "akhir_proyek" + ] + }, + { + "name": "m_subproyek", + "column_join": "subproyek_id", + "column_results": [ + "nama", + "biaya", + "color_progress", + "jumlah_pekerja", + "pic", + "mulai_proyek", + "akhir_proyek" + ] + }, + { + "name": "m_users", + "column_join": "user_id", + "column_results": [ + "name", + "username", + "email", + "phone_number", + "gender" + ] + } + ], + "orders": { + "columns": [ + "id" + ], + "ascending": true + }, + "paging": { + "start": 0, + "length": 25 + } + } + + if (chosenProyekIds.length > 0) { + payload.columns.push({ + "name": "proyek_id", + "logic_operator": "in", + "value": chosenProyekIds.join(), + "operator": "AND" + }); + } + + // if(parseInt(localStorage.getItem('role_id'))!==1){ + // payload.columns.push( + // { + // "name": "id", + // "logic_operator": "=", + // "value": localStorage.getItem('proyek_id'), + // "operator": "AND" + // } + // ) + // } + + const config = { + method: 'POST', // *GET, POST, PUT, DELETE, etc. + // mode: 'cors', // no-cors, *cors, same-origin + // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+window.localStorage.getItem('token') + // 'Content-Type': 'application/x-www-form-urlencoded', + }, + // redirect: 'follow', // manual, *follow, error + // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: JSON.stringify(payload) // body data type must match "Content-Type" header + } + + const result = await fetch(USERPROYEK_SEARCH, config).then(response => response.json()).then(res => res); + if (result && result.code == 200) { + // this.getWaspangFeatures(result.data); + this.setState({waspangData: result.data}, () => this.getWaspangFeatures()); + } else { + toast.error('Gagal Mengambil Data!!'); + } + } + + // ngerequest API buat ngepush data chart + // (dataStatusProyek, + // dataPersentaseProyek, + // dataCostProyek, + // dataStatusWaspang) + getChartData = async () => { + const { chosenProyekIds } = this.state; + let payload = { + "columns": [], + "orders": { + "columns": [ + "id" + ], + "ascending": true + } + } + + if (chosenProyekIds.length > 0) { + payload.columns.push({ + "name": "id", + "logic_operator": "in", + "value": chosenProyekIds.join(), + "operator": "AND" + }); + } + + // if(parseInt(localStorage.getItem('role_id'))!==1){ + // payload.columns.push( + // { + // "name": "id", + // "logic_operator": "=", + // "value": localStorage.getItem('proyek_id'), + // "operator": "AND" + // } + // ) + // } + + const config = { + method: 'POST', // *GET, POST, PUT, DELETE, etc. + // mode: 'cors', // no-cors, *cors, same-origin + // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+window.localStorage.getItem('token') + // 'Content-Type': 'application/x-www-form-urlencoded', + }, + // redirect: 'follow', // manual, *follow, error + // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: JSON.stringify(payload) // body data type must match "Content-Type" header + } + + const result = await fetch(DASHBOARD_STATUS_SEARCH, config).then(response => response.json()).then(res => res); + if (result && result.code == 200) { + const { persentase_progress_plan, persentase_progress_actual, progress_cost_planning, progress_cost_realisasi, status_proyek } = result.data + + const labelPersentaseProyek = persentase_progress_plan ? persentase_progress_plan.map(res => res.label) : [] + const valuePersentaseProyekPlan = persentase_progress_plan ? persentase_progress_plan.map(res => res.total) : [] + const valuePersentaseProyekActual = persentase_progress_actual ? persentase_progress_actual.map(res => res.total) : [] + const persentaseProyek = { + labels: labelPersentaseProyek, + datasets: [ + { + label: 'Perencanaan', + data: valuePersentaseProyekPlan, + fill: false, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgba(255, 99, 132, 0.2)', + yAxisID: 'y-axis-1', + }, + { + label: 'Realisasi', + data: valuePersentaseProyekActual, + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], + }; + this.setState({dataPersentaseProyek: persentaseProyek}); + + const labelCostPlaning = progress_cost_planning ? progress_cost_planning.map(res => res.label) : [] + const valueCostPlaning = progress_cost_planning ? progress_cost_planning.map(res => res.total) : [] + const valueCostRealisasi = progress_cost_realisasi ? progress_cost_realisasi.map(res => res.total) : [] + + const costProyek = { + labels: labelCostPlaning, + datasets: [ + { + label: 'Perencanaan', + data: valueCostPlaning, + fill: false, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgba(255, 99, 132, 0.2)', + yAxisID: 'y-axis-1', + }, + { + label: 'Realisasi', + data: valueCostRealisasi, + fill: false, + backgroundColor: 'rgb(54, 162, 235)', + borderColor: 'rgba(54, 162, 235, 0.2)', + yAxisID: 'y-axis-1', + }, + ], + }; + this.setState({dataCostProyek: costProyek}); + + const valueStatusProyek = status_proyek ? status_proyek.map(res => res.total) : [] + + const statusProyek = { + labels: ['Aman', 'Alert', 'Critical'], + datasets: [ + { + label: '# of Votes', + data: valueStatusProyek, + backgroundColor: [ + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(255, 99, 132, 0.2)', + ], + borderColor: [ + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(255, 99, 132, 1)', + ], + borderWidth: 1, + }, + ], + }; + this.setState({dataStatusProyek: statusProyek}); + } else { + toast.error('Gagal Mengambil Data!!'); + } + } + + getDailyInfo = async () => { + const payload = { + "columns": [ + {"name": "created_at","logic_operator": "range","value": `${moment().format('YYYY-MM-DD')} 00:00:00`,"value1": `${moment().format('YYYY-MM-DD')} 23:59:59`,"operator": "AND"} + ], + "paging": {"start": 0,"length": -1} + } + + const config = { + method: 'POST', // *GET, POST, PUT, DELETE, etc. + // mode: 'cors', // no-cors, *cors, same-origin + // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+window.localStorage.getItem('token') + // 'Content-Type': 'application/x-www-form-urlencoded', + }, + // redirect: 'follow', // manual, *follow, error + // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: JSON.stringify(payload) // body data type must match "Content-Type" header + } + try { + const result = await fetch(DASHBOARD_PROYEK_SEARCH, config).then(response => response.json()).then(res => res); + console.log('getDailyInfo result', result); + if (result.code === 200 && result.data) { + const dataSum = result.data; + // this.setState({ + // planning: dataSum.planning, + // realisasi: dataSum.realisasi, + // status_project: dataSum.status_project, + // waspang_status: dataSum.waspang_status, + // panic_button: dataSum.panic_button, + // isReady: true + // }); + + const statusWaspang = { + labels: ['Hadir', 'Izin', 'Panic Button'], + datasets: [ + { + label: '# of Votes', + data: [dataSum.waspang_status.presensi, dataSum.waspang_status.absensi, dataSum.panic_button], + backgroundColor: [ + 'rgba(77,232,0, 0.2)', + 'rgba(255,146,3, 0.2)', + 'rgba(164,7,120, 0.2)', + ], + borderColor: [ + 'rgba(77,232,0, 1)', + 'rgba(255,146,3, 1)', + 'rgba(164,7,120, 1)', + ], + borderWidth: 1, + }, + ], + }; + this.setState({dataStatusWaspang: statusWaspang}); + + + } + // const result = await axios + // .get(DASHBOARD_PROYEK_SEARCH, config) + // .then(res => res) + // .catch((error) => error.response); + + // console.log('result', result); + + // if (result && result.data && result.data.code == 200) { + // const dataSum = result.data; + // this.setState({ + // planning: dataSum.planning, + // realisasi: dataSum.realisasi, + // status_project: dataSum.status_project, + // waspang_status: dataSum.waspang_status, + // panic_button: dataSum.panic_button, + // isReady: true + // }); + // } + } + catch (e) { + toast.error('Gagal mengambil data'); + // return false; + } + } + + getLayerSearchLabel = async() => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + // console.log('getLayerSearchLabel result', result); + this.setState({ + searchLabelData: result.data + }, () => console.log('getLayerSearchLabel searchLabelData', this.state.searchLabelData)); + } + } else { + + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + // toast.warn(err.message.toString()); + } + } + + + getLayerInfo = async () => { + let layerInfo = []; + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_GET_CHART_KATEGORI, param).then(response => response.json()).then(res => res) + // console.log(result) + + if(result.data){ + if (result.data.length > 0) { + for(let i=0; i < result.data.length; i++) { + let layer_name = result.data[i].layer_name; + let layer_title = result.data[i].layer_title; + let layer_geom_type = result.data[i].layer_geom_type; + let total_features = result.data[i].total_features; + + let SLD_URL = `${layerStyleUrl+layer_name}`;; + let reqColor = await getLayerColor(SLD_URL); + let color = ''; + if (reqColor.success) { + color = reqColor.result; + } + layerInfo.push({ + layer_name: layer_name, + layer_title: layer_title, + layer_geom_type: layer_geom_type, + layer_color: color, + total_features: total_features + }); + } + this.setState({layerInfo: layerInfo}, () => console.log('layerInfo',this.state.layerInfo)); + } + // this.setState({alert: true, messageAlert: result.code_message, successAlert: true, dangerAlert: false}) + } else { + // this.setState({chartKategori: pie}); + // this.setState({alert: true, messageAlert: result.code_message, successAlert: false, dangerAlert: true}) + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + // toast.warn(err.message.toString()); + // this.setState({alert: true, messageAlert: err.message.toString(), successAlert: false, dangerAlert: true}) + } + } + + setDefaultMap = () => { + this.olmap.getView().animate({ + zoom: zoom, + center: Indonesia + }); + } + + changeBaseLayer(item) { + console.log('change baselayer', item); + // console.log(this.olmap.getLayers()); + if (this.olmap.getLayers().values_.length == 0) { + // check if layers empty, so just insert base layer to position 0 + this.olmap.getLayers().insertAt(0, item); + } + // check if layer exist + else if (this.olmap.getLayers().values_.length > 0) { + // check the position 0, if base layer then replace to new + if (this.olmap.getLayers().array_[0].get('type') === 'base') { + this.olmap.getLayers().removeAt(0); + this.olmap.getLayers().insertAt(0, item); + } + // else just insert base layer at position 0 + else { + this.olmap.getLayers().insertAt(0, item); + } + } + } + + getHomeView = () => { + // this should request to siopas map api to get current map + // this.olmap.getView().fit(new transformExtent(Bali_bbox, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + const { mapZoom, mapCenter } = this.state; + if (mapZoom && mapCenter !== null) { + this.olmap.getView().animate({ + zoom: mapZoom, + center: mapCenter + }); + } + else { + // alert("Map Zoom and Map Center are not set yet"); + this.olmap.getView().animate({ + zoom: zoom, + center: Indonesia + }); + } + } + + mapOnClick = (evt) => { + evt.preventDefault(); + let isDrawing = false; + let removedFeature = []; + let isRemoving = false; + let transformedCoords4326 = transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326'); + // console.log('transformedCoords4326', transformedCoords4326); + + let mapInteractions = []; + this.olmap.getInteractions().forEach((interaction) => { + // console.log('interaction', interaction); + if (interaction instanceof Draw) { + console.log('drawing is active!!'); + isDrawing = true; + } + // if (interaction instanceof Select) { + // console.log('select interaction is active'); + // isRemoving = true; + // } + + if (interaction instanceof Modify) { + console.log('modify feature is active'); + isDrawing = true; + } + }); + + if (isDrawing) { + return; + } + + let viewResolution = this.olmap.getView().getResolution(); + let viewProjection = this.olmap.getView().getProjection(); + let url = ''; + let promises = []; + let featureGet = []; + + let hitGeojson = null; + + this.olmap.getLayers().forEach((layer, i) => { + console.log('layer', layer.get('name')); + if (layer.get('type') !== 'base') { + if (layer.get('type') == 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + url = sublayer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + if (url) { + promises.push(axios.get(url)); + } + } + }); + } + else { + if (layer.getVisible()) { + if (layer.get('name') !== 'ChosenLayer') { + if (layer.get('source_type') && layer.get('source_type') === 'geojson') { + let layerSource = layer.getSource(); + hitGeojson = this.olmap.getFeaturesAtPixel(evt.pixel); + console.log('hitGeojson', hitGeojson); + } + else if (layer.get('source_type') === "wms") { + url = layer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + if (url) { + promises.push(axios.get(url)); + } + } + } + } + } + } + }); + + + if (hitGeojson && hitGeojson.length > 0) { + for (let i=0; i < hitGeojson.length; i++) { + let feature = hitGeojson[i]; + + let feat = { + "type": "Feature", + "id": "", + "geometry": { + "type": "", + "coordinates": [] + }, + "geometry_name": "the_geom", + "properties": {} + } + + feat.id = feature.id_; + feat.geometry.type = feature.getGeometry().getType(); + // feat.geometry.coordinates = transform(feature.getGeometry().getCoordinates(), 'EPSG:3857', 'EPSG:4326'); + feat.geometry.coordinates = feature.getGeometry().getCoordinates(); + feat.properties = feature.getProperties(); + delete feat.properties["geometry"]; + + console.log('feat', feat); + featureGet.push(feat); + } + + console.log('featureGet geojson', featureGet); + } + + + // kalo dari WMS + if (promises.length > 0) { + axios.all(promises).then((results) => { + results.forEach((response) => { + console.log('mapOnClick response promises', response); + if (response.data !== undefined) { + if (response.data.features.length > 0) { + for(let i=0; i < response.data.features.length; i++) { + featureGet.push(response.data.features[i]); + } + } + } + }) + + + console.log('featureGet WMS', featureGet); + + // adding it here because it's trapped on axios callback + this.setState({ + popupDataTemp: featureGet, + evtCoordinate: evt.coordinate + }, () => this.setActiveListFeature()); + }); + } + + if (hitGeojson && promises) { + this.setState({ + popupDataTemp: featureGet, + evtCoordinate: evt.coordinate + }, () => this.setActiveListFeature()); + } + } + + removeChosenLayer = () => { + this.olmap.getLayers().forEach((layer, i) => { + if (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'ChosenLayer' ) { + layer.getSource().clear(); + this.olmap.removeLayer(layer); + } + } + }); + } + + openPopupRight() { + // console.log('opening popup right...') + this.setState({popupRightVisible: true}, () => this.setActiveListFeature()); + } + + closePopupRight() { + // console.log('closing popup right...') + this.setState({popupRightVisible: false, popupDataTemp: []}, () => this.setActiveListFeature()); + this.removeChosenLayer(); // selected features + // this.removeLayerByName('routeLayer'); + this.setState({editGeometryVisible: false, routingBarVisible: false}); // disable editing when no ChosenLayer on Map + } + + /*toggleImagePopup() { + this.setState({imagePopupVisible: !this.state.imagePopupVisible}); + }*/ + + setPopupDataTemp = (feature) => { + // console.log('setPopupDataTemp', feature); + this.setState({popupDataTemp: [feature]}, () => this.setActiveListFeature()); + } + + reloadPopupData = () => { + const { evtCoordinate } = this.state; + } + + setActiveListFeature = () => { + // console.log('this.state.popupDataTemp', this.state.popupDataTemp); + if (this.state.popupRightVisible) { + if (this.state.popupDataTemp.length === 1) { + this.setState({activeListFeatureId: this.state.popupDataTemp[0].id}, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + let layerName = this.state.activeListFeatureId ? this.state.activeListFeatureId.substr(0, this.state.activeListFeatureId.indexOf('.')) : ''; + this.getLayerAttribute(layerName); + }) + } + else { + this.setState({activeListFeatureId: ''}, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + }) + } + } + else { + this.setState({activeListFeatureId: ''}, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + }) + } + } + + // getGeomType = async (layerName) => { + + // let res = await getGeomType(layerName); + // console.log('getGeomType', res); + // return res; + // } + + loadMap = async () => { + let response = await axios.get(API_LOAD_MAP).then(res => res).catch(error => error); + console.log('loadMap', response); + // this.getHomeView(response); + // this.olmap.getView().fit(new transformExtent(Bali_bbox, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + if (response.data !== undefined) { + if (response.data.length > 0) { + let dataMap = response.data[0]; + let layersToRemove = []; + + // if the API response about zoom is null + if (dataMap.zoom == null) { + this.setDefaultMap(); + } + // if the API response about center_x and center_y is null + else if (dataMap.center_x == null && dataMap.center_y == null ) { + this.setDefaultMap(); + } + // otherwise, add layer to map from map_layer response + else { + // set the zoom and center + this.setState({mapZoom: dataMap.zoom, mapCenter: [dataMap.center_x, dataMap.center_y]}); + this.olmap.getView().animate({ + zoom: dataMap.zoom, + center: [dataMap.center_x, dataMap.center_y] + }); + + // if map_layers from API is exist + if (dataMap.map_layers.length > 0) { + // first, removing all layers from the current map + this.olmap.getLayers().forEach((layer, i) => { + layersToRemove.push(layer); + }); + if (layersToRemove.length > 0) { + for(let i=0; i < layersToRemove.length; i++) { + this.olmap.removeLayer(layersToRemove[i]); + } + } + + // add map_layers from api to view + if (this.olmap.getLayers().array_.length < 1) { + // console.log('layer empty', response.data); + let map_layers = dataMap.map_layers; + if (map_layers !== undefined) { + // console.log('ada map_layers', map_layers); + if (map_layers.length > 0) { + for (let i=0; i < map_layers.length; i++) { + let newLayer = null; + + // if layer_type is not base + if (map_layers[i].layer_type !== 'base') { + newLayer = new ImageLayer({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new OlSourceImageWMS(map_layers[i].layer_source), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + // if layer_type is base + else { + // if base is OSM (OlSourceOsm); + if (map_layers[i].layer_name == 'OSM') { + newLayer = new OlLayerTile({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new OlSourceOsm(), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + // if base is other than OSM + else { + newLayer = new OlLayerTile({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new XYZSource(map_layers[i].layer_source), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + } + + // console.log('adding new layer', newLayer); + + // add the map_layers from API + this.olmap.addLayer(newLayer); + } + } + } + } + } + } + } + } + } + + saveMap = () => { + /* What map update data that are needed? + - title of map (o) + - zoom + - projection + - center_x + - center_y + - base_layer_id + - mapLayers[] + - layer_name + - layer_type (base / layer) + - layer_geom_type + - layer_source + - layer_visible + - layer_position + + Example of layer_source: + { + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sekolah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sekolah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous' + } + */ + let confirmation = window.confirm('Are you sure you want to save this map?'); + // let mapId = localStorage.getItem('u_group') === "kominfo" ? 2 : 1; + let mapId = MAP_ID; // get from m_group + let mapTitle = localStorage.getItem('u_group')+"_map"; // get from m_group + let mapZoom = this.olmap.getView().getZoom(); + let mapProjection = this.olmap.getView().getProjection().code_; + let mapCenter = this.olmap.getView().getCenter(); + let center_x = mapCenter[0]; // longitude + let center_y = mapCenter[1]; // latitude + let mapLayers = []; + let requestPayload = null; + let layerType = ''; + let layerGeomType = ''; + let count = 0; + + if (confirmation) { + this.olmap.getLayers().forEach( async (layer, i) => { + + // console.log('layer after confirmation i', i, layer); + layerType = layer.get('type') !== undefined ? layer.get('type') : 'layer'; + + if (layer.get('name') !== "DrawingLayer" && layer.get('name') !== "ChosenLayer") { + if (layer.get('type') === "base") { + if (layer.get('name') === "OSM") { + // if the baselayer is OSM, use the OSM function from openlayers + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: 'base', + layer_source: "OlSourceOsm", // i don't know why null in database + layer_visible: layer.getVisible(), + layer_position: i + }); + } + else { + // or if the baselayer is other than OSM (such as ESRI, Google, etc), use the XYZSource + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: 'base', + layer_source: { + // url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + url: layer.getSource().urls[0], + projection: mapProjection, + maxZoom: 18 + }, + layer_visible: layer.getVisible(), + layer_position: i + }); + } + } + else { + // if the layer is not base layer + // layerGeomType = layer.get('geom_type') !== undefined ? layer.get('geom_type') : await this.getGeomType(layer.get('name')); + let reqGeomType = await getGeomType(layer.get('name')); + if (reqGeomType.success) { + layerGeomType = reqGeomType.result; + + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: layerGeomType, + layer_source: { + url: appConfig.geoserver_host+'wms', + // url: appConfig.geoserver_host+'gwc/service/wms?SERVICE=WMS', + params: { + 'LAYERS': appConfig.workspace_name+':'+layer.get('name'), + 'TILED': true, + 'SLD': layerStyleUrl+layer.get('name') + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous' + }, + layer_visible: layer.getVisible(), + layer_position: i + }); + } + } + } + count = count + 1; + + // the last loop, so send to updateMap API + // console.log('count', count); + // console.log('this.olmap.getLayers().array_.length',this.olmap.getLayers().array_.length); + // console.log('if count==this.olmap.getLayers().array_length', count == this.olmap.getLayers().array_.length); + if (count == this.olmap.getLayers().array_.length) { + console.log('terakhirrrrrr'); + requestPayload = { + 'map_id': mapId, + 'map_title': mapTitle, + 'map_zoom': mapZoom, + 'map_projection': mapProjection, + 'center_x': center_x, + 'center_y': center_y, + 'map_layers': mapLayers + }; + console.log('requestPayload',requestPayload); + this.saveMapToApi(requestPayload); + } + }); + } + } + + saveMapToApi = async (requestPayload) => { + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(requestPayload) + } + + try { + const result = await fetch(API_UPDATE_MAP, param).then(response => response.json()).then(res => res) + if(result.data){ + console.log('after save',result); + // this.(); + this.setState({alert: true, messageAlert: result.code_message, successAlert: true, dangerAlert: false}) + } else { + this.setState({alert: true, messageAlert: result.code_message, successAlert: false, dangerAlert: true}) + } + } catch(err) { + this.setState({alert: true, messageAlert: err.message.toString(), successAlert: false, dangerAlert: true}) + } + } + + toggleEditGeometry = (selectedPopupData) => { + let { editGeometryVisible } = this.state; + if (!editGeometryVisible) { + this.setState({ + editGeometryVisible: true, + layerNameDraw: selectedPopupData.id, + geomTypeDraw: selectedPopupData.geometry.type + }); + + } + else { + this.cancelDraw(); + } + } + + cancelDraw = () => { + this.setState({ + editGeometryVisible: false, + layerNameDraw: '', + geomTypeDraw: '' + }); + } + + toggleActiveStateAddGeometry = () => { + this.setState({activeStateAddGeometry: !this.state.activeStateAddGeometry}); + } + + printMap = () => { + this.olmap.once('rendercomplete', () => { + domtoimage + .toJpeg(this.olmap.getViewport()) + .then((dataUrl) => { + console.log('dataUrl', dataUrl); + let link = document.getElementById('image-download'); + link.href = dataUrl; + link.click(); + toast.success("Success Print Map!") + }) + .catch(e => toast.error(e.toString())); + }); + + } + + signOut = (e) => { + e.preventDefault() + // localStorage.removeItem("u_group"); + // localStorage.removeItem("fullname"); + window.localStorage.clear(); + // emptyConstants(); + this.props.history.push('/login'); + } + + getLayerAttribute = async (layerName) => { + const res = await getLayerAttribute(layerName); + console.log('getLayerAttribute',res); + if (res.success) { + // console.log(res.result); + if (res.result.data) { + if (res.result.data.length > 0) { + this.setState({layer_attribute: res.result.data}, () => { + console.log(this.state.layer_attribute); + }); + } + } + } + else { + // alert(res.result); + toast.warn(res.result); + } + } + + // when checking chekbox on Layer Tree Panel + onCheckOpt = (state, checkedKeys) => { + this.setState({[state]: checkedKeys}); + } + + setLayer = async (state) => { + console.log('setLayer', state); + + await this.setState({isProcessing: true}); + + this.closePopupRight(); + + const { checkedKeysSales, checkedKeysCustomer, checkedKeysOffice, checkedKeysDemografi, checkedKeysAnalisa, checkedKeysEmployeeDivision, + salesGroupTree, employeeDivisionTree, queryBuilderOutput, queryBuilderType, checkedKeysProjectTree, projectTree } = this.state; + let layersToAdd = []; + let newLayer = null; + let vectorSource = null; + let vectorLayer = null; + + if (state === 'checkedKeysProjectTree') { + console.log('checkedKeysProjectTree', checkedKeysProjectTree); + + // first remove projectLayer and its features + this.removeLayerByName('routeLayer'); + this.removeLayerByName('projectLayer'); + this.removeLayerByName('waspangLayer'); + this.projectFeatures = []; + this.waspangFeatures = []; + + this.getChosenProyekRealisasi(); + + if (checkedKeysProjectTree && checkedKeysProjectTree.length > 0) { + + /* + Logic: + - looping all tree + - there are 3 variables + 1. features (Array) -> untuk nampung features yg ada di laporan planning + 2. projectTree (Array) + 3. checkedKeysProjectTree (Array) -> projectTree yg tercentang + + Jadi looping all nesting yg ada di projectTree (ambil object children) + Terus cek setiap levelnya dia ada di checkedKeysProjectTree gak? + Jika ada, cek apakah di object tersebut mengandung key namanya "plannings" tidak? + Jika iya, maka cek di dalamnya ada laporan_planning tidak? + Jika iya, maka push ke features[] dengan format: + { + "type": "Feature", + "properties": {}, // isian dari objectnya + "geometry": { + "type": "Point", + "coordinates": [ + 107.90771484375, // object.lon + -6.795535025719518 // object.lat + ] + } + } + */ + + this.getChildrenTree(projectTree[0].children); + // this.getWaspangFeatures(); + + console.log('projectFeatures', this.projectFeatures); + // console.log('waspangFeatures', this.waspangFeatures); + + let project = null; + project = { + type: "FeatureCollection", + features: this.projectFeatures + } + + // console.log('project', project); + + + // generate new layer + if (project && project.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(project, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'projectLayer', + source_type: 'geojson', + source: vectorSource, + style: PROJECT_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + else { + toast.warn('Data realisasi tidak ditemukan di project ini'); + } + + } + else if (checkedKeysProjectTree && checkedKeysProjectTree.length < 1) { + this.removeLayerByName('projectLayer'); + this.removeLayerByName('waspangLayer'); + this.removeLayerByName('routeLayer'); + this.projectFeatures = []; + this.waspangFeatures = []; + this.closePopupRight(); + } + } + + if (layersToAdd.length > 0) { + for (let i=0; i < layersToAdd.length; i++) { + + this.olmap.addLayer(layersToAdd[i]); // adding layer to exist map + + console.log('check addLayer'); + if (i === layersToAdd.length - 1) { + let extent = await this.getExtentLayerByName(layersToAdd[i]); + if (extent) { + this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + } + } + } + } + + // this.olmap.getLayers().forEach((layer, i) => { + // console.log('getLayers', layer); + // }); + + await this.setState({isProcessing: false}); + } + + // find the lat lon inside laporan_plannings that the planning has been checked on projectTree + getChildrenTree = (data) => { + data.map((item, index) => { + if (item.children && item.children.length > 0) { + this.getChildrenTree(item.children); + } + else if (item.laporan_plannings && item.laporan_plannings.length > 0) { + if (this.state.checkedKeysProjectTree.includes(item.key)) { + for (let i=0; i < item.laporan_plannings.length; i++) { + console.log('got features!!!!!'); + this.projectFeatures.push({ + "type": "Feature", + "id": `realisasi.${item.laporan_plannings[i].id}`, + "properties": {...item.laporan_plannings[i]}, + "geometry": { + "type": "Point", + "coordinates": [ + item.laporan_plannings[i].lon, + item.laporan_plannings[i].lat + ] + } + }) + } + } + } + }); + + // console.log('features end....', features); + + // return features; + } + + getWaspangFeatures = async () => { + // console.log('getWaspangFeatures', data); + const { waspangData, checkedKeysProjectTree } = this.state; + if (checkedKeysProjectTree && checkedKeysProjectTree.length > 0) { + if (waspangData.length > 0) { + for (let i=0; i < waspangData.length; i++) { + let item = waspangData[i]; + if (item.last_waypoint) { + this.waspangFeatures.push({ + "type": "Feature", + "id": `m_waspang.${item.id}`, + "properties": { + "id": item.id, + "user_id": item.user_id, + "nama_user": item.join.m_users_name, + "nama_proyek": item.join.m_proyek_nama, + "mulai_tugas": item.mulai, + "akhir_tugas": item.akhir, + "_type": "waspang" + }, + "geometry": { + "type": "Point", + "coordinates": [ + item.last_waypoint.lon, + item.last_waypoint.lat + ] + } + }) + } + } + + console.log('this.waspangFeatures', this.waspangFeatures); + + let layersToAdd = []; + let vectorSource = null; + let vectorLayer = null; + let waspang = null; + waspang = { + type: "FeatureCollection", + features: this.waspangFeatures + } + + // generate new layer + if (waspang && waspang.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(waspang, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'waspangLayer', + source_type: 'geojson', + source: vectorSource, + style: WASPANG_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + else { + toast.warn('Data waspang tidak ditemukan di project ini'); + } + + if (layersToAdd.length > 0) { + for (let i=0; i < layersToAdd.length; i++) { + + this.olmap.addLayer(layersToAdd[i]); // adding layer to exist map + + console.log('check addLayer'); + if (i === layersToAdd.length - 1) { + let extent = await this.getExtentLayerByName(layersToAdd[i]); + if (extent) { + this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + } + } + } + } + + } + } + + else if (checkedKeysProjectTree && checkedKeysProjectTree.length < 1) { + this.removeLayerByName('projectLayer'); + this.removeLayerByName('waspangLayer'); + this.removeLayerByName('routeLayer'); + this.projectFeatures = []; + this.waspangFeatures = []; + this.closePopupRight(); + } + + // this.waspangFeatures = [{ + // "type": "Feature", + // "id": `m_waspang.1`, + // "properties": { + // "id": 1, + // "user_id": 10, + // "nama_user": "Ryan", + // "nama_proyek": "FTTH Paket 2", + // "mulai_tugas": "2021-11-01T09:31:32.775Z", + // "akhir_tugas": "2021-11-30T09:31:32.775Z", + // "_type": "waspang" + // }, + // "geometry": { + // "type": "Point", + // "coordinates": [ + // 106.89285278320312, + // -6.228275226686636 + // ] + // } + // }, + // { + // "type": "Feature", + // "id": `m_waspang.2`, + // "properties": { + // "id": 2, + // "user_id": 12, + // "nama_user": "Effendi", + // "nama_proyek": "FTTH Paket 2", + // "mulai_tugas": "2021-11-01T09:31:32.775Z", + // "akhir_tugas": "2021-11-30T09:31:32.775Z", + // "_type": "waspang" + // }, + // "geometry": { + // "type": "Point", + // "coordinates": [ + // 106.9357681274414, + // -6.267522831839373 + // ] + // } + // }, + // { + // "type": "Feature", + // "id": `m_waspang.3`, + // "properties": { + // "id": 1, + // "user_id": 16, + // "nama_user": "Waspang C", + // "nama_proyek": "FTTH Paket 2", + // "mulai_tugas": "2021-11-01T09:31:32.775Z", + // "akhir_tugas": "2021-11-30T09:31:32.775Z", + // "_type": "waspang" + // }, + // "geometry": { + // "type": "Point", + // "coordinates": [ + // 106.80564880371092, + // -6.263086293713913 + // ] + // } + // }] + } + + findChildLayerToRemove = (parentObj) => { + + let layersToRemove = []; + + if (parentObj.hasOwnProperty('children')) { + + // get semua children layer name nya + for (let i=0; i < parentObj.children.length; i++) { + // newLayer = this.generateLayerWMSByName(parentObj.children[i].layers.name); + // if (newLayer) { + // layersToAdd.push(newLayer); + // } + + layersToRemove.push(parentObj.children[i].layers.name) + } + } + else { + layersToRemove.push(parentObj.layers.name) + } + + return layersToRemove; + + } + + // findChildLayerToAdd = (parentObj) => { + // for (let i=0; i < parentObj.length; i++) { + + // } + // } + + findChildLayerToAdd = (parentObj, checkedKeys) => { + + let layersToAdd = []; + let newLayer = null; + + // 1. cek apakah parentObj itu kecentang atau ngga + // 2. cek apakah parentObj itu punya child apa ngga + // kalo kecentang parent nya ya berarti loop aja + // 3. cek apakah childnya itu kecentang ngga + + console.log('parentObj', parentObj); + + if (checkedKeys.includes(parentObj["key"])) { + if (parentObj.hasOwnProperty('children')) { + // loop aja semua child layernya + for (let i=0; i < parentObj.children.length; i++) { + newLayer = this.generateLayerWMSByName(parentObj.children[i].layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + else { + newLayer = this.generateLayerWMSByName(parentObj.layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + else { + if (parentObj.hasOwnProperty('children')) { + + } + + } + + // // tapi dicek lagi kalo childrennya punya children + // if (parentObj.hasOwnProperty('children')) { + + // // cek apakah parent nya kecentang gak? (berdasarkan key) + // if (checkedKeys.includes(parentObj["key"])) { + + // // centang semua children nya + // for (let i=0; i < parentObj.children.length; i++) { + // newLayer = this.generateLayerWMSByName(parentObj.children[i].layers.name); + // if (newLayer) { + // layersToAdd.push(newLayer); + // } + // } + // } + // // just activate the selected nya aja + // else { + // // for (let i=0; i < checkedKeys.length; i++ ) { + // // let obj = parentObj.children.find(data => data.key == checkedKeys[i]) + // // console.log('ngecek obj', obj); + // // newLayer = this.generateLayerWMSByName(obj.layers.name); + // // if (newLayer) { + // // layersToAdd.push(newLayer); + // // } + // // } + // } + // } + // else { + // newLayer = this.generateLayerWMSByName(parentObj.layers.name); + // if (newLayer) { + // layersToAdd.push(newLayer); + // } + // } + + return layersToAdd; + + } + + getExtentLayerByName = async (layer) => { + + // console.log('getExtentLayerByName', layerName, source_type); + + let layerName = layer.get('name'); + let source_type = layer.get('source_type'); + + // if source_type is undefined = WMS + // otherwise source_type is not undefined = geojson + + let extent = null; + + if (source_type === 'wms') { + let getExt = await fetch(WMS_CAPABILITIES_URL_2).then((response) => { + return response.text(); + }).then((text) => text); + + let result = new WMSCapabilities().read(getExt); + if (result && result.Capability.Layer.Layer.find(l => l.Name === appConfig.workspace_name+':'+layerName)) { + extent = result.Capability.Layer.Layer.find(l => l.Name === appConfig.workspace_name+':'+layerName).EX_GeographicBoundingBox; + } + } + else if (source_type === 'geojson') { + // extent = layer.getSource().getExtent(); + + // layer.getSource().once('change',(e) => { + // if (layer.getSource().getState() === 'ready') { + // extent = layer.getSource().getExtent(); + // console.log('extent------------', extent); + // // map.getView().fit(extent, map.getSize()); + // } + // }); + extent = null + } + console.log('extent', extent); + return extent; + + } + + generateLayerWMSByName = (layerName) => { + let theLayer = null; + if (layerName) { + theLayer = new OlLayerTile({ + name: layerName, + source_type: 'wms', + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':'+layerName, + 'TILED': true, + 'SLD': layerStyleUrl+layerName, + // 'CQL_FILTER': CQL_QUERY + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + }), + type: 'layer', + geom_type: 'Polygon' + }); + } + return theLayer; + } + + clearMapLayers = () => { + // console.log('clearing layers'); + // clear map. removing all layer other than OSM + let removeLayers = []; + this.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') !== 'OSM') { + removeLayers.push(layer); + } + }); + // console.log('removeLayers', removeLayers); + if (removeLayers.length > 0) { + for (let i=0; i < removeLayers.length; i++) { + this.olmap.removeLayer(removeLayers[i]); + } + } + } + + removeLayerByName = (name) => { + + let removeLayers = []; + this.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === name) { + removeLayers.push(layer); + } + }); + // console.log('removeLayers', removeLayers); + if (removeLayers.length > 0) { + for (let i=0; i < removeLayers.length; i++) { + this.olmap.removeLayer(removeLayers[i]); + } + } + } + + getRoute = () => { + fetch(routeDummy).then((response) => { + response.json().then((result) => { + console.log('getRoute', result); + }) + }); + } + + showRoute = (salesRoute) => { + const { mapProjection } = this.state; + console.log('showRoute', salesRoute); + + this.removeLayerByName('routeLayer'); + if (salesRoute.features && salesRoute.features.length < 1) { + toast.warn("Couldn't show route at selected time. Please select another range time."); + return; + } + + let route = null; + let polyline = null; + let extent = null; + + // polyline = routeDummy.routes[0].overview_polyline.points; + // console.log('polyline', polyline); + + // route = new Polyline({ + // }).readGeometry(polyline, { + // dataProjection: 'EPSG:4326', + // featureProjection: 'EPSG:4326', + // }); + + // let route = new Polyline().readGeometry(polyline); + + // polyline = { + // "type": "LineString", + // "coordinates": [ + // [ + // 106.78853, + // -6.26235 + // ], + // [ + // 106.78863, + // -6.26201 + // ], + // [ + // 106.80065, + // -6.24523 + // ] + // ] + // } + + // console.log('check route', salesRoute.features.length > 0 && (salesRoute.features[0].geometry && salesRoute.features[0].geometry.coordinates)); + + if (salesRoute.features.length > 0 && (salesRoute.features[0].geometry && salesRoute.features[0].geometry.coordinates)) { + polyline = salesRoute.features[0].geometry; + + route = new LineString(polyline.coordinates).transform('EPSG:4326', mapProjection); + extent = route.getExtent(); + + console.log('route', route); + console.log('extent', extent); + + let routeFeature = new Feature({ + type: 'route', + geometry: route, + geometry_name: salesRoute.features[0].geometry_name, + id: salesRoute.features[0].id, + properties: salesRoute.features[0].properties, + routeColor: getRandomColor() + }); + let geoMarker = new Feature({ + type: 'geoMarker', + geometry: new Point(route.getCoordinateAt(0)), + id: salesRoute.features[0].id, + properties: salesRoute.features[0].properties + }); + let startMarker = new Feature({ + type: 'pinRouteStart', + geometry: new Point(route.getCoordinateAt(0)), + id: salesRoute.features[0].id, + properties: salesRoute.features[0].properties + }); + let endMarker = new Feature({ + type: 'pinRouteEnd', + geometry: new Point(route.getCoordinateAt(1)), + id: salesRoute.features[0].id, + properties: salesRoute.features[0].properties + }); + + let animating = false; + + let vectorLayer = new VectorLayer({ + name: 'routeLayer', + source_type: 'routeLayer', + source: new VectorSource({ + features: [routeFeature, geoMarker, startMarker, endMarker], + }), + style: (feature, resolution) => { + // hide geoMarker if animation is active + if (animating && feature.get('type') === 'geoMarker') { + return null; + } + // return ROUTE_MAP_STYLES[feature.get('type')]; + return ROUTE_MAP_STYLES(feature, resolution); + }, + }); + + this.olmap.addLayer(vectorLayer); + + if (extent) { + // this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + this.olmap.getView().fit(extent, { size: this.olmap.getSize(), duration: 500 }); + + } + } + } + + searchRouting = async (userId, dateString) => { + await this.setState({isSearchingRoute: true, isProcessing: true}); + + const {routeType} = this.state; + let routes = null; + + if (routeType === 'waspang') { + routes = await getWaspangRoutingApi(userId, dateString); + } + + if (routes) { + this.setState({isSearchingRoute: false, isProcessing: false}, () => this.showRoute(routes)) + } + else { + this.setState({isSearchingRoute: false, isProcessing: false}, () => toast.warn("Sorry. Couldn't get user waypoint")); + } + } + + handleQueryBuilder = (query, type, tree) => { + console.log("query builder "+type, query); + this.setState({ queryBuilderOutput:query, queryBuilderType:type, currentQbTree:tree,currentQbType:type }, () => { + if(type === "Sales"){ + this.setLayer('checkedKeysSales'); + }else if(type === "Customer"){ + this.setLayer('checkedKeysCustomer'); + }else if(type === "Office"){ + this.setLayer('checkedKeysOffice'); + } + }) + } + + handleQbReset = (type) => { + this.setState({ + queryBuilderOutput:'', + queryBuilderType:'', + currentQbTree:'', + currentQbType:'' }, () => { + if(type === "Sales"){ + this.setLayer('checkedKeysSales'); + }else if(type === "Customer"){ + this.setLayer('checkedKeysCustomer'); + }else if(type === "Office"){ + this.setLayer('checkedKeysOffice'); + } + }) + } + + + getChosenProyekRealisasi = () => { + const { chosenProyek, projectTree, checkedKeysProjectTree } = this.state; + + this.chosenProyekTemp = []; + let chosenProyekId = []; + + if (checkedKeysProjectTree.length > 0) { + chosenProyekId = this.getChosenProyekId(); // ini yang sudah diambil uniquenya + } + else { + chosenProyekId = [] + } + + console.log('chosenProyekId', chosenProyekId); + this.setState({chosenProyekIds: chosenProyekId}); + } + + // ambil id atau proyek_id berdasarkan key yang tercentang di projectTree + // dengan cara mencocokkan di levelnya + // kalau dia gak punya parent_id, ambil "id" + // kalau dia punya parent_id, ambil "proyek_id" + // khusus untuk key = "project-0" berarti dia tercentang semua, gak usah cek dalemannya lagi, langsung ambil children -> id nya. + // masukin ke array chosenProyekTemp + // ambil unique nya aja lalu return uniquenya + getChosenProyekId = () => { + const { checkedKeysProjectTree, projectTree } = this.state; + // console.log('checkedKeysProjectTree', checkedKeysProjectTree); + + if (checkedKeysProjectTree.length > 0) { + if (checkedKeysProjectTree.includes('project-0')) { + // langsung ambil semua children di level pertama + for (let i=0; i < projectTree[0].children.length; i++) { + this.chosenProyekTemp.push(projectTree[0].children[i].id); + } + } + else { + // for (let i=0; i < projectTree[0].children.length; i++) { + this.getChosenProyekIdByKey(projectTree[0].children); + // } + } + } + + // usage example: + // var myArray = ['a', 1, 'a', 2, '1']; + // var unique = myArray.filter((v, i, a) => a.indexOf(v) === i); + let unique = this.chosenProyekTemp.filter((v, i, a) => a.indexOf(v) === i); + return unique; + } + + getChosenProyekIdByKey = (dataTree) => { + const { checkedKeysProjectTree } = this.state; + + for (let i=0; i < dataTree.length; i++) { + if (checkedKeysProjectTree.includes(dataTree[i].key)) { + // console.log('matched!!!', dataTree[i].key); + // get the proyek_id, then stop + if (dataTree[i].parent_id === undefined) { + this.chosenProyekTemp.push(dataTree[i].id) // ambil idnya, karena dia adalah level paling atas (proyek) + } + else if (dataTree[i].parent_id !== undefined && dataTree[i].parent_id === null) { // dia adalah subproyek pertama + this.chosenProyekTemp.push(dataTree[i].proyek_id); // ambil proyek_id + } + else if (dataTree[i].parent_id !== undefined && dataTree[i].parent_id !== null) { // dia adalah subproyek kedua, dst + this.chosenProyekTemp.push(dataTree[i].proyek_id); // ambil proyek_id + } + } + else { + // console.log('not matched, keep looping!', dataTree[i]); + // keep looping until get the matched key + if (dataTree[i].subproyeks) { + this.getChosenProyekIdByKey(dataTree[i].children); + } + } + } + } + + toggleStatusRight = () => { + this.setState({statusRight:!this.state.statusRight}); + } + + toggleProggresBottom = () => { + this.setState({proggressBottom:!this.state.proggressBottom}) + } + + closeStatusRight = () => { + this.setState({statusRight:false}); + } + + closeProggressBottom = () => { + this.setState({proggressBottom:false}) + } + + render() { + const { alert, successAlert, dangerAlert, messageAlert, + dataStatusProyek, dataPersentaseProyek, dataCostProyek, dataStatusWaspang } = this.state; + return ( +
+ this.saveMap()} + printMap={() => this.printMap()} + onLogout={(e) => this.signOut(e)} + /> + + +
+ + +
+ + this.setState({alert: false, successAlert: false, dangerAlert: false})}> + {messageAlert} + + + { this.state.isProcessing && +
+ +
+ } + +
{this.state.popupRightVisible ? + this.setPopupDataTemp(feature)} + layerName={this.state.activeListFeatureId ? this.state.activeListFeatureId.substr(0, this.state.activeListFeatureId.indexOf('.')) : ''} + toggleEditGeometry={(selectedPopupData) => this.toggleEditGeometry(selectedPopupData)} + activeStateAddGeometry={this.state.activeStateAddGeometry} + fid={this.state.activeListFeatureId} + closePopupRight={this.closePopupRight} + getLayerAttribute={(layerName) => this.getLayerAttribute(layerName)} + layer_attribute={this.state.layer_attribute} + searchLabelData={this.state.searchLabelData} + layerInfo={this.state.layerInfo} + toggleRoutingBarVisible={() => this.setState({routingBarVisible: !this.state.routingBarVisible})} + setRouteType={(routeType) => this.setState({routeType: routeType})} + /> : null + }
+
+ + + + this.openPopupRight()} + closePopupRight={() => this.closePopupRight()} + popupDataTemp={this.state.popupDataTemp} + removeChosenLayer={() => this.removeChosenLayer()} + setPopupDataTemp={(feature) => this.setPopupDataTemp(feature)} + activeListFeatureId={this.state.activeListFeatureId} + editGeometryVisible={this.state.editGeometryVisible} + toggleActiveStateAddGeometry={() => this.toggleActiveStateAddGeometry()} + searchLabelData={this.state.searchLabelData} + layerInfo={this.state.layerInfo} + onCheckOpt={(state, checkedKeys) => this.onCheckOpt(state, checkedKeys)} + checkedKeysSales={this.state.checkedKeysSales} + checkedKeysCustomer={this.state.checkedKeysCustomer} + checkedKeysOffice={this.state.checkedKeysOffice} + checkedKeysDemografi={this.state.checkedKeysDemografi} + checkedKeysAnalisa={this.state.checkedKeysAnalisa} + checkedKeysEmployeeDivision={this.state.checkedKeysEmployeeDivision} + checkedKeysProjectTree={this.state.checkedKeysProjectTree} + salesGroupTree={this.state.salesGroupTree} + employeeDivisionTree={this.state.employeeDivisionTree} + setSalesGroupTree={(data) => this.setState({salesGroupTree: data})} + setEmployeeDivisionTree={(data) => this.setState({employeeDivisionTree: data})} + showRoute={(route) => this.showRoute(route)} + removeLayerByName={(layerName) => this.removeLayerByName(layerName)} + setIsProcessing={(data) => this.setState({isProcessing: data})} + handleQueryBuilder={this.handleQueryBuilder} + currentQbTree={this.state.currentQbTree} + currentQbType={this.state.currentQbType} + handleQbReset={this.handleQbReset} + projectTree={this.state.projectTree} + setProjectTree={(data) => this.setState({projectTree: data})} + proggressBottom={this.state.proggressBottom} + toggleStatusRight={this.toggleStatusRight} + toggleProggresBottom={this.toggleProggresBottom} + /> + + + { + this.state.editGeometryVisible && + this.cancelDraw()} + layerName={this.state.layerNameDraw} + geomType={this.state.geomTypeDraw} + olmap={this.olmap} + /> + } + + { this.state.routingBarVisible && + this.setState({routingBarVisible: !this.state.routingBarVisible})} + popupDataTemp={this.state.popupDataTemp} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + searchRouting={(userId, dateString) => this.searchRouting(userId, dateString)} + isSearchingRoute={this.state.isSearchingRoute} + setRouteType={(routeType) => this.setState({routeType: routeType})} + /> + } + + + {/**/} + + +
+ + + {this.state.proggressBottom ? + + + + + Persentase Progress Proyek x + + + console.log('click persentase linechart')} + /> + + + + + + + Progress Cost Perencanaan dan Realisasi x + + + + + + + + : null} + + {this.state.statusRight ? + + + + + + Status Proyek x + + + {/* */} + + + + + + + + + Status Pengawas Lapangan x + + + + + + + + {/* console.log('click chart')} + /> */} + + : null } + + + + ); + } +} + +export default SiopasMap; + diff --git a/src/views/Map/Map_16.js b/src/views/Map/Map_16.js new file mode 100644 index 0000000..1030c72 --- /dev/null +++ b/src/views/Map/Map_16.js @@ -0,0 +1,4091 @@ +import React, { Component, Suspense, Fragment } from 'react'; +import ReactDOM from 'react-dom'; +import { + Container, Col, Row, Button, UncontrolledTooltip, + Card, + CardBody, + CardHeader, + Table, + Modal, ModalHeader, ModalBody, ModalFooter, + Carousel, + CarouselItem, + CarouselIndicators, + CarouselControl, + CarouselCaption, + ListGroup, + ListGroupItem, + Badge, + Progress +} from 'reactstrap'; +import './Map.css'; +import './Popup.css'; +import './CustomScroll.css'; +import 'ol/ol.css'; +import 'antd/dist/antd.css'; +import './react-geo.css'; + +import OlMap from 'ol/Map'; +import OlView from 'ol/View'; +import OlLayerTile from 'ol/layer/Tile'; +import ImageLayer from 'ol/layer/Image'; +import OlSourceOsm from 'ol/source/OSM'; +import OlSourceTileJson from 'ol/source/TileJSON'; +import OlLayerGroup from 'ol/layer/Group'; +import OlSourceTileWMS from 'ol/source/TileWMS'; +import OlSourceImageWMS from 'ol/source/ImageWMS'; +// import OlLayerSwitcher from 'ol/control/LayerSwitcher'; +import { fromLonLat, transformExtent, transform } from 'ol/proj'; +import { Vector as VectorSource, XYZ as XYZSource, Cluster } from 'ol/source'; +import Overlay from 'ol/Overlay'; +import { Draw, Select, Modify } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import { Vector as VectorLayer } from 'ol/layer'; +import { Circle as CircleStyle, Fill, Stroke, Style, Text, Icon as IconOl } from 'ol/style'; +import { defaults as defaultControls, MousePosition, ScaleLine } from 'ol/control'; +import { createStringXY, toStringXY } from 'ol/coordinate'; +import GeoJSON from 'ol/format/GeoJSON'; +import WMSCapabilities from 'ol/format/WMSCapabilities'; +import { formatRupiah, formatThousand, sortBy, uniqueKeyValues } from '../../const/CustomFunc' + +import { Drawer } from 'antd'; +import { + SimpleButton, + MapComponent, + NominatimSearch, + MeasureButton, + // LayerTree, + MapProvider, + mappify, + onDropAware, + // AddWmsPanel +} from '@terrestris/react-geo'; + +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import LayerSwitcher from '@terrestris/react-geo/dist/LayerSwitcher/LayerSwitcher'; +import axios from 'axios'; +import PopupContainer from '../../components/PopupContainer/PopupContainer'; +import { + AppHeader +} from '@coreui/react'; +// import MapHeader from '../../components/MapHeader/MapHeader'; +import MapToolbar from '../../components/MapToolbar/MapToolbar'; +import DrawingTool from '../../components/DrawingTool/DrawingTool'; +import ImagePopup from '../../components/ImagePopup/ImagePopup'; +import ImageSlider from '../../components/ImageSlider/ImageSlider'; +import RoutingBar from '../../components/RoutingBar/RoutingBar'; +// import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import { + appConfig, setRequestMapHeader, layerStyleUrl, BMD_DENPASAR_MAPSERVICE_URL, IU_MAPSERVICE_URL, MAP_ID, emptyConstants, + WMS_CAPABILITIES_URL_2 +} from '../../const/MapConst.js'; +import { Icon } from '@iconify/react'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import country_indonesia from '../../assets/json/indonesia.json'; +import { test, getGeomType, updateMap, getLayerAttribute, getLayerColor, getRandomColor } from '../../const/GeoserverFunc.js'; +import { + API_UPDATE_MAP, API_LOAD_MAP, API_LAYER_SEARCH_LABEL, API_GET_CHART_KATEGORI, PROYEK_SEARCH, + USERPROYEK_SEARCH, DASHBOARD_STATUS_SEARCH, DASHBOARD_PROYEK_SEARCH, PRESENSI_SEARCH, ABSENSI_SEARCH, PANIC_BUTTON_SEARCH, + DASHBOARD_COST_PLANNING_ACTUAL, + DASHBOARD_PERSENTASE_PROGRESS_PROYEK, + DASHBOARD_REPORT_POINTS, + DASHBOARD_STATUS_PROYEK, + DASHBOARD_KURVA_S, + GET_PERCENTAGE_PERDAY, + APP_MODE, + PRESENCE_SEARCH +} from '../../const/ApiConst.js'; +import { getSalesRoutingApi, getSalesFeatures, getOfficeFeatures, getCustomerFeatures, getEmployeeFeatures, getEmployeeRoutingApi, getWaspangRoutingApi, getPresensiRoutingApi } from '../../const/GeohrApiFunc.js'; +import { SALES_FEATURES_STYLE, CUSTOMER_FEATURES_STYLE, OFFICE_FEATURES_STYLE, ROUTE_MAP_STYLES, EMPLOYEE_FEATURES_STYLE, PROJECT_FEATURES_STYLE, WASPANG_FEATURES_STYLE, LAPORAN_FEATURES_STYLE, PRESENSI_FEATURES_STYLE } from '../../const/GeohrMapStyles.js'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import moment from "moment"; +import salesGeojson from '../../dummy_data/sales.geojson'; +import routeDummy from '../../dummy_data/route2.json'; +import { demografiTree, analisaTree } from '../../const/LayerTreeConst.js' +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import pinRouteStart from '../../assets/img/map/pin_route_green.png'; +import pinRouteEnd from '../../assets/img/map/pin_route_red.png'; +import domtoimage from 'dom-to-image'; +import Loader from 'react-loader-spinner' +import "react-loader-spinner/dist/loader/css/react-spinner-loader.css" +import * as alasql from 'alasql'; +import * as lodash from 'lodash'; +// import LineChart from './LineChart' +// import PieChart from './PieChart' +// import Chart from 'chart.js' +import { Pie, Line, Bar } from 'react-chartjs-2'; +import numeral from 'numeral'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import 'chartjs-plugin-zoom'; + +const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${window.localStorage.getItem('token')}` + } +} + +const MappifiedNominatimSearch = mappify(NominatimSearch); +const MappifiedMeasureButton = mappify(MeasureButton); +// const MappifiedLayerTree = mappify(LayerTree); +const Map = mappify(onDropAware(MapComponent)); + +// const center = [ 788453.4890155146, 6573085.729161344 ]; +const projection = 'EPSG:3857'; //default +const projection4326 = 'EPSG:4326'; +// const projection = 'EPSG:4326'; // lat long +// Indonesia +// const lat = -2.6000285; +// const lon = 118.015776; +// const zoom = 5; +// const lat = -0.1240; +// const lon = 103.5173; + +// const lat = -2.36; +// const lon = 121.96; +const lat = -6.228000; +const lon = 106.559242; +const zoom = 9; +const maxZoom = APP_MODE === 'KIT' ? 9 : 22; +// const maxZoom = 22 +const Indonesia = new fromLonLat([lon, lat], projection); +const Bali_bbox = [115.178638994694, -8.71934970794214, 115.269238650799, -8.59763413248024]; + +const osmLayer = new OlLayerTile({ + source: new OlSourceOsm(), + name: 'OSM', + type: 'base', + imageName: 'osm.PNG', + // maxZoom: 9 +}); + +const esriLayer = new OlLayerTile({ + name: "ESRI", + source: new XYZSource({ + url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + projection: projection, + // maxZoom: 9 + }), + type: 'base', + imageName: 'esri.PNG' +}); + +const googleLayer = new OlLayerTile({ + name: "Google", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 9 + }), + type: 'base', + imageName: 'google.PNG' +}) + +const googleStreetLayer = new OlLayerTile({ + name: "Google Street", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 9 + }), + type: 'base', + imageName: 'google_street.PNG' +}) + + +// let baseLayers = [ +// osmLayer, +// esriLayer, +// // googleLayer, +// // googleStreetLayer, +// denpasarLayer, +// citraDenpasarLayer, +// iuLayerGroup +// ]; + +let baseLayers = []; + +if (localStorage.getItem('u_group') == 'kominfo') { + baseLayers = [ + osmLayer, + esriLayer, + googleLayer, + googleStreetLayer, + // denpasarLayer, + // citraDenpasarLayer, + // iuLayerGroup + ]; +} +else { + baseLayers = [ + osmLayer, + esriLayer, + googleLayer, + googleStreetLayer, + // denpasarLayer, + // citraDenpasarLayer, + // iuLayerGroup + ]; +} + +const baseLayerGroup = new OlLayerGroup({ + name: 'Base Layers', + layers: [ + osmLayer, + esriLayer + ], + type: 'baseGroup' +}); + +const defaultPersentaseProyek = { + labels: [], + datasets: [ + { + label: 'Perencanaan', + data: [], + fill: false, + backgroundColor: 'rgba(255, 99, 132, 0.5)', + borderColor: 'rgba(255, 99, 132, 0.5)', + // stack: 'Stack-0' + yAxisID: 'y-axis-1', + }, + { + label: 'Aktual', + data: [], + fill: false, + backgroundColor: 'rgba(54, 162, 235, 0.5)', + borderColor: 'rgba(54, 162, 235, 0.5)', + // stack: 'Stack-1' + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultCostProyek = { + labels: [], + datasets: [ + { + label: 'Perencanaan', + data: [], + fill: false, + backgroundColor: 'rgba(255, 99, 132, 0.5)', + borderColor: 'rgba(255, 99, 132, 0.5)', + // stack: 'Stack-0' + yAxisID: 'y-axis-1', + }, + { + label: 'Aktual', + data: [], + fill: false, + backgroundColor: 'rgba(54, 162, 235, 0.5)', + borderColor: 'rgba(54, 162, 235, 0.5)', + // stack: 'Stack-1' + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultKurvaS = { + labels: [], + datasets: [ + { + label: 'Perencanaan', + data: [], + fill: false, + backgroundColor: 'rgba(255, 99, 132, 0.5)', + borderColor: 'rgba(255, 99, 132, 0.5)', + // stack: 'Stack-0' + yAxisID: 'y-axis-1', + }, + { + label: 'Aktual', + data: [], + fill: false, + backgroundColor: 'rgba(54, 162, 235, 0.5)', + borderColor: 'rgba(54, 162, 235, 0.5)', + // stack: 'Stack-1' + yAxisID: 'y-axis-1', + }, + ], +}; + +const defaultStatusProyek = { + labels: ['Aman', 'Alert', 'Critical'], + datasets: [ + { + label: '# of Votes', + data: [], + backgroundColor: [ + // 'rgba(54, 162, 235, 0.2)', + 'rgba(28, 165, 23, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(255, 99, 132, 0.2)', + ], + borderColor: [ + // 'rgba(54, 162, 235, 1)', + 'rgba(28, 165, 23, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(255, 99, 132, 1)', + ], + borderWidth: 1, + }, + ], +}; + +const defaultStatusWaspang = { + labels: ['Hadir', 'Izin', 'Panic Button'], + datasets: [ + { + label: '# of Votes', + data: [], + backgroundColor: [ + 'rgba(77,232,0, 0.2)', + 'rgba(255,146,3, 0.2)', + 'rgba(164,7,120, 0.2)', + ], + borderColor: [ + 'rgba(77,232,0, 1)', + 'rgba(255,146,3, 1)', + 'rgba(164,7,120, 1)', + ], + borderWidth: 1, + }, + ], +}; + +const barPersentaseOptions = { + tooltips: { + callbacks: { + label: function (tooltipItem, data) { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${tooltipItem.value}%`; + } + } + }, + scales: { + yAxes: [ + { + // type: 'linear', + // display: true, + // position: 'left', + id: 'y-axis-1', + ticks: { + max: 100, + min: 1, + callback: function (value, index, values) { + return `${value}%`; + } + }, + } + ], + }, + plugins: { + datalabels: { + display: true + } + } +} + +const barCostOptions = { + scales: { + yAxes: [ + { + // type: 'bar', + // display: true, + // position: 'left', + id: 'y-axis-1', + ticks: { + // beginAtZero: true, + callback: function (value, index, values) { + return numeral(value).format('0,0'); + } + }, + } + ], + }, + tooltips: { + callbacks: { + label: function (tooltipItem, data) { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${numeral(tooltipItem.value).format('0,0')}`; + } + } + }, + plugins: { + datalabels: { + display: false + } + } +}; + +const optionsChartStatusProyek = { + responsive: true, + maintainAspectRatio: false, + // title: { + // display: true, + // text: 'Status Proyek' + // }, + legend: { + display: true, + position: 'bottom' + // labels: { + // fontColor: 'rgb(255, 99, 132)' + // } + } +} + +const optionsChartPersentaseProgress = { + // title: { + // display: true, + // text: 'Persentase Progress Proyek' + // }, + responsive: true, + maintainAspectRatio: false, + tooltips: { + callbacks: { + label: function (tooltipItem, data) { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${tooltipItem.value}%`; + } + } + }, + scales: { + yAxes: [ + { + type: 'linear', + display: true, + position: 'left', + id: 'y-axis-1', + + ticks: { + max: 100, + min: 1, + callback: function (value, index, values) { + return `${value}%`; + } + }, + } + ], + } +} + +const optionsChartCostProyek = { + // title: { + // display: true, + // text: 'Progress Cost Perencanaan dan Realisasi' + // }, + responsive: true, + maintainAspectRatio: false, + tooltips: { + callbacks: { + label: function (tooltipItem, data) { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${numeral(tooltipItem.value).format('0,0')}`; + } + } + }, + scales: { + yAxes: [ + { + type: 'linear', + display: true, + position: 'left', + id: 'y-axis-1', + ticks: { + callback: function (value, index, values) { + return numeral(value).format('0,0'); + } + }, + } + ], + }, +} + +const optionsChartKurvaS = { + // title: { + // display: true, + // text: 'Progress Cost Perencanaan dan Realisasi' + // }, + responsive: true, + maintainAspectRatio: false, + aspectRatio: 1, + tooltips: { + callbacks: { + label: function (tooltipItem, data) { + const label = data.datasets[tooltipItem.datasetIndex].label || ''; + return `${label}: ${numeral(tooltipItem.value).format('0,0')}`; + } + } + }, + scales: { + yAxes: [ + { + type: 'linear', + display: true, + position: 'left', + id: 'y-axis-1', + ticks: { + beginAtZero: true, + max: 100, + callback: function (value, index, values) { + return numeral(value).format('0,0'); + } + }, + min: 0, + max: 100 + } + ], + }, + zoom: { + enabled: true, + mode: 'xy', + limits: { + y: {min: 0, max: 100} + } + }, + pan: { + enabled: true, + mode: 'xy', + limits: { + y: {min: 0, max: 100} + } + } +} + +const columnPresensi = [ + { name: "Nama Waspang" }, + { name: "Tanggal Kehadiran" }, + { name: "Jam Masuk" }, + { name: "Jam Keluar" }, + { name: "Durasi Kerja " }, +] + +const columnAbssensi = [ + { name: "Nama Waspang" }, + { name: "Deskripsi" }, + { name: "Tanggal Absensi" }, +] + +const columnPanic = [ + { name: "Tanggal" }, + { name: "Nama Waspang" }, + { name: "Status Response" }, +] + +const columnStatusProyek = [ + { name: "Nama" }, + { name: "Biaya" }, + { name: "SDM" }, + { name: "PM" }, + { name: "Aktivitas Mulai" }, + { name: "Aktivitas Selesai" }, +] + + +class SiopasMap extends Component { + constructor(props) { + super(props); + + this.state = { + openTablePengawas: false, + openTableStatusProyek: false, + allDataWaspang: null, + tabelTypeWaspang: "", + columnWaspang: [], + dataWaspangHadir: [], + dataWaspangAbsent: [], + dataWaspangPanic: [], + dataTableStatusProyek: [], + typeTableStatusProyek: "", + mapZoom: null, + mapCenter: null, + mapProjection: null, + drawerLayerVisible: false, + drawerSearchVisible: false, + wmsLayers: [], + totalLayerHasFeature: 0, + countGetFeature: 0, + countNotGetFeature: 0, + popupDataTemp: [], + activeListFeatureId: '', + evtCoordinate: null, + visibleLS: false, + visibleLSProp: 'hide', + popupRightVisible: false, + imagePopupVisible: false, + alert: false, + successAlert: false, + dangerAlert: false, + messageAlert: "", + editGeometryVisible: false, + layerNameDraw: '', + geomTypeDraw: '', + activeStateAddGeometry: false, + layer_attribute: [], + searchLabelData: [], + layerInfo: [], + // LayerTree Panel + checkedKeysSales: [], + checkedKeysCustomer: [], + checkedKeysOffice: [], + checkedKeysDemografi: [], + checkedKeysAnalisa: [], + checkedKeysEmployeeDivision: [], + checkedKeysProjectTree: [], + salesGroupTree: null, + employeeDivisionTree: null, + projectTree: null, + routingBarVisible: false, + isSearchingRoute: false, + isProcessing: false, + queryBuilderOutput: '', + queryBuilderType: '', + currentQbTree: '', + currentQbType: '', + routeType: '', + chosenProyekIds: [], + dataStatusProyek: null, + // dataStatusProyekAdw: [ + // { + // "id": 1, + // "proyek_name": "Pembangunan Jembatan Layang", + // "current_budget": 220000, + // "acwp": 100000, + // "bcwp": 99000, + // "rem_to_complete": 120000, + // "add_cost_to_complete": 10000, + // "estimated_at_completion": 230000, + // "cost_deviation": 10000, + // "potential": "overrun" + // }, + // { + // "id": 2, + // "proyek_name": "Pembuatan Aplikasi Survey Covid Varian Baru", + // "current_budget": 220000, + // "acwp": 100000, + // "bcwp": 99000, + // "rem_to_complete": 120000, + // "add_cost_to_complete": 10000, + // "estimated_at_completion": 230000, + // "cost_deviation": 10000, + // "potential": "overrun" + // } + // ], + dataStatusProyekAdw: null, + dataPersentaseProyek: null, + dataCostProyek: null, + dataCurvaS: null, + dataStatusWaspang: null, + waspangData: [], + planning: 0, + realisasi: 0, + status_project: { + good: 0, + warning: 0, + critical: 0 + }, + waspang_status: { + presensi: 0, + absensi: 0 + }, + panic_button: 0, + statusRight: true, + proggressBottom: true, + // Dashboard Carousel + activeIndex: 0, + activeIndex2: 0, + activeIndex3: 0, + animating: false, + animating2: false, + animating3: false, + laporanData: [], + presensiData: [], + kurvaSWindowMode: 'default' + }; + + this.layers = [ + osmLayer, + // layerGroupSekolah, + // layerGroupRS, + // layerGroupKantorDesa, + // layerGroupSarPras, + // tanah_kantor_instansi_pemerintah, + // point_sekolah, + // polygon_rumah_sakit + // paxel + // testLayer + ]; + + this.overlay = new Overlay({ + element: document.getElementById('popup'), + autoPan: true, + autoPanAnimation: { + duration: 250 + } + }); + + this.scaleLineControl = new ScaleLine({ + units: 'metric', + bar: true, + steps: 4, + text: true, + // minWidth: 140 + }) + + this.mousePositionControl = new MousePosition({ + coordinateFormat: createStringXY(4), + projection: 'EPSG:4326', + // className: 'custom-mouse-position', + target: document.getElementById('custom-mouse-position'), + }); + + this.olmap = new OlMap({ + view: new OlView({ + center: Indonesia, + zoom: zoom, + maxZoom: maxZoom, + projection: projection + }), + layers: this.layers, + overlays: [this.overlay], + controls: defaultControls().extend([this.scaleLineControl, this.mousePositionControl]) + }); + + this.changeBaseLayer = this.changeBaseLayer.bind(this); + this.openPopupRight = this.openPopupRight.bind(this); + this.closePopupRight = this.closePopupRight.bind(this); + + this.projectFeatures = []; + this.waspangFeatures = []; + this.chosenProyekTemp = []; + this.laporanFeatures = []; + this.presensiFeatures = []; + } + + componentDidMount = () => { + // this.loadMap(); + // setRequestMapHeader(); + // this.getLayerSearchLabel(); + // this.getLayerInfo(); + + this.olmap.on("singleclick", (evt) => { + this.mapOnClick(evt); + }); + this.setState({ mapProjection: this.olmap.getView().getProjection() }, () => console.log('mapProjection', this.state.mapProjection)); + + // // set default to check all project + // if (this.state.projectTree && this.state.projectTree.length > 0) { + // let checked = []; + // this.state.projectTree.map((item, index) => checked.push(item.key)); + // console.log('checked', checked); + // this.setState({checkedKeysProjectTree: checked}); + // } + + } + + componentDidUpdate = (prevProps, prevState) => { + + if (!this.state.popupRightVisible && this.state.popupDataTemp.length > 0) { + // this.showPopup(); + this.openPopupRight(); + // this.setActiveListFeature(); + // console.log('popupDataTemp > 0'); + // console.log('popupDataTemp ', this.state.popupDataTemp); + } + else if (this.state.popupRightVisible && this.state.popupDataTemp.length < 1) { + // this.closePopup(); + this.closePopupRight(); + // console.log('popupDataTemp < 1'); + // console.log('popupDataTemp ', this.state.popupDataTemp); + } + + if (prevState.checkedKeysProjectTree !== this.state.checkedKeysProjectTree) { + this.setLayer('checkedKeysProjectTree'); + } + + if (prevState.routingBarVisible !== this.state.routingBarVisible) { + if (!this.state.routingBarVisible) { + this.removeLayerByName('routeLayer'); + } + } + + if (this.state.chosenProyekIds !== prevState.chosenProyekIds) { + if (this.state.chosenProyekIds.length > 0) { + // this.getDataUserToProyek(); + this.getDataLaporanMap(); + this.getDataPresensiMap(); + this.getChartData(); + // this.getDailyInfo(); + } + else { + this.resetCharts() + } + } + } + + loading = () =>
Loading...
+ + resetCharts = () => { + this.setState({ + dataStatusProyek: null, + dataPersentaseProyek: null, + dataCostProyek: null, + dataStatusWaspang: null, + }) + } + + resetLayerDashboard = () => { + this.removeLayerByName('projectLayer'); + this.removeLayerByName('waspangLayer'); + this.removeLayerByName('routeLayer'); + this.removeLayerByName('laporanLayer'); + this.removeLayerByName('presensiLayer'); + this.projectFeatures = []; + this.waspangFeatures = []; + this.laporanFeatures = []; + this.presensiFeatures = []; + this.closePopupRight(); + // this.resetCharts(); + } + + // ngerequest API buat ngepush ke this.waspangFeatures + // getDataUserToProyek = async () => { + // const { chosenProyekIds } = this.state; + // let payload = { + // "columns": [], + // "joins": [ + // { + // "name": "m_proyek", + // "column_join": "proyek_id", + // "column_results": [ + // "nama", + // // "biaya", + // // "color_progress", + // // "jumlah_pekerja", + // // "pic", + // "mulai_proyek", + // "akhir_proyek" + // ] + // }, + // { + // "name": "m_subproyek", + // "column_join": "subproyek_id", + // "column_results": [ + // "nama", + // // "biaya", + // // "color_progress", + // // "jumlah_pekerja", + // // "pic", + // "mulai_proyek", + // "akhir_proyek" + // ] + // }, + // { + // "name": "m_users", + // "column_join": "user_id", + // "column_results": [ + // "name", + // "username", + // "email", + // "phone_number", + // "gender" + // ] + // } + // ], + // "orders": { + // "columns": [ + // "id" + // ], + // "ascending": true + // }, + // "paging": { + // "start": 0, + // "length": 25 + // } + // } + + // if (chosenProyekIds.length > 0) { + // payload.columns.push({ + // "name": "proyek_id", + // "logic_operator": "in", + // "value": chosenProyekIds.join(), + // "operator": "AND" + // }); + // } + + // // if(parseInt(localStorage.getItem('role_id'))!==1){ + // // payload.columns.push( + // // { + // // "name": "id", + // // "logic_operator": "=", + // // "value": localStorage.getItem('proyek_id'), + // // "operator": "AND" + // // } + // // ) + // // } + + // const config = { + // method: 'POST', // *GET, POST, PUT, DELETE, etc. + // // mode: 'cors', // no-cors, *cors, same-origin + // // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // // credentials: 'same-origin', // include, *same-origin, omit + // headers: { + // 'Content-Type': 'application/json', + // 'Authorization': 'Bearer ' + localStorage.getItem('token') + // // 'Content-Type': 'application/x-www-form-urlencoded', + // }, + // // redirect: 'follow', // manual, *follow, error + // // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + // body: JSON.stringify(payload) // body data type must match "Content-Type" header + // } + + // const result = await fetch(USERPROYEK_SEARCH, config).then(response => response.json()).then(res => res); + // if (result && result.code == 200) { + // // this.getWaspangFeatures(result.data); + // this.setState({ waspangData: result.data }, () => this.getWaspangFeatures()); + // } else { + // toast.error('Gagal Mengambil Data!!'); + // } + // } + + // ngerequest API buat ngepush data this.laporanFeatures + getDataLaporanMap = async () => { + let payload = { + project_id: [] + } + if (this.state.chosenProyekIds && this.state.chosenProyekIds.length > 0) { + payload.project_id = this.state.chosenProyekIds; + } + + const result = await axios + .post(DASHBOARD_REPORT_POINTS, payload, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code == 200) { + console.log("cek laporan points", result) + let dataRes = result.data.data || [] + this.setState({ laporanData: dataRes }, () => this.getLaporanFeatures()) + } + } + + getLaporanFeatures = async () => { + const { laporanData, checkedKeysProjectTree } = this.state; + if (checkedKeysProjectTree && checkedKeysProjectTree.length > 0) { + if (laporanData.length > 0) { + for (let i = 0; i < laporanData.length; i++) { + let item = laporanData[i]; + if (item) { + if (item.lat && item.lon) { + this.laporanFeatures.push({ + "type": "Feature", + "id": `report_activity.${item.id}`, + "properties": { + "id": item.id, + "user_id": item.user_id, + "nama_user": item.created_by, + "nama_proyek": item.proyek_name, + "jumlah_pekerjaan": item.jumlah_pekerjaan, + "pekerjaan_yang_dilaporkan": item.job_count_report, + "tanggal_lapor": item.report_date ? moment(item.report_date).format("YYYY-MM-DD HH:mm:ss") : '-', + // "mulai_tugas": item.mulai, + // "akhir_tugas": item.akhir, + "_type": "report_activity" + }, + "geometry": { + "type": "Point", + "coordinates": [ + parseFloat(item.lon), + parseFloat(item.lat) + ] + } + }) + } + } + } + + console.log('this.laporanFeatures', this.laporanFeatures); + + let layersToAdd = []; + let vectorSource = null; + let vectorLayer = null; + let laporan = null; + laporan = { + type: "FeatureCollection", + features: this.laporanFeatures + } + + // generate new layer + if (laporan && laporan.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(laporan, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'laporanLayer', + source_type: 'geojson', + source: vectorSource, + style: LAPORAN_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + else { + // toast.warn('Data laporan pada peta tidak ditemukan di project ini'); + } + + if (layersToAdd.length > 0) { + for (let i = 0; i < layersToAdd.length; i++) { + console.log('layersToAdd', layersToAdd[i]); + this.olmap.addLayer(layersToAdd[i]); // adding layer to exist map + console.log('check addLayer'); + if (i === layersToAdd.length - 1) { + let extent = await this.getExtentLayerByName(layersToAdd[i]); + if (extent) { + this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + } + } + } + } + } + } + + else if (checkedKeysProjectTree && checkedKeysProjectTree.length < 1) { + this.resetLayerDashboard(); + this.resetCharts(); + } + } + + // ngerequest API buat ngepush data this.presensiFeatures + getDataPresensiMap = async () => { + // let payload = { + // project_id: [] + // } + // if (this.state.chosenProyekIds && this.state.chosenProyekIds.length > 0) { + // payload.project_id = this.state.chosenProyekIds; + // } + + // const result = await axios + // .post('', payload, HEADER) + // .then(res => res) + // .catch((error) => error.response); + + // if (result && result.data && result.data.code == 200) { + // console.log("cek presensi points", result) + // let dataRes = result.data.data || [] + // this.setState({ presensiData: dataRes }, () => this.getPresensiFeatures()) + // } + + let dateStart = moment(this.state.startDate).format("YYYY-MM-DD 00:00:00"); + let dateEnd = moment(this.state.endDate).format("YYYY-MM-DD 23:59:59"); + + const payload = { + "paging": {"start": 0, "length": -1}, + "columns": [ + // {"name": "name", "logic_operator": "like", "value": search, "table_name": "m_users"}, + {"name": "clock_in", "logic_operator": "range", "value": dateStart, "value1": dateEnd}, + ], + "joins": [{ + "name":"m_users", + "column_join":"user_id", + "column_results":[ + "name", + "ktp_number" + ] + }], + "orders": {"columns": ["id"], "ascending": false} + } + + const result = await axios + .post(PRESENCE_SEARCH, payload, HEADER) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200) { + let dataRes = result.data.data || [] + this.setState({ presensiData: dataRes }, () => this.getPresensiFeatures()) + }else{ + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + getPresensiFeatures = async () => { + const { presensiData, checkedKeysProjectTree } = this.state; + console.log('getPresensiFeature presensiData', presensiData); + if (checkedKeysProjectTree && checkedKeysProjectTree.length > 0) { + if (presensiData.length > 0) { + for (let i = 0; i < presensiData.length; i++) { + let item = presensiData[i]; + if (item) { + if (item.clock_in_lat && item.clock_in_lng) { + this.presensiFeatures.push({ + "type": "Feature", + "id": `m_presensi.${item.id}`, + "properties": { + "id": item.id, + "user_id": item.user_id, + "name": item.created_by, // mandatory to fill in popup routing + "clock_in": item.clock_in ? moment(item.clock_in).format('D-M-YYYY HH:mm:ss') : '-', + "clock_out": item.clock_out ? moment(item.clock_out).format('D-M-YYYY HH:mm:ss') : '-', + "clock_in_location": item.clock_in_loc !== '' ? item.clock_in_loc : '-', + "clock_out_location": item.clock_out_loc !== '' ? item.clock_out_loc : '-', + // "nama_proyek": item.proyek_name, + // "mulai_tugas": item.mulai, + // "akhir_tugas": item.akhir, + "_type": "presensi" + }, + "geometry": { + "type": "Point", + "coordinates": [ + parseFloat(item.clock_in_lng), + parseFloat(item.clock_in_lat) + ] + } + }) + } + } + } + + console.log('this.presensiFeatures', this.presensiFeatures); + + let layersToAdd = []; + let vectorSource = null; + let vectorLayer = null; + let presensi = null; + presensi = { + type: "FeatureCollection", + features: this.presensiFeatures + } + + // generate new layer + if (presensi && presensi.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(presensi, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'presensiLayer', + source_type: 'geojson', + source: vectorSource, + style: PRESENSI_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + else { + // toast.warn('Data laporan pada peta tidak ditemukan di project ini'); + } + + if (layersToAdd.length > 0) { + for (let i = 0; i < layersToAdd.length; i++) { + console.log('layersToAdd', layersToAdd[i]); + this.olmap.addLayer(layersToAdd[i]); // adding layer to exist map + console.log('check addLayer'); + if (i === layersToAdd.length - 1) { + let extent = await this.getExtentLayerByName(layersToAdd[i]); + if (extent) { + this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + } + } + } + } + } + } + + else if (checkedKeysProjectTree && checkedKeysProjectTree.length < 1) { + this.resetLayerDashboard(); + this.resetCharts(); + } + } + + + // ngerequest API buat ngepush data chart + getChartData = async () => { + + const payload = { + "project_id": [], + "period": "week" + } + + if (this.state.chosenProyekIds.length > 0) { + console.log('chosenProyekIds', this.state.chosenProyekIds); + payload.project_id = this.state.chosenProyekIds; + } + + // get cost proyek + const result = await axios + .post(DASHBOARD_KURVA_S, payload, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code == 200) { + console.log("cek dashboard chart", result) + let dataRes = result.data.data || [] + // const labelCostPlaning = dataRes ? dataRes.map(res => res.total) : [] // ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] + // const valueCostPlaning = [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + // const valueCostRealisasi = [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + + // const costProyek = { + // labels: labelCostPlaning, + // datasets: [ + // { + // label: 'Perencanaan', + // data: valueCostPlaning, + // fill: false, + // backgroundColor: 'rgba(255, 99, 132, 0.5)', + // borderColor: 'rgba(255, 99, 132, 0.5)', + // yAxisID: 'y-axis-1', + // // stack: 'Stack-0' + // }, + // { + // label: 'Aktual', + // data: valueCostRealisasi, + // fill: false, + // backgroundColor: 'rgba(54, 162, 235, 0.5)', + // borderColor: 'rgba(54, 162, 235, 0.5)', + // yAxisID: 'y-axis-1', + // // stack: 'Stack-1' + // }, + // ], + // }; + this.setState({ dataCostProyek: dataRes }); + + } else { + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + + // get persentase progress proyek + const result2 = await axios + .post(DASHBOARD_KURVA_S, payload, HEADER) + .then(res => res) + .catch((error) => error.response); + + console.log('result kurva s', result2); + + if (result2 && result2.data && result2.data.code == 200) { + let dataRes2 = result2.data.data || [] + + // const labelPersentaseProyek = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] + // const valuePersentaseProyekPlan = [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + // const valuePersentaseProyekActual = [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + // const persentaseProyek = { + // labels: labelPersentaseProyek, + // datasets: [ + // { + // label: 'Perencanaan', + // data: valuePersentaseProyekPlan, + // fill: false, + // backgroundColor: 'rgba(255, 99, 132, 0.5)', + // borderColor: 'rgba(255, 99, 132, 0.5)', + // // stack: 'Stack 0' + // yAxisID: 'y-axis-1', + // }, + // { + // label: 'Aktual', + // data: valuePersentaseProyekActual, + // fill: false, + // backgroundColor: 'rgba(54, 162, 235, 0.5)', + // borderColor: 'rgba(54, 162, 235, 0.5)', + // // stack: 'Stack 1' + // yAxisID: 'y-axis-1', + // }, + // ], + // }; + // console.log("dataRes2", dataRes2); + // this.setState({ dataPersentaseProyek: dataRes2 }); + this.setState({dataCurvaS: dataRes2}); + console.log('dataRes2-----------', dataRes2); + + // set status proyek for adw + if ((APP_MODE === 'ADW')) { + let budgetControl = []; + if (dataRes2.length > 0) { + dataRes2.map((item, index) => { + let proyek_name = item.proyek_name; + item.data.budget_control.proyek_name = proyek_name; // adding key proyek_name in budget_control object + budgetControl.push(item.data.budget_control); + }); + } + this.setState({dataStatusProyekAdw: budgetControl}); + } + } + else { + NotificationManager.error('Gagal Mengambil Data Persentase Progress Proyek!!', 'Failed'); + } + + // const valueStatusProyek = [70, 20, 10] + // const statusProyek = { + // labels: ['Aman', 'Alert', 'Critical'], + // datasets: [ + // { + // label: '# of Votes', + // data: valueStatusProyek, + // backgroundColor: [ + // // 'rgba(54, 162, 235, 0.2)', + // 'rgba(28, 165, 23, 0.2)', + // 'rgba(255, 206, 86, 0.2)', + // 'rgba(255, 99, 132, 0.2)', + // ], + // borderColor: [ + // // 'rgba(54, 162, 235, 1)', + // 'rgba(28, 165, 23, 1)', + // 'rgba(255, 206, 86, 1)', + // 'rgba(255, 99, 132, 1)', + // ], + // borderWidth: 1, + // }, + // ], + // }; + + + // const statusProyek = [ + // { + // "id": "1", + // "proyek_name": "Pembuatan Aplikasi Survey Covid Varian Baru", + // "total_task": 80, + // "task_on_progress": 60, + // "day_left": 7, + // "percentage": 75 + // }, + // { + // "id": "2", + // "proyek_name": "Pembuatan Aplikasi Survey Covid Varian Baru", + // "total_task": 70, + // "task_on_progress": 70, + // "day_left": 0, + // "percentage": 100 + // }, + // { + // "id": "3", + // "proyek_name": "Pembuatan Aplikasi Survey Covid Varian Baru", + // "total_task": 70, + // "task_on_progress": 70, + // "day_left": 0, + // "percentage": 100 + // } + // ]; + + // get cost proyek + const result3 = await axios + .post(DASHBOARD_STATUS_PROYEK, payload, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result3 && result3.data && result3.data.code == 200) { + let dataRes3 = result3.data.data || []; + console.log(" dataRes3 ", dataRes3) + this.setState({ dataStatusProyek: dataRes3 }); + } + else { + NotificationManager.error('Gagal Mengambil Data Status Proyek!!', 'Failed'); + } + + if ((APP_MODE === 'ADW')) { + // request to API get status proyek for ADW and save to dataStatusProyekAdw state + // cek di result2 + } + } + + getDailyInfo = async () => { + const payload = { + "columns": [ + { "name": "created_at", "logic_operator": "range", "value": `${moment().utc().format('YYYY-MM-DD')} 00:00:00`, "value1": `${moment().utc().format('YYYY-MM-DD')} 23:59:59`, "operator": "AND" } + ], + "paging": { "start": 0, "length": -1 } + } + + const config = { + method: 'POST', // *GET, POST, PUT, DELETE, etc. + // mode: 'cors', // no-cors, *cors, same-origin + // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + // credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + localStorage.getItem('token') + // 'Content-Type': 'application/x-www-form-urlencoded', + }, + // redirect: 'follow', // manual, *follow, error + // referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: JSON.stringify(payload) // body data type must match "Content-Type" header + } + try { + const result = await fetch(DASHBOARD_PROYEK_SEARCH, config).then(response => response.json()).then(res => res); + console.log('getDailyInfo result', result); + if (result.code === 200 && result.data) { + const dataSum = result.data; + // this.setState({ + // planning: dataSum.planning, + // realisasi: dataSum.realisasi, + // status_project: dataSum.status_project, + // waspang_status: dataSum.waspang_status, + // panic_button: dataSum.panic_button, + // isReady: true + // }); + + const statusWaspang = { + labels: ['Hadir', 'Izin', 'Panic Button'], + datasets: [ + { + label: '# of Votes', + data: [dataSum.waspang_status.presensi, dataSum.waspang_status.absensi, dataSum.panic_button], + backgroundColor: [ + 'rgba(77,232,0, 0.2)', + 'rgba(255,146,3, 0.2)', + 'rgba(164,7,120, 0.2)', + ], + borderColor: [ + 'rgba(77,232,0, 1)', + 'rgba(255,146,3, 1)', + 'rgba(164,7,120, 1)', + ], + borderWidth: 1, + }, + ], + }; + this.setState({ dataStatusWaspang: statusWaspang, allDataWaspang: result.data }); + } + } + catch (e) { + toast.error('Gagal mengambil data'); + } + } + + getLayerSearchLabel = async () => { + const param = { + method: 'GET', + header: JSON.stringify({ 'Content-Type': 'application/json' }), + } + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL, param).then(response => response.json()).then(res => res) + if (result.data) { + if (result.data.length > 0) { + // console.log('getLayerSearchLabel result', result); + this.setState({ + searchLabelData: result.data + }, () => console.log('getLayerSearchLabel searchLabelData', this.state.searchLabelData)); + } + } else { + + } + } catch (err) { + console.log(err); + // alert(err.message.toString()); + // toast.warn(err.message.toString()); + } + } + + + getLayerInfo = async () => { + let layerInfo = []; + const param = { + method: 'GET', + header: JSON.stringify({ 'Content-Type': 'application/json' }), + } + + try { + const result = await fetch(API_GET_CHART_KATEGORI, param).then(response => response.json()).then(res => res) + // console.log(result) + + if (result.data) { + if (result.data.length > 0) { + for (let i = 0; i < result.data.length; i++) { + let layer_name = result.data[i].layer_name; + let layer_title = result.data[i].layer_title; + let layer_geom_type = result.data[i].layer_geom_type; + let total_features = result.data[i].total_features; + + let SLD_URL = `${layerStyleUrl + layer_name}`;; + let reqColor = await getLayerColor(SLD_URL); + let color = ''; + if (reqColor.success) { + color = reqColor.result; + } + layerInfo.push({ + layer_name: layer_name, + layer_title: layer_title, + layer_geom_type: layer_geom_type, + layer_color: color, + total_features: total_features + }); + } + this.setState({ layerInfo: layerInfo }, () => console.log('layerInfo', this.state.layerInfo)); + } + // this.setState({alert: true, messageAlert: result.code_message, successAlert: true, dangerAlert: false}) + } else { + // this.setState({chartKategori: pie}); + // this.setState({alert: true, messageAlert: result.code_message, successAlert: false, dangerAlert: true}) + } + } catch (err) { + console.log(err); + // alert(err.message.toString()); + // toast.warn(err.message.toString()); + // this.setState({alert: true, messageAlert: err.message.toString(), successAlert: false, dangerAlert: true}) + } + } + + setDefaultMap = () => { + this.olmap.getView().animate({ + zoom: zoom, + maxZoom: maxZoom, + center: Indonesia + }); + } + + changeBaseLayer(item) { + console.log('change baselayer', item); + // console.log(this.olmap.getLayers()); + if (this.olmap.getLayers().values_.length == 0) { + // check if layers empty, so just insert base layer to position 0 + this.olmap.getLayers().insertAt(0, item); + } + // check if layer exist + else if (this.olmap.getLayers().values_.length > 0) { + // check the position 0, if base layer then replace to new + if (this.olmap.getLayers().array_[0].get('type') === 'base') { + this.olmap.getLayers().removeAt(0); + this.olmap.getLayers().insertAt(0, item); + } + // else just insert base layer at position 0 + else { + this.olmap.getLayers().insertAt(0, item); + } + } + } + + getHomeView = () => { + // this should request to siopas map api to get current map + // this.olmap.getView().fit(new transformExtent(Bali_bbox, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + const { mapZoom, mapCenter } = this.state; + if (mapZoom && mapCenter !== null) { + this.olmap.getView().animate({ + zoom: mapZoom, + center: mapCenter + }); + } + else { + // alert("Map Zoom and Map Center are not set yet"); + this.olmap.getView().animate({ + zoom: zoom, + maxZoom: maxZoom, + center: Indonesia + }); + } + } + + mapOnClick = (evt) => { + evt.preventDefault(); + let isDrawing = false; + let removedFeature = []; + let isRemoving = false; + let transformedCoords4326 = transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326'); + // console.log('transformedCoords4326', transformedCoords4326); + + let mapInteractions = []; + this.olmap.getInteractions().forEach((interaction) => { + // console.log('interaction', interaction); + if (interaction instanceof Draw) { + console.log('drawing is active!!'); + isDrawing = true; + } + // if (interaction instanceof Select) { + // console.log('select interaction is active'); + // isRemoving = true; + // } + + if (interaction instanceof Modify) { + console.log('modify feature is active'); + isDrawing = true; + } + }); + + if (isDrawing) { + return; + } + + let viewResolution = this.olmap.getView().getResolution(); + let viewProjection = this.olmap.getView().getProjection(); + let url = ''; + let promises = []; + let featureGet = []; + + let hitGeojson = null; + + this.olmap.getLayers().forEach((layer, i) => { + console.log('layer', layer.get('name')); + if (layer.get('type') !== 'base') { + if (layer.get('type') == 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + url = sublayer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, { 'INFO_FORMAT': 'application/json' }); + if (url) { + promises.push(axios.get(url)); + } + } + }); + } + else { + if (layer.getVisible()) { + if (layer.get('name') !== 'ChosenLayer') { + if (layer.get('source_type') && layer.get('source_type') === 'geojson') { + let layerSource = layer.getSource(); + hitGeojson = this.olmap.getFeaturesAtPixel(evt.pixel); + console.log('hitGeojson', hitGeojson); + } + else if (layer.get('source_type') === "wms") { + url = layer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, { 'INFO_FORMAT': 'application/json' }); + if (url) { + promises.push(axios.get(url)); + } + } + } + } + } + } + }); + + + if (hitGeojson && hitGeojson.length > 0) { + for (let i = 0; i < hitGeojson.length; i++) { + let feature = hitGeojson[i]; + + let feat = { + "type": "Feature", + "id": "", + "geometry": { + "type": "", + "coordinates": [] + }, + "geometry_name": "the_geom", + "properties": {} + } + + feat.id = feature.id_; + feat.geometry.type = feature.getGeometry().getType(); + // feat.geometry.coordinates = transform(feature.getGeometry().getCoordinates(), 'EPSG:3857', 'EPSG:4326'); + feat.geometry.coordinates = feature.getGeometry().getCoordinates(); + feat.properties = feature.getProperties(); + delete feat.properties["geometry"]; + + console.log('feat', feat); + featureGet.push(feat); + } + + console.log('featureGet geojson', featureGet); + } + + + // kalo dari WMS + if (promises.length > 0) { + axios.all(promises).then((results) => { + results.forEach((response) => { + console.log('mapOnClick response promises', response); + if (response.data !== undefined) { + if (response.data.features.length > 0) { + for (let i = 0; i < response.data.features.length; i++) { + featureGet.push(response.data.features[i]); + } + } + } + }) + + + console.log('featureGet WMS', featureGet); + + // adding it here because it's trapped on axios callback + this.setState({ + popupDataTemp: featureGet, + evtCoordinate: evt.coordinate + }, () => this.setActiveListFeature()); + }); + } + + if (hitGeojson && promises) { + this.setState({ + popupDataTemp: featureGet, + evtCoordinate: evt.coordinate + }, () => this.setActiveListFeature()); + } + } + + removeChosenLayer = () => { + this.olmap.getLayers().forEach((layer, i) => { + if (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'ChosenLayer') { + layer.getSource().clear(); + this.olmap.removeLayer(layer); + } + } + }); + } + + openPopupRight() { + // console.log('opening popup right...') + this.setState({ popupRightVisible: true }, () => this.setActiveListFeature()); + } + + closePopupRight() { + // console.log('closing popup right...') + this.setState({ popupRightVisible: false, popupDataTemp: [] }, () => this.setActiveListFeature()); + this.removeChosenLayer(); // selected features + // this.removeLayerByName('routeLayer'); + this.setState({ editGeometryVisible: false, routingBarVisible: false }); // disable editing when no ChosenLayer on Map + } + + /*toggleImagePopup() { + this.setState({imagePopupVisible: !this.state.imagePopupVisible}); + }*/ + + setPopupDataTemp = (feature) => { + // console.log('setPopupDataTemp', feature); + this.setState({ popupDataTemp: [feature] }, () => this.setActiveListFeature()); + } + + reloadPopupData = () => { + const { evtCoordinate } = this.state; + } + + setActiveListFeature = () => { + // console.log('this.state.popupDataTemp', this.state.popupDataTemp); + if (this.state.popupRightVisible) { + if (this.state.popupDataTemp.length === 1) { + this.setState({ activeListFeatureId: this.state.popupDataTemp[0].id }, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + let layerName = this.state.activeListFeatureId ? this.state.activeListFeatureId.substr(0, this.state.activeListFeatureId.indexOf('.')) : ''; + this.getLayerAttribute(layerName); + }) + } + else { + this.setState({ activeListFeatureId: '' }, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + }) + } + } + else { + this.setState({ activeListFeatureId: '' }, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + }) + } + } + + // getGeomType = async (layerName) => { + + // let res = await getGeomType(layerName); + // console.log('getGeomType', res); + // return res; + // } + + loadMap = async () => { + let response = await axios.get(API_LOAD_MAP).then(res => res).catch(error => error); + console.log('loadMap', response); + // this.getHomeView(response); + // this.olmap.getView().fit(new transformExtent(Bali_bbox, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + if (response.data !== undefined) { + if (response.data.length > 0) { + let dataMap = response.data[0]; + let layersToRemove = []; + + // if the API response about zoom is null + if (dataMap.zoom == null) { + this.setDefaultMap(); + } + // if the API response about center_x and center_y is null + else if (dataMap.center_x == null && dataMap.center_y == null) { + this.setDefaultMap(); + } + // otherwise, add layer to map from map_layer response + else { + // set the zoom and center + this.setState({ mapZoom: dataMap.zoom, mapCenter: [dataMap.center_x, dataMap.center_y] }); + this.olmap.getView().animate({ + zoom: dataMap.zoom, + center: [dataMap.center_x, dataMap.center_y] + }); + + // if map_layers from API is exist + if (dataMap.map_layers.length > 0) { + // first, removing all layers from the current map + this.olmap.getLayers().forEach((layer, i) => { + layersToRemove.push(layer); + }); + if (layersToRemove.length > 0) { + for (let i = 0; i < layersToRemove.length; i++) { + this.olmap.removeLayer(layersToRemove[i]); + } + } + + // add map_layers from api to view + if (this.olmap.getLayers().array_.length < 1) { + // console.log('layer empty', response.data); + let map_layers = dataMap.map_layers; + if (map_layers !== undefined) { + // console.log('ada map_layers', map_layers); + if (map_layers.length > 0) { + for (let i = 0; i < map_layers.length; i++) { + let newLayer = null; + + // if layer_type is not base + if (map_layers[i].layer_type !== 'base') { + newLayer = new ImageLayer({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new OlSourceImageWMS(map_layers[i].layer_source), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + // if layer_type is base + else { + // if base is OSM (OlSourceOsm); + if (map_layers[i].layer_name == 'OSM') { + newLayer = new OlLayerTile({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new OlSourceOsm(), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + // if base is other than OSM + else { + newLayer = new OlLayerTile({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new XYZSource(map_layers[i].layer_source), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + } + + // console.log('adding new layer', newLayer); + + // add the map_layers from API + this.olmap.addLayer(newLayer); + } + } + } + } + } + } + } + } + } + + saveMap = () => { + /* What map update data that are needed? + - title of map (o) + - zoom + - projection + - center_x + - center_y + - base_layer_id + - mapLayers[] + - layer_name + - layer_type (base / layer) + - layer_geom_type + - layer_source + - layer_visible + - layer_position + + Example of layer_source: + { + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sekolah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sekolah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous' + } + */ + let confirmation = window.confirm('Are you sure you want to save this map?'); + // let mapId = localStorage.getItem('u_group') === "kominfo" ? 2 : 1; + let mapId = MAP_ID; // get from m_group + let mapTitle = localStorage.getItem('u_group') + "_map"; // get from m_group + let mapZoom = this.olmap.getView().getZoom(); + let mapProjection = this.olmap.getView().getProjection().code_; + let mapCenter = this.olmap.getView().getCenter(); + let center_x = mapCenter[0]; // longitude + let center_y = mapCenter[1]; // latitude + let mapLayers = []; + let requestPayload = null; + let layerType = ''; + let layerGeomType = ''; + let count = 0; + + if (confirmation) { + this.olmap.getLayers().forEach(async (layer, i) => { + + // console.log('layer after confirmation i', i, layer); + layerType = layer.get('type') !== undefined ? layer.get('type') : 'layer'; + + if (layer.get('name') !== "DrawingLayer" && layer.get('name') !== "ChosenLayer") { + if (layer.get('type') === "base") { + if (layer.get('name') === "OSM") { + // if the baselayer is OSM, use the OSM function from openlayers + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: 'base', + layer_source: "OlSourceOsm", // i don't know why null in database + layer_visible: layer.getVisible(), + layer_position: i + }); + } + else { + // or if the baselayer is other than OSM (such as ESRI, Google, etc), use the XYZSource + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: 'base', + layer_source: { + // url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + url: layer.getSource().urls[0], + projection: mapProjection, + maxZoom: 18 + }, + layer_visible: layer.getVisible(), + layer_position: i + }); + } + } + else { + // if the layer is not base layer + // layerGeomType = layer.get('geom_type') !== undefined ? layer.get('geom_type') : await this.getGeomType(layer.get('name')); + let reqGeomType = await getGeomType(layer.get('name')); + if (reqGeomType.success) { + layerGeomType = reqGeomType.result; + + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: layerGeomType, + layer_source: { + url: appConfig.geoserver_host + 'wms', + // url: appConfig.geoserver_host+'gwc/service/wms?SERVICE=WMS', + params: { + 'LAYERS': appConfig.workspace_name + ':' + layer.get('name'), + 'TILED': true, + 'SLD': layerStyleUrl + layer.get('name') + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous' + }, + layer_visible: layer.getVisible(), + layer_position: i + }); + } + } + } + count = count + 1; + + // the last loop, so send to updateMap API + // console.log('count', count); + // console.log('this.olmap.getLayers().array_.length',this.olmap.getLayers().array_.length); + // console.log('if count==this.olmap.getLayers().array_length', count == this.olmap.getLayers().array_.length); + if (count == this.olmap.getLayers().array_.length) { + console.log('terakhirrrrrr'); + requestPayload = { + 'map_id': mapId, + 'map_title': mapTitle, + 'map_zoom': mapZoom, + 'map_projection': mapProjection, + 'center_x': center_x, + 'center_y': center_y, + 'map_layers': mapLayers + }; + console.log('requestPayload', requestPayload); + this.saveMapToApi(requestPayload); + } + }); + } + } + + saveMapToApi = async (requestPayload) => { + + const param = { + method: 'POST', + header: JSON.stringify({ 'Content-Type': 'application/json' }), + body: JSON.stringify(requestPayload) + } + + try { + const result = await fetch(API_UPDATE_MAP, param).then(response => response.json()).then(res => res) + if (result.data) { + console.log('after save', result); + // this.(); + this.setState({ alert: true, messageAlert: result.code_message, successAlert: true, dangerAlert: false }) + } else { + this.setState({ alert: true, messageAlert: result.code_message, successAlert: false, dangerAlert: true }) + } + } catch (err) { + this.setState({ alert: true, messageAlert: err.message.toString(), successAlert: false, dangerAlert: true }) + } + } + + toggleEditGeometry = (selectedPopupData) => { + let { editGeometryVisible } = this.state; + if (!editGeometryVisible) { + this.setState({ + editGeometryVisible: true, + layerNameDraw: selectedPopupData.id, + geomTypeDraw: selectedPopupData.geometry.type + }); + + } + else { + this.cancelDraw(); + } + } + + cancelDraw = () => { + this.setState({ + editGeometryVisible: false, + layerNameDraw: '', + geomTypeDraw: '' + }); + } + + toggleActiveStateAddGeometry = () => { + this.setState({ activeStateAddGeometry: !this.state.activeStateAddGeometry }); + } + + printMap = () => { + this.olmap.once('rendercomplete', () => { + domtoimage + .toJpeg(this.olmap.getViewport()) + .then((dataUrl) => { + console.log('dataUrl', dataUrl); + let link = document.getElementById('image-download'); + link.href = dataUrl; + link.click(); + toast.success("Success Print Map!") + }) + .catch(e => toast.error(e.toString())); + }); + + } + + signOut = (e) => { + e.preventDefault() + // localStorage.removeItem("u_group"); + // localStorage.removeItem("fullname"); + window.localStorage.clear(); + // emptyConstants(); + this.props.history.push('/login'); + } + + getLayerAttribute = async (layerName) => { + const res = await getLayerAttribute(layerName); + console.log('getLayerAttribute', res); + if (res.success) { + // console.log(res.result); + if (res.result.data) { + if (res.result.data.length > 0) { + this.setState({ layer_attribute: res.result.data }, () => { + console.log(this.state.layer_attribute); + }); + } + } + } + else { + // alert(res.result); + toast.warn(res.result); + } + } + + // when checking chekbox on Layer Tree Panel + onCheckOpt = (state, checkedKeys) => { + this.setState({ [state]: checkedKeys }); + } + + setLayer = async (state) => { + console.log('setLayer', state); + + await this.setState({ isProcessing: true }); + + this.closePopupRight(); + + const { checkedKeysSales, checkedKeysCustomer, checkedKeysOffice, checkedKeysDemografi, checkedKeysAnalisa, checkedKeysEmployeeDivision, + salesGroupTree, employeeDivisionTree, queryBuilderOutput, queryBuilderType, checkedKeysProjectTree, projectTree } = this.state; + let layersToAdd = []; + let newLayer = null; + let vectorSource = null; + let vectorLayer = null; + + if (state === 'checkedKeysProjectTree') { + console.log('checkedKeysProjectTree', checkedKeysProjectTree); + + // first remove projectLayer and its features + + // this.removeLayerByName('routeLayer'); + // this.removeLayerByName('projectLayer'); + // this.removeLayerByName('waspangLayer'); + // this.projectFeatures = []; + // this.waspangFeatures = []; + this.resetLayerDashboard(); + + this.getChosenProyekRealisasi(); + + if (checkedKeysProjectTree && checkedKeysProjectTree.length > 0) { + + /* + Logic: + - looping all tree + - there are 3 variables + 1. features (Array) -> untuk nampung features yg ada di laporan planning + 2. projectTree (Array) + 3. checkedKeysProjectTree (Array) -> projectTree yg tercentang + + Jadi looping all nesting yg ada di projectTree (ambil object children) + Terus cek setiap levelnya dia ada di checkedKeysProjectTree gak? + Jika ada, cek apakah di object tersebut mengandung key namanya "plannings" tidak? + Jika iya, maka cek di dalamnya ada laporan_planning tidak? + Jika iya, maka push ke features[] dengan format: + { + "type": "Feature", + "properties": {}, // isian dari objectnya + "geometry": { + "type": "Point", + "coordinates": [ + 107.90771484375, // object.lon + -6.795535025719518 // object.lat + ] + } + } + */ + + this.getChildrenTree(projectTree[0].children); + // this.getWaspangFeatures(); + + console.log('projectFeatures', this.projectFeatures); + // console.log('waspangFeatures', this.waspangFeatures); + + let project = null; + project = { + type: "FeatureCollection", + features: this.projectFeatures + } + + // console.log('project', project); + + + // generate new layer + if (project && project.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(project, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'projectLayer', + source_type: 'geojson', + source: vectorSource, + style: PROJECT_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + else { + // toast.warn('Data realisasi tidak ditemukan di project ini'); + } + + } + else if (checkedKeysProjectTree && checkedKeysProjectTree.length < 1) { + this.removeLayerByName('projectLayer'); + this.removeLayerByName('waspangLayer'); + this.removeLayerByName('routeLayer'); + this.projectFeatures = []; + this.waspangFeatures = []; + this.closePopupRight(); + this.resetCharts() + } + } + + if (layersToAdd.length > 0) { + for (let i = 0; i < layersToAdd.length; i++) { + + this.olmap.addLayer(layersToAdd[i]); // adding layer to exist map + + console.log('check addLayer'); + if (i === layersToAdd.length - 1) { + let extent = await this.getExtentLayerByName(layersToAdd[i]); + if (extent) { + this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + } + } + } + } + await this.setState({ isProcessing: false }); + } + + // find the lat lon inside laporan_plannings that the planning has been checked on projectTree + getChildrenTree = (data) => { + data.map((item, index) => { + if (item.children && item.children.length > 0) { + this.getChildrenTree(item.children); + } + else if (item.laporan_plannings && item.laporan_plannings.length > 0) { + if (this.state.checkedKeysProjectTree.includes(item.key)) { + for (let i = 0; i < item.laporan_plannings.length; i++) { + console.log('got features!!!!!'); + this.projectFeatures.push({ + "type": "Feature", + "id": `realisasi.${item.laporan_plannings[i].id}`, + "properties": { ...item.laporan_plannings[i] }, + "geometry": { + "type": "Point", + "coordinates": [ + item.laporan_plannings[i].lon, + item.laporan_plannings[i].lat + ] + } + }) + } + } + } + }); + } + + // getWaspangFeatures = async () => { + // // console.log('getWaspangFeatures', data); + // const { waspangData, checkedKeysProjectTree } = this.state; + // if (checkedKeysProjectTree && checkedKeysProjectTree.length > 0) { + // if (waspangData.length > 0) { + // for (let i = 0; i < waspangData.length; i++) { + // let item = waspangData[i]; + // if (item.last_waypoint) { + // this.waspangFeatures.push({ + // "type": "Feature", + // "id": `m_waspang.${item.id}`, + // "properties": { + // "id": item.id, + // "user_id": item.user_id, + // "nama_user": item.join.m_users_name, + // "nama_proyek": item.join.m_proyek_nama, + // "mulai_tugas": item.mulai, + // "akhir_tugas": item.akhir, + // "_type": "waspang" + // }, + // "geometry": { + // "type": "Point", + // "coordinates": [ + // item.last_waypoint.lon, + // item.last_waypoint.lat + // ] + // } + // }) + // } + // } + + // console.log('this.waspangFeatures', this.waspangFeatures); + + // let layersToAdd = []; + // let vectorSource = null; + // let vectorLayer = null; + // let waspang = null; + // waspang = { + // type: "FeatureCollection", + // features: this.waspangFeatures + // } + + // // generate new layer + // if (waspang && waspang.features.length > 0) { + // vectorSource = new VectorSource({ + // features: new GeoJSON().readFeatures(waspang, { + // dataProjection: projection4326, // from + // featureProjection: projection // to + // }) + // }) + + // vectorLayer = new VectorLayer({ + // name: 'waspangLayer', + // source_type: 'geojson', + // source: vectorSource, + // style: WASPANG_FEATURES_STYLE + // }); + + // layersToAdd.push(vectorLayer); + // } + // else { + // toast.warn('Data human resource tidak ditemukan di project ini'); + // } + + // if (layersToAdd.length > 0) { + // for (let i = 0; i < layersToAdd.length; i++) { + + // this.olmap.addLayer(layersToAdd[i]); // adding layer to exist map + + // console.log('check addLayer'); + // if (i === layersToAdd.length - 1) { + // let extent = await this.getExtentLayerByName(layersToAdd[i]); + // if (extent) { + // this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + // } + // } + // } + // } + + // } + // } + + // else if (checkedKeysProjectTree && checkedKeysProjectTree.length < 1) { + // this.removeLayerByName('projectLayer'); + // this.removeLayerByName('waspangLayer'); + // this.removeLayerByName('routeLayer'); + // this.projectFeatures = []; + // this.waspangFeatures = []; + // this.closePopupRight(); + // this.resetCharts(); + // } + + // // this.waspangFeatures = [{ + // // "type": "Feature", + // // "id": `m_waspang.1`, + // // "properties": { + // // "id": 1, + // // "user_id": 10, + // // "nama_user": "Ryan", + // // "nama_proyek": "FTTH Paket 2", + // // "mulai_tugas": "2021-11-01T09:31:32.775Z", + // // "akhir_tugas": "2021-11-30T09:31:32.775Z", + // // "_type": "waspang" + // // }, + // // "geometry": { + // // "type": "Point", + // // "coordinates": [ + // // 106.89285278320312, + // // -6.228275226686636 + // // ] + // // } + // // }, + // // { + // // "type": "Feature", + // // "id": `m_waspang.2`, + // // "properties": { + // // "id": 2, + // // "user_id": 12, + // // "nama_user": "Effendi", + // // "nama_proyek": "FTTH Paket 2", + // // "mulai_tugas": "2021-11-01T09:31:32.775Z", + // // "akhir_tugas": "2021-11-30T09:31:32.775Z", + // // "_type": "waspang" + // // }, + // // "geometry": { + // // "type": "Point", + // // "coordinates": [ + // // 106.9357681274414, + // // -6.267522831839373 + // // ] + // // } + // // }, + // // { + // // "type": "Feature", + // // "id": `m_waspang.3`, + // // "properties": { + // // "id": 1, + // // "user_id": 16, + // // "nama_user": "Waspang C", + // // "nama_proyek": "FTTH Paket 2", + // // "mulai_tugas": "2021-11-01T09:31:32.775Z", + // // "akhir_tugas": "2021-11-30T09:31:32.775Z", + // // "_type": "waspang" + // // }, + // // "geometry": { + // // "type": "Point", + // // "coordinates": [ + // // 106.80564880371092, + // // -6.263086293713913 + // // ] + // // } + // // }] + // } + + findChildLayerToRemove = (parentObj) => { + let layersToRemove = []; + if (parentObj.hasOwnProperty('children')) { + // get semua children layer name nya + for (let i = 0; i < parentObj.children.length; i++) { + layersToRemove.push(parentObj.children[i].layers.name) + } + } + else { + layersToRemove.push(parentObj.layers.name) + } + return layersToRemove; + } + + findChildLayerToAdd = (parentObj, checkedKeys) => { + + let layersToAdd = []; + let newLayer = null; + + // 1. cek apakah parentObj itu kecentang atau ngga + // 2. cek apakah parentObj itu punya child apa ngga + // kalo kecentang parent nya ya berarti loop aja + // 3. cek apakah childnya itu kecentang ngga + + console.log('parentObj', parentObj); + + if (checkedKeys.includes(parentObj["key"])) { + if (parentObj.hasOwnProperty('children')) { + // loop aja semua child layernya + for (let i = 0; i < parentObj.children.length; i++) { + newLayer = this.generateLayerWMSByName(parentObj.children[i].layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + else { + newLayer = this.generateLayerWMSByName(parentObj.layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + else { + if (parentObj.hasOwnProperty('children')) { + + } + + } + + return layersToAdd; + + } + + getExtentLayerByName = async (layer) => { + + // console.log('getExtentLayerByName', layerName, source_type); + + let layerName = layer.get('name'); + let source_type = layer.get('source_type'); + + // if source_type is undefined = WMS + // otherwise source_type is not undefined = geojson + + let extent = null; + + if (source_type === 'wms') { + let getExt = await fetch(WMS_CAPABILITIES_URL_2).then((response) => { + return response.text(); + }).then((text) => text); + + let result = new WMSCapabilities().read(getExt); + if (result && result.Capability.Layer.Layer.find(l => l.Name === appConfig.workspace_name + ':' + layerName)) { + extent = result.Capability.Layer.Layer.find(l => l.Name === appConfig.workspace_name + ':' + layerName).EX_GeographicBoundingBox; + } + } + else if (source_type === 'geojson') { + // extent = layer.getSource().getExtent(); + + // layer.getSource().once('change',(e) => { + // if (layer.getSource().getState() === 'ready') { + // extent = layer.getSource().getExtent(); + // console.log('extent------------', extent); + // // map.getView().fit(extent, map.getSize()); + // } + // }); + extent = null + } + console.log('extent', extent); + return extent; + + } + + generateLayerWMSByName = (layerName) => { + let theLayer = null; + if (layerName) { + theLayer = new OlLayerTile({ + name: layerName, + source_type: 'wms', + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host + 'wms', + params: { + 'LAYERS': appConfig.workspace_name + ':' + layerName, + 'TILED': true, + 'SLD': layerStyleUrl + layerName, + // 'CQL_FILTER': CQL_QUERY + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + }), + type: 'layer', + geom_type: 'Polygon' + }); + } + return theLayer; + } + + clearMapLayers = () => { + // console.log('clearing layers'); + // clear map. removing all layer other than OSM + let removeLayers = []; + this.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') !== 'OSM') { + removeLayers.push(layer); + } + }); + // console.log('removeLayers', removeLayers); + if (removeLayers.length > 0) { + for (let i = 0; i < removeLayers.length; i++) { + this.olmap.removeLayer(removeLayers[i]); + } + } + } + + removeLayerByName = (name) => { + + let removeLayers = []; + this.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === name) { + removeLayers.push(layer); + } + }); + // console.log('removeLayers', removeLayers); + if (removeLayers.length > 0) { + for (let i = 0; i < removeLayers.length; i++) { + this.olmap.removeLayer(removeLayers[i]); + } + } + } + + getRoute = () => { + fetch(routeDummy).then((response) => { + response.json().then((result) => { + console.log('getRoute', result); + }) + }); + } + + handleGetPercentagePerDay = async () => { + console.log("GET_PERCENTAGE_PERDAY", GET_PERCENTAGE_PERDAY); + const result = await axios.post(GET_PERCENTAGE_PERDAY, HEADER).then(res => res).catch(err => err.response) + console.log("result", result); + if (result.data.code === 200) { + let dataRes = result.data.data + console.log("dataRes", dataRes); + } else { + NotificationManager.error('Gaga mengambil Data!!', 'Failed'); + } + } + + showRoute = (userRoute) => { + const { mapProjection } = this.state; + console.log('showRoute', userRoute); + + this.removeLayerByName('routeLayer'); + if (userRoute.features && userRoute.features.length < 1) { + toast.warn("Couldn't show route at selected time. Please select another range time."); + return; + } + + let route = null; + let polyline = null; + let extent = null; + + // polyline = routeDummy.routes[0].overview_polyline.points; + // console.log('polyline', polyline); + + // route = new Polyline({ + // }).readGeometry(polyline, { + // dataProjection: 'EPSG:4326', + // featureProjection: 'EPSG:4326', + // }); + + // let route = new Polyline().readGeometry(polyline); + + // polyline = { + // "type": "LineString", + // "coordinates": [ + // [ + // 106.78853, + // -6.26235 + // ], + // [ + // 106.78863, + // -6.26201 + // ], + // [ + // 106.80065, + // -6.24523 + // ] + // ] + // } + + // console.log('check route', userRoute.features.length > 0 && (userRoute.features[0].geometry && userRoute.features[0].geometry.coordinates)); + + if (userRoute.features.length > 0 && (userRoute.features[0].geometry && userRoute.features[0].geometry.coordinates)) { + // polyline = userRoute.features[0].geometry; + polyline = { + "type": "LineString", + "coordinates": [] + } + + for (let i=0; i < userRoute.features.length; i++) { + polyline.coordinates.push(userRoute.features[i].geometry.coordinates); + } + + route = new LineString(polyline.coordinates).transform('EPSG:4326', mapProjection); + extent = route.getExtent(); + + console.log('route', route); + console.log('extent', extent); + + let routeFeature = new Feature({ + type: 'route', + geometry: route, + geometry_name: userRoute.features[0].geometry_name, + id: userRoute.features[0].id, + properties: userRoute.features[0].properties, + routeColor: getRandomColor() + }); + let geoMarker = new Feature({ + type: 'geoMarker', + geometry: new Point(route.getCoordinateAt(0)), + id: userRoute.features[0].id, + properties: userRoute.features[0].properties + }); + let startMarker = new Feature({ + type: 'pinRouteStart', + geometry: new Point(route.getCoordinateAt(0)), + id: userRoute.features[0].id, + properties: userRoute.features[0].properties + }); + let endMarker = new Feature({ + type: 'pinRouteEnd', + geometry: new Point(route.getCoordinateAt(1)), + id: userRoute.features[0].id, + properties: userRoute.features[0].properties + }); + + let animating = false; + + let vectorLayer = new VectorLayer({ + name: 'routeLayer', + source_type: 'routeLayer', + source: new VectorSource({ + features: [routeFeature, geoMarker, startMarker, endMarker], + }), + style: (feature, resolution) => { + // hide geoMarker if animation is active + if (animating && feature.get('type') === 'geoMarker') { + return null; + } + // return ROUTE_MAP_STYLES[feature.get('type')]; + return ROUTE_MAP_STYLES(feature, resolution); + }, + }); + + this.olmap.addLayer(vectorLayer); + + if (extent) { + // this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + this.olmap.getView().fit(extent, { size: this.olmap.getSize(), duration: 500 }); + + } + } + } + + searchRouting = async (userId, dateString) => { + await this.setState({ isSearchingRoute: true, isProcessing: true }); + + const { routeType } = this.state; + let routes = null; + + if (routeType === 'waspang') { + routes = await getWaspangRoutingApi(userId, dateString); + } + + if (routeType === 'presensi') { + routes = await getPresensiRoutingApi(userId, dateString); + } + + if (routes) { + this.setState({ isSearchingRoute: false, isProcessing: false }, () => this.showRoute(routes)) + } + else { + this.setState({ isSearchingRoute: false, isProcessing: false }, () => toast.warn("Sorry. Couldn't get user waypoint")); + } + } + + handleQueryBuilder = (query, type, tree) => { + console.log("query builder " + type, query); + this.setState({ queryBuilderOutput: query, queryBuilderType: type, currentQbTree: tree, currentQbType: type }, () => { + if (type === "Sales") { + this.setLayer('checkedKeysSales'); + } else if (type === "Customer") { + this.setLayer('checkedKeysCustomer'); + } else if (type === "Office") { + this.setLayer('checkedKeysOffice'); + } + }) + } + + handleQbReset = (type) => { + this.setState({ + queryBuilderOutput: '', + queryBuilderType: '', + currentQbTree: '', + currentQbType: '' + }, () => { + if (type === "Sales") { + this.setLayer('checkedKeysSales'); + } else if (type === "Customer") { + this.setLayer('checkedKeysCustomer'); + } else if (type === "Office") { + this.setLayer('checkedKeysOffice'); + } + }) + } + + + getChosenProyekRealisasi = () => { + const { chosenProyek, projectTree, checkedKeysProjectTree } = this.state; + + this.chosenProyekTemp = []; + let chosenProyekId = []; + + if (checkedKeysProjectTree.length > 0) { + chosenProyekId = this.getChosenProyekId(); // ini yang sudah diambil uniquenya + } + else { + chosenProyekId = [] + } + + console.log('chosenProyekId', chosenProyekId); + this.setState({ chosenProyekIds: chosenProyekId }); + } + + // ambil id atau proyek_id berdasarkan key yang tercentang di projectTree + // dengan cara mencocokkan di levelnya + // kalau dia gak punya parent_id, ambil "id" + // kalau dia punya parent_id, ambil "proyek_id" + // khusus untuk key = "project-0" berarti dia tercentang semua, gak usah cek dalemannya lagi, langsung ambil children -> id nya. + // masukin ke array chosenProyekTemp + // ambil unique nya aja lalu return uniquenya + getChosenProyekId = () => { + const { checkedKeysProjectTree, projectTree } = this.state; + // console.log('checkedKeysProjectTree', checkedKeysProjectTree); + + if (checkedKeysProjectTree.length > 0) { + if (checkedKeysProjectTree.includes('project-0')) { + // langsung ambil semua children di level pertama + for (let i = 0; i < projectTree[0].children.length; i++) { + this.chosenProyekTemp.push(projectTree[0].children[i].id); + } + } + else { + // for (let i=0; i < projectTree[0].children.length; i++) { + this.getChosenProyekIdByKey(projectTree[0].children); + // } + } + } + + // usage example: + // var myArray = ['a', 1, 'a', 2, '1']; + // var unique = myArray.filter((v, i, a) => a.indexOf(v) === i); + let unique = this.chosenProyekTemp.filter((v, i, a) => a.indexOf(v) === i); + return unique; + } + + getChosenProyekIdByKey = (dataTree) => { + const { checkedKeysProjectTree } = this.state; + + for (let i = 0; i < dataTree.length; i++) { + if (checkedKeysProjectTree.includes(dataTree[i].key)) { + // console.log('matched!!!', dataTree[i].key); + // get the proyek_id, then stop + if (dataTree[i].parent_id === undefined) { + this.chosenProyekTemp.push(dataTree[i].id) // ambil idnya, karena dia adalah level paling atas (proyek) + } + else if (dataTree[i].parent_id !== undefined && dataTree[i].parent_id === null) { // dia adalah subproyek pertama + this.chosenProyekTemp.push(dataTree[i].proyek_id); // ambil proyek_id + } + else if (dataTree[i].parent_id !== undefined && dataTree[i].parent_id !== null) { // dia adalah subproyek kedua, dst + this.chosenProyekTemp.push(dataTree[i].proyek_id); // ambil proyek_id + } + } + else { + // console.log('not matched, keep looping!', dataTree[i]); + // keep looping until get the matched key + if (dataTree[i].subproyeks) { + this.getChosenProyekIdByKey(dataTree[i].children); + } + } + } + } + + toggleStatusRight = () => { + this.setState({ statusRight: !this.state.statusRight }); + } + + toggleProggresBottom = () => { + this.setState({ proggressBottom: !this.state.proggressBottom }) + } + + closeStatusRight = () => { + this.setState({ statusRight: false }); + } + + closeProggressBottom = () => { + this.setState({ proggressBottom: false }) + } + + toggleKurvaSWindowMode = () => { + const { kurvaSWindowMode } = this.state; + console.log('toggleKurvaSWindowMode', kurvaSWindowMode); + if (kurvaSWindowMode === 'default') { + this.setState({kurvaSWindowMode: 'maximize'}, () => this.renderCarouselKurvaS()) + } + else { + this.setState({kurvaSWindowMode: 'default'}, () => this.renderCarouselKurvaS()); + } + } + + renderDurasiKerja = (jamMasuk, jamKeluar) => { + + if (jamMasuk && jamKeluar) { + + let start = moment(jamMasuk), + end = moment(jamKeluar); + + let diff = end.diff(start); + let result = moment.utc(diff).format('HH:mm:ss'); + + if (result) { + return result; + } else { + return "-" + } + + } else { + return "-" + } + } + + handleRequestDataTable = async (url, payload) => { + + const config = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + localStorage.getItem('token') + }, + body: JSON.stringify(payload) // body data type must match "Content-Type" header + } + try { + const result = await fetch(url, config).then(response => response.json()).then(res => res); + return result.data || [] + } + catch (e) { + toast.error('Gagal mengambil data'); + return [] + } + } + + + handleClickChartPengawas = async param => { + const { dataStatusWaspang } = this.state + const { start_time, end_time } = dataStatusWaspang + + // if (!param.length) return; + + console.log('handleClickChartPengawas param', param); + + const { _index } = param[0]; + const label = dataStatusWaspang.labels[_index] + // console.log({ param, label }) + console.log('label', label); + + const payload = { + "columns": [ + { "name": label == " Hadir" ? "clock_time" : "created_at", "logic_operator": "range", "value": `${moment(start_time).utc().format('YYYY-MM-DD')} 00:00:00`, "value1": `${moment(end_time).utc().format('YYYY-MM-DD')} 23:59:59`, "operator": "AND" } + ], + "joins": [ + { + "name": "m_users", + "column_join": "user_id", + "column_results": [ + "name", + ] + } + ], + "paging": { "start": 0, "length": -1 } + } + console.log(payload) + if (label == "Hadir") { + const result = await this.handleRequestDataTable(PRESENSI_SEARCH, payload) + console.log('data hadir pengawas', result) + this.setState({ dataWaspangHadir: result, openTablePengawas: true, tabelTypeWaspang: label, columnWaspang: columnPresensi }); + } + + if (label == "Izin") { + const result = await this.handleRequestDataTable(ABSENSI_SEARCH, payload) + console.log('data absen pengawas', result) + this.setState({ dataWaspangAbsent: result, openTablePengawas: true, tabelTypeWaspang: label, columnWaspang: columnAbssensi }); + } + + if (label == "Panic Button") { + const result = await this.handleRequestDataTable(PANIC_BUTTON_SEARCH, payload) + console.log('data panic pengawas', result) + + let resultSortedDesc = sortBy(result, { + prop: "created_at", + desc: true, + }); + + const resultMod = resultSortedDesc.filter((value, index, self) => { + return self.findIndex(v => v.user_id === value.user_id) === index; + }).map(ele => ele); + + console.log('resultMod', resultMod); + + this.setState({ dataWaspangPanic: resultMod, openTablePengawas: true, tabelTypeWaspang: label, columnWaspang: columnPanic }); + } + } + + handleClickChartProyek = async param => { + const { dataStatusProyek, chosenProyekIds } = this.state + const { _index } = param[0]; + const label = dataStatusProyek.labels[_index] + console.log({ param, label }) + let str = "" + chosenProyekIds.map((res, idx) => { + if (idx == 0) str += `${res}` + if (idx != 0) str += `,${res}` + + }) + const val = label == "Aman" ? "green" : label == "Alert" ? "orange" : "red" + + const payload = { + "columns": [ + { "name": "color_progress", "logic_operator": "like", "value": val, "operator": "AND" }, + { "name": "id", "logic_operator": "in", "value": str ? str : "0", "operator": "AND" }, + ], + "joins": [ + { "name": "subproyeks.m_proyek", "column_join": "proyek_id", "column_results": ["nama", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual"] }, + { "name": "subproyeks.m_subproyek", "column_join": "parent_id", "column_results": ["nama", "color_progress", "jumlah_pekerja", "pic", "mulai_proyek", "akhir_proyek", "biaya_actual", "persentase_progress_plan", "persentase_progress_actual"] } + ], + "orders": { "columns": ["id"], "ascending": true }, + "paging": { "start": 0, "length": 25 } + } + console.log(payload) + const result = await this.handleRequestDataTable(PROYEK_SEARCH, payload) + console.log('data status proyek', result) + this.setState({ dataTableStatusProyek: result, openTableStatusProyek: true, typeTableStatusProyek: label }); + + } + + handleClickChartPersentase = async param => { + const { dataPersentaseProyek, chosenProyekIds } = this.state + const { _index } = param[0]; + const label = dataPersentaseProyek.labels[_index] + // console.log({ param, label }) + } + + handleClickChartCost = async param => { + const { dataCostProyek, chosenProyekIds } = this.state + const { _index } = param[0]; + const label = dataCostProyek.labels[_index] + } + + + renderTablePresensi = () => { + console.log('render hadir table', this.state.dataWaspangHadir) + const dataTable2 = this.state.dataWaspangHadir || []; + return ( +
+ {dataTable2.length !== 0 ? dataTable2.map((n) => { + return ( + + + + + + + + ) + }) : + + + } + + ) + } + + renderTablePanic = () => { + const dataTable2 = this.state.dataWaspangPanic || []; + return ( + + {dataTable2.length !== 0 ? dataTable2.map((n) => { + return ( + + + + + {/* */} + + ) + }) : + + + } + + ) + } + + renderTableAbsent = () => { + const dataTable2 = this.state.dataWaspangAbsent || []; + // const dataTable2 = []; + return ( + + {dataTable2.length !== 0 ? dataTable2.map((n) => { + return ( + + + + + + ) + }) : + + + } + + ) + } + + renderFormatRupiah = (text, prefix) => { + if (text) { + return formatRupiah(text, prefix) + } else { + return "-" + } + } + + renderTableStatusProyek = () => { + // console.log('render hadir table', this.state.dataTableStatusProyek) + const dataTable2 = this.state.dataTableStatusProyek || []; + return ( + + {dataTable2.length !== 0 ? dataTable2.map((n) => { + return ( + + + + + + + + + ) + }) : + + + } + + ) + } + + renderEmptyData = () => { + return ( +
+ Data tidak ditemukan.
Pilih proyek untuk menampilkan data. +
+ ) + } + + next = () => { + const { animating, activeIndex, dataStatusProyek, dataStatusProyekAdw } = this.state; + if (animating) return; + if ((APP_MODE === 'ADW')) { + const nextIndex = activeIndex === dataStatusProyekAdw.length - 1 ? 0 : activeIndex + 1; + this.setState({activeIndex: nextIndex}); + } + else { + const nextIndex = activeIndex === dataStatusProyek.length - 1 ? 0 : activeIndex + 1; + this.setState({activeIndex: nextIndex}); + } + } + + next2 = () => { + const { animating2, activeIndex2, dataPersentaseProyek } = this.state; + if (animating2) return; + const nextIndex = activeIndex2 === dataPersentaseProyek.length - 1 ? 0 : activeIndex2 + 1; + this.setState({activeIndex2: nextIndex}); + } + + next3 = () => { + const { animating3, activeIndex3, dataCostProyek } = this.state; + if (animating3) return; + const nextIndex = activeIndex3 === dataCostProyek.length - 1 ? 0 : activeIndex3 + 1; + this.setState({activeIndex3: nextIndex}); + } + + previous = () => { + const { animating, activeIndex, dataStatusProyek, dataStatusProyekAdw } = this.state; + if (animating) return; + if ((APP_MODE === 'ADW')) { + const nextIndex = activeIndex === 0 ? dataStatusProyekAdw.length - 1 : activeIndex - 1; + this.setState({activeIndex: nextIndex}); + } + else { + const nextIndex = activeIndex === 0 ? dataStatusProyek.length - 1 : activeIndex - 1; + this.setState({activeIndex: nextIndex}); + } + } + + previous2 = () => { + const { animating2, activeIndex2, dataPersentaseProyek } = this.state; + if (animating2) return; + const nextIndex = activeIndex2 === 0 ? dataPersentaseProyek.length - 1 : activeIndex2 - 1; + this.setState({activeIndex2: nextIndex}); + } + + previous3 = () => { + const { animating3, activeIndex3, dataCostProyek } = this.state; + if (animating3) return; + const nextIndex = activeIndex3 === 0 ? dataCostProyek.length - 1 : activeIndex3 - 1; + this.setState({activeIndex3: nextIndex}); + } + + goToIndex = (newIndex) => { + const { animating } = this.state; + if (animating) return; + this.setState({activeIndex: newIndex}) + } + + goToIndex2 = (newIndex) => { + const { animating2 } = this.state; + if (animating2) return; + this.setState({activeIndex2: newIndex}) + } + + goToIndex3 = (newIndex) => { + const { animating3 } = this.state; + if (animating3) return; + this.setState({activeIndex3: newIndex}) + } + + renderRemainingDays = (item) => { + const {day_left, percentage} = item; + if (day_left > 0 && day_left < 8) { + return {`${day_left} ${day_left > 1 ? 'days' : 'day'} left`} + } + else if (day_left === 0) { + return Today + } + else if (day_left < 0) { + return Overdue {day_left} {day_left < -1 ? 'days' : 'day'} + } + else { + return {`${day_left} days left`} + } + } + + renderCarouselStatusProyek = () => { + const { dataStatusProyek } = this.state; + if (dataStatusProyek) { + // return + if (dataStatusProyek.length > 0) { + return dataStatusProyek.map((item, index) => { + return ( + + {/* */} + +
{item.proyek_name}
+ +
{ item.count_activity_done } / { item.count_activity } Task
+ {this.renderRemainingDays(item)} + + {item.percentage}% +
Contract: {item.total_kontrak} {item.total_kontrak > 1 ? "days" : "day"}
+ + + ) + }) + } + else { + return
No Data Found
+ } + } + else { + return this.renderEmptyData() + } + } + + renderCarouselStatusProyekAdw = () => { + const { dataStatusProyekAdw } = this.state; + if (dataStatusProyekAdw) { + if (dataStatusProyekAdw.length > 0) { + return ( + + + {this.renderTableStatusProyekAdw()} + + + + ) + } + else { + return this.renderEmptyData() + } + } + else { + return this.renderEmptyData() + } + } + + renderTableStatusProyekAdw = () => { + // console.log('render hadir table', this.state.dataTableStatusProyek) + // const dataTable2 = this.state.dataTableStatusProyek || []; + // return ( + // + // {dataTable2.length !== 0 ? dataTable2.map((n) => { + // return ( + // + // + // + // + // + // + // + // + // ) + // }) : + // + // + // } + // + // ) + const { dataStatusProyekAdw } = this.state; + + if (dataStatusProyekAdw && dataStatusProyekAdw.length > 0) { + return dataStatusProyekAdw.map((item, idx) => { + // let chartData = item.data; + return ( + this.setState({animating: true}) } + onExited={() => this.setState({animating: false})} + > +
+
+
{item.proyek_name}
+
+
+ {/* */} +
{n.join.m_users_name}{n.clock_time !== null ? moment(n.clock_time).format("DD-MM-YYYY") : "-"}{n.clock_time !== null ? moment(n.clock_time).format("HH:mm:ss") : "-"}{n.clock_out_time !== null ? moment(n.clock_out_time).format("HH:mm:ss") : "-"}{n.clock_time ? this.renderDurasiKerja(n.clock_time, n.clock_out_time) : "-"}
No Data Available
{n.created_date !== null ? moment(n.created_at).format("DD-MM-YYYY HH:MM:SS") : "-"}{n.join.m_users_name !== null ? n.join.m_users_name : "-"}{n.status_response !== null ? n.status_response : "-"}{ n.clock_out_time!==null ? moment(n.clock_out_time).format("HH:mm:ss") : "-" }
No Data Available
{n.join.m_users_name}{n.description ? n.description : "-"}{n.created_at ? moment(n.created_at).format("YYYY-MM-DD") : "-"}
No Data Available
{n.nama}{this.renderFormatRupiah(n.biaya, "Rp")}{n.jumlah_pekerja}{n.pic}{n.mulai_proyek !== null ? moment(n.mulai_proyek).format("DD-MM-YYYY") : "-"}{n.akhir_proyek !== null ? moment(n.akhir_proyek).format("DD-MM-YYYY") : "-"}
No Data Available
{n.nama}{this.renderFormatRupiah(n.biaya, "Rp")}{n.jumlah_pekerja}{n.pic}{n.mulai_proyek !== null ? moment(n.mulai_proyek).format("DD-MM-YYYY") : "-"}{n.akhir_proyek !== null ? moment(n.akhir_proyek).format("DD-MM-YYYY") : "-"}
No Data Available
+ + {/* + + */} + + + + + + + + + + + + + + + + + + + + + + + + + + + {this.renderCostDeviation(item.cost_deviation)} + + + + {this.renderPotentialBudget(item.potential)} + + +
Budget Control This Period
Current Budget{item.current_budget === null ? '-' : this.renderFormatRupiah(item.current_budget.toString(), "Rp")}
Actual to Date{item.acwp === null ? '-' : this.renderFormatRupiah(item.acwp.toString(), "Rp")}
BCWP (cost vs perform){item.bcwp === null ? '-' : this.renderFormatRupiah(item.bcwp.toString(), "Rp")}
Rem. to Complete{item.rem_to_complete === null ? '-' : this.renderFormatRupiah(item.rem_to_complete.toString(), "Rp")}
Add Cost to Complete{item.add_cost_to_complete === null ? '-' : this.renderFormatRupiah(item.add_cost_to_complete.toString(), "Rp")}
Estimated at Completion{item.estimated_at_completion === null ? '-' : this.renderFormatRupiah(item.estimated_at_completion.toString(), "Rp")}
Cost Deviation
POTENTIAL
+
+
+ + ) + }) + } + else { + this.renderEmptyData() + } + } + + renderCostDeviation = (cost_deviation) => { + if (cost_deviation < 0) { + return {cost_deviation === null ? '-' : this.renderFormatRupiah(cost_deviation.toString(), "Rp")} + } + else if (cost_deviation === 0) { + return {cost_deviation === null ? '-' : this.renderFormatRupiah(cost_deviation.toString(), "Rp")} + } + else if (cost_deviation > 0) { + return {cost_deviation === null ? '-' : this.renderFormatRupiah(cost_deviation.toString(), "Rp")} + } + else { + return {cost_deviation === null ? '-' : this.renderFormatRupiah(cost_deviation.toString(), "Rp")} + } + } + + renderPotentialBudget = (pot) => { + let potential = pot.toLowerCase(); + if (potential === 'overrun') { + return {potential ? potential.toUpperCase() : '-'} + } + else if (potential === 'saving') { + return {potential ? potential.toUpperCase() : '-'} + } + else if (potential === 'on budget') { + return {potential ? potential.toUpperCase() : '-'} + } + else { + return {potential ? potential.toUpperCase() : '-'} + } + } + + renderChartStatusProyek = (items) => { + console.log('renderChartStatusProyek', items); + return items.map((item, idx) => { + let chartData = item.data; + return ( + this.setState({animating: true}) } + onExited={() => this.setState({animating: false})} + > +
+
+
Status Proyek
+
+ {/* Luas coverage: */} + {/* { numberWithCommas(item.total_luas_opt) } km2 */} +
+
+
+ +
+
+
+ ) + }) + } + + renderCarouselPersentaseProgress = () => { + const { dataPersentaseProyek } = this.state; + if (dataPersentaseProyek) { + if (dataPersentaseProyek.length > 0) { + let dataChart = []; + dataPersentaseProyek.map((item, idx) => { + let proyekName = item.proyek_name ? item.proyek_name : '-'; + let dataRes = item.data; + let labelPercentagePlaning = dataRes ? dataRes.map(res => res.kode_sortname) : []; // ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] + // let valuePercentagePlaning = dataRes ? dataRes.map(res => parseFloat(res.bobot_planning)) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + let valuePercentagePlaning = dataRes ? dataRes.map(res => parseFloat(res.cal_bobot_p)) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + // let valuePercentageRealisasi = dataRes ? dataRes.map(res => { + // let total = parseFloat(res.bobot_planning) * parseFloat(res.persentase_progress) / 100; + // if (Number.isNaN(total)) total = 0 + // return total; + // }) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + let valuePercentageRealisasi = dataRes ? dataRes.map(res => parseFloat(res.cal_bobot_a)) : []; + + let chartData = { + proyek_name: proyekName, + labels: labelPercentagePlaning, + datasets: [ + { + label: 'Perencanaan', + data: valuePercentagePlaning, + fill: false, + backgroundColor: 'rgba(255, 99, 132, 0.5)', + borderColor: 'rgba(255, 99, 132, 0.5)', + yAxisID: 'y-axis-1', + // stack: 'Stack-0' + }, + // { + // label: 'Aktual', + // data: valuePercentageRealisasi, + // fill: false, + // backgroundColor: 'rgba(54, 162, 235, 0.5)', + // borderColor: 'rgba(54, 162, 235, 0.5)', + // yAxisID: 'y-axis-1', + // // stack: 'Stack-1' + // }, + ], + }; + + dataChart.push(chartData); + }); + + console.log('dataChart persentase', dataChart); + + return ( + + + {this.renderChartPersentaseProgress(dataChart)} + + + + ) + } + else { + return this.renderEmptyData() + } + } + else { + return this.renderEmptyData() + } + } + + renderChartPersentaseProgress = (items) => { + if (items.length > 0) { + return items.map((item, idx) => { + // let chartData = item.data; + // console.log('chart persentaseeeeee', item); + return ( + this.setState({animating2: true}) } + onExited={() => this.setState({animating2: false})} + > +
+
+
{item.proyek_name}
+
+ {/* Luas coverage: */} + {/* { numberWithCommas(item.total_luas_opt) } km2 */} +
+
+
+ { + item.labels.length > 0 ? + console.log('click persentase linechart')} + /> + : +
No data found
+ } +
+
+
+ ) + }) + } + else { + return this.renderEmptyData(); + } + } + + renderCarouselCostProyek = () => { + const { dataCostProyek } = this.state; + if (dataCostProyek) { + if (dataCostProyek.length > 0) { + let dataChart = []; + + dataCostProyek.map((item, idx) => { + let proyekName = item.proyek_name ? item.proyek_name : '-'; + let dataRes = item.data; + let labelCostPlaning = dataRes ? dataRes.map(res => res.kode_sortname) : []; // ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] + // let valueCostPlaning = dataRes ? dataRes.map(res => res.rencana_biaya) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + let valueCostPlaning = dataRes ? dataRes.map(res => parseFloat(res.cal_budget_cost)) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + // let valueCostRealisasi = dataRes ? dataRes.map(res => res.biaya_actual) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + let valueCostRealisasi = dataRes ? dataRes.map(res => parseFloat(res.cal_actual_cost)) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + + let chartData = { + proyek_name: proyekName, + labels: labelCostPlaning, + datasets: [ + { + label: 'Perencanaan', + data: valueCostPlaning, + fill: false, + backgroundColor: 'rgba(255, 99, 132, 0.5)', + borderColor: 'rgba(255, 99, 132, 0.5)', + yAxisID: 'y-axis-1', + // stack: 'Stack-0' + }, + { + label: 'Aktual', + data: valueCostRealisasi, + fill: false, + backgroundColor: 'rgba(54, 162, 235, 0.5)', + borderColor: 'rgba(54, 162, 235, 0.5)', + yAxisID: 'y-axis-1', + // stack: 'Stack-1' + }, + ], + }; + + dataChart.push(chartData); + }); + + // console.log('dataChart', dataChart); + + return ( + + + {this.renderChartCostProyek(dataChart)} + + + + ) + } + else { + return this.renderEmptyData() + } + } + else { + return this.renderEmptyData() + } + } + + renderChartCostProyek = (items) => { + if (items.length > 0) { + return items.map((item, idx) => { + return ( + this.setState({animating3: true})} + onExited={() => this.setState({animating3: false})} + > +
+
+
{item.proyek_name}
+ {/*
+ Luas coverage: + { numberWithCommas(item.total_luas_opt) } km2 +
*/} +
+
+ { + item.labels.length > 0 ? + console.log('click persentase linechart')} + /> + : +
No data found
+ } + {/*
Test
*/} +
+
+
+ ) + }) + } + else { + return this.renderEmptyData(); + } + } + + renderCarouselKurvaS = () => { + const { dataCurvaS } = this.state; + if (dataCurvaS) { + if (dataCurvaS.length > 0) { + let dataChart = []; + + dataCurvaS.map((item, idx) => { + let proyekName = item.proyek_name ? item.proyek_name : '-'; + let dataRes = item.data; + // let labelCostPlanning = dataRes ? dataRes.map(res => res.kode_sortname) : []; // ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] + // let labels = dataRes ? dataRes.date ? moment(dataRes.date).format('DD-MM-YY') : '-' : []; + let labels = dataRes && dataRes.date ? dataRes.date.map((item, idx) => { + let date = item[0]; + let bcwp = item[1]; + let acwp = item[2]; + date = date ? moment(date).format('DD MMM YY') : '-' + bcwp = bcwp ? this.renderFormatRupiah(bcwp.toString(), 'Rp') : '-'; + acwp = acwp ? this.renderFormatRupiah(acwp.toString(), 'Rp') : '-'; + return [date,bcwp,acwp] + }) : []; + // let valueCostPlaning = dataRes ? dataRes.map(res => res.rencana_biaya) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + // let valueCostPlanning = dataRes ? dataRes.map(res => parseFloat(res.cal_budget_cost)) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + let valuePlanning = dataRes ? dataRes.percentage.map(res => res && res[0] !== null ? res[0] : null) : []; // validate if null (holiday) + // let valueCostRealisasi = dataRes ? dataRes.map(res => res.biaya_actual) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + // let valueCostRealisasi = dataRes ? dataRes.map(res => parseFloat(res.cal_actual_cost)) : []; // [10, 23, 39, 47, 55, 68, 70, 82, 90, 100] + let valueRealisasi = dataRes ? dataRes.percentage.map(res => res && res[1] !== null ? res[1] : null) : []; // validate if null (holiday) + + // console.log('labelCostPlanning', labelCostPlanning) + let chartData = { + proyek_name: proyekName, + // labels: [['P1',1500,1000],['P2',2000,1500],['P3',2500,1800],['P4',3000,2000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000],['P5',3500,3000]], + labels: labels, + datasets: [ + { + label: 'Perencanaan', + data: valuePlanning, + fill: false, + backgroundColor: 'rgba(255, 99, 132, 0.5)', + borderColor: 'rgba(255, 99, 132, 0.5)', + yAxisID: 'y-axis-1', + // stack: 'Stack-0' + }, + { + label: 'Aktual', + data: valueRealisasi, + fill: false, + backgroundColor: 'rgba(54, 162, 235, 0.5)', + borderColor: 'rgba(54, 162, 235, 0.5)', + yAxisID: 'y-axis-1', + // stack: 'Stack-1' + }, + ], + }; + + dataChart.push(chartData); + }); + + // console.log('dataChart', dataChart); + + return ( + + + {this.renderChartKurvaS(dataChart)} + + + + ) + } + else { + return this.renderEmptyData() + } + } + else { + return this.renderEmptyData() + } + } + + renderChartKurvaS = (items) => { + if (items.length > 0) { + return items.map((item, idx) => { + return ( + this.setState({animating3: true})} + onExited={() => this.setState({animating3: false})} + > +
+
+
{item.proyek_name}
+ {/*
+ Luas coverage: + { numberWithCommas(item.total_luas_opt) } km2 +
*/} +
+ { + this.state.kurvaSWindowMode === 'default' ? +
+ { + item.labels.length > 0 ? + console.log('click persentase linechart')} + redraw={true} + /> + : +
No data found
+ } +
+ : +
+ { + item.labels.length > 0 ? + console.log('click persentase linechart')} + redraw={true} + /> + : +
No data found
+ } +
+ } + {/*
+ { + item.labels.length > 0 ? + console.log('click persentase linechart')} + /> + : +
No data found
+ } +
*/} +
+
+ ) + }) + } + else { + return this.renderEmptyData(); + } + } + + + + render() { + const { alert, successAlert, dangerAlert, messageAlert, + dataStatusProyek, dataPersentaseProyek, dataCostProyek, dataStatusWaspang } = this.state; + + const headerHeight = document.getElementsByClassName('app-header').clientHeight; + const progressBottomHeight = document.getElementsByClassName(' progress-bottom').clientHeight; + const heightRightStatus = `calc(100vh - ${headerHeight + 10} - ${progressBottomHeight}` + return ( +
+ {/* this.saveMap()} + printMap={() => this.printMap()} + onLogout={(e) => this.signOut(e)} + /> */} + this.setState({ openTablePengawas: false })} + > + this.setState({ openTablePengawas: false })}> + Data Waspang {this.state.tabelTypeWaspang} + + + + + + {/* */} + {this.state.columnWaspang.map((i, index) => { + return ( + + ) + })} + + + {this.state.tabelTypeWaspang == "Hadir" && this.renderTablePresensi()} + {this.state.tabelTypeWaspang == "Izin" && this.renderTableAbsent()} + {this.state.tabelTypeWaspang == "Panic Button" && this.renderTablePanic()} +
Actions{i.name}
+
+ + + +
+ + this.setState({ openTableStatusProyek: false })} + > + this.setState({ openTableStatusProyek: false })}> + Data Status Proyek {this.state.typeTableStatusProyek} + + + + + + {/* */} + {columnStatusProyek.map((i, index) => { + return ( + + ) + })} + + + {this.renderTableStatusProyek()} +
Actions{i.name}
+
+ {/* + + */} +
+ + + + + + +
+ + this.setState({ alert: false, successAlert: false, dangerAlert: false })}> + {messageAlert} + + + {this.state.isProcessing && +
+ +
+ } + +
{this.state.popupRightVisible ? + this.setPopupDataTemp(feature)} + layerName={this.state.activeListFeatureId ? this.state.activeListFeatureId.substr(0, this.state.activeListFeatureId.indexOf('.')) : ''} + toggleEditGeometry={(selectedPopupData) => this.toggleEditGeometry(selectedPopupData)} + activeStateAddGeometry={this.state.activeStateAddGeometry} + fid={this.state.activeListFeatureId} + closePopupRight={this.closePopupRight} + getLayerAttribute={(layerName) => this.getLayerAttribute(layerName)} + layer_attribute={this.state.layer_attribute} + searchLabelData={this.state.searchLabelData} + layerInfo={this.state.layerInfo} + toggleRoutingBarVisible={() => this.setState({ routingBarVisible: !this.state.routingBarVisible })} + setRouteType={(routeType) => this.setState({ routeType: routeType })} + /> : null + }
+
+ + + + this.openPopupRight()} + closePopupRight={() => this.closePopupRight()} + popupDataTemp={this.state.popupDataTemp} + removeChosenLayer={() => this.removeChosenLayer()} + setPopupDataTemp={(feature) => this.setPopupDataTemp(feature)} + activeListFeatureId={this.state.activeListFeatureId} + editGeometryVisible={this.state.editGeometryVisible} + toggleActiveStateAddGeometry={() => this.toggleActiveStateAddGeometry()} + searchLabelData={this.state.searchLabelData} + layerInfo={this.state.layerInfo} + onCheckOpt={(state, checkedKeys) => this.onCheckOpt(state, checkedKeys)} + checkedKeysSales={this.state.checkedKeysSales} + checkedKeysCustomer={this.state.checkedKeysCustomer} + checkedKeysOffice={this.state.checkedKeysOffice} + checkedKeysDemografi={this.state.checkedKeysDemografi} + checkedKeysAnalisa={this.state.checkedKeysAnalisa} + checkedKeysEmployeeDivision={this.state.checkedKeysEmployeeDivision} + checkedKeysProjectTree={this.state.checkedKeysProjectTree} + setCheckedKeysProjectTree={(data) => this.setState({checkedKeysProjectTree: data})} + salesGroupTree={this.state.salesGroupTree} + employeeDivisionTree={this.state.employeeDivisionTree} + setSalesGroupTree={(data) => this.setState({ salesGroupTree: data })} + setEmployeeDivisionTree={(data) => this.setState({ employeeDivisionTree: data })} + showRoute={(route) => this.showRoute(route)} + removeLayerByName={(layerName) => this.removeLayerByName(layerName)} + setIsProcessing={(data) => this.setState({ isProcessing: data })} + handleQueryBuilder={this.handleQueryBuilder} + currentQbTree={this.state.currentQbTree} + currentQbType={this.state.currentQbType} + handleQbReset={this.handleQbReset} + projectTree={this.state.projectTree} + setProjectTree={(data) => this.setState({ projectTree: data })} + proggressBottom={this.state.proggressBottom} + toggleStatusRight={this.toggleStatusRight} + toggleProggresBottom={this.toggleProggresBottom} + /> + + + { + this.state.editGeometryVisible && + this.cancelDraw()} + layerName={this.state.layerNameDraw} + geomType={this.state.geomTypeDraw} + olmap={this.olmap} + /> + } + + {this.state.routingBarVisible && + this.setState({ routingBarVisible: !this.state.routingBarVisible })} + popupDataTemp={this.state.popupDataTemp} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + searchRouting={(userId, dateString) => this.searchRouting(userId, dateString)} + isSearchingRoute={this.state.isSearchingRoute} + setRouteType={(routeType) => this.setState({ routeType: routeType })} + /> + } + + + + +
+ +
+ {this.state.proggressBottom ? + + + {/* */} + + + Kurva S +
+ this.toggleKurvaSWindowMode()} style={{ marginRight: '15px', cursor: 'pointer'}}> { this.state.kurvaSWindowMode === 'default' ? : } + this.closeProggressBottom()} style={{ marginRight: '5px', cursor: 'pointer' }}>x +
+
+ + { this.renderCarouselKurvaS() } + +
+ +
+ : null} + + + {this.state.statusRight && +
+ + + {(APP_MODE === 'ADW') ? 'Budget Control This Period' : 'Status Proyek' } x + + + { + (APP_MODE === 'ADW') ? + this.renderCarouselStatusProyekAdw() + : + this.renderCarouselStatusProyek() + } + + +
+ } +
+
+
+ ); + } +} + +export default SiopasMap; \ No newline at end of file diff --git a/src/views/Map/Map_backup.js b/src/views/Map/Map_backup.js new file mode 100644 index 0000000..8b09655 --- /dev/null +++ b/src/views/Map/Map_backup.js @@ -0,0 +1,965 @@ +/* +maplayer: +{ + name: "Polygon Tanah Kantor Instansi Pemerintah", + source: { + "url": "http://192.168.99.110/geoserver/wms", + "params": { + "LAYERS": "bmd_denpasar:tanah_kantor_instansi_pemerintah", + "TILED": true, + "SLD": "http://192.168.99.110/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS=bmd_denpasar:tanah_kantor_instansi_pemerintah" + }, + "serverType": "geoserver", + "transition": 0, + "crossOrigin": "anonymous" + }, + type: 'layer', + geom_type: 'Polygon' +}, + +{ + name: "Polygon Sekolah", + source: { + "url": "http://192.168.99.110/geoserver/wms", + "params": { + "LAYERS": "bmd_denpasar:tanah_sekolah", + "TILED": true, + "SLD": "http://192.168.99.110/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS=bmd_denpasar:tanah_sekolah" + }, + "serverType": "geoserver", + "transition": 0, + "crossOrigin": "anonymous" + }, + type: 'layer', + geom_type: 'Polygon' +} + +baselayer +---------- +{ + name: 'OSM', + source: new OlSourceOsm(), + type: 'base', + imageName: 'osm.PNG' +} + +{ + name: "ESRI", + source: { + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", + "projection": "EPSG:3857", + "maxZoom": 18 + }, + type: 'base', + imageName: 'esri.PNG' +}); + + +*/ + +import React, { Component, Suspense, Fragment } from 'react'; +import ReactDOM from 'react-dom'; +// import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; +import { Button, UncontrolledTooltip } from 'reactstrap'; + +import './Map.css'; +import './Popup.css'; +import './CustomScroll.css'; +import 'ol/ol.css'; +import 'antd/dist/antd.css'; +import './react-geo.css'; + +import OlMap from 'ol/Map'; +import OlView from 'ol/View'; +import OlLayerTile from 'ol/layer/Tile'; +import OlSourceOsm from 'ol/source/OSM'; +import OlSourceTileJson from 'ol/source/TileJSON'; +import OlLayerGroup from 'ol/layer/Group'; +import OlSourceTileWMS from 'ol/source/TileWMS'; +// import OlLayerSwitcher from 'ol/control/LayerSwitcher'; +import {fromLonLat, transformExtent} from 'ol/proj'; +import {Vector as VectorSource, XYZ as XYZSource, Cluster} from 'ol/source'; +import Overlay from 'ol/Overlay'; +import { Draw, Select } from 'ol/interaction'; + +import { Drawer } from 'antd'; +import { + SimpleButton, + MapComponent, + NominatimSearch, + MeasureButton, + // LayerTree, + MapProvider, + mappify, + onDropAware, + // AddWmsPanel +} from '@terrestris/react-geo'; + +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import LayerSwitcher from '@terrestris/react-geo/dist/LayerSwitcher/LayerSwitcher'; +import axios from 'axios'; +// import { PopupContainer } from '../../components/PopupContainer'; +import PopupContainer from '../../components/PopupContainer'; +import { + AppHeader +} from '@coreui/react'; +// import { Badge, UncontrolledDropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, +// InputGroup, Input, InputGroupAddon, InputGroupText, Button +// } from 'reactstrap'; + +// const DefaultHeader = React.lazy(() => import('../../containers/DefaultLayout/DefaultHeader')); +// import MapHeader from './MapHeader'; +import MapHeader from '../../components/MapHeader'; +import MapToolbar from '../../components/MapToolbar'; +// import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import { appConfig, setRequestMapHeader, layerStyleUrl } from '../../const/MapConst.js'; +import { Icon } from '@iconify/react'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import {Col, Row} from 'reactstrap'; + +// import { +// custom, //name spaces +// //group +// //objects +// } from "react-openlayers"; + + +const MappifiedNominatimSearch = mappify(NominatimSearch); +const MappifiedMeasureButton = mappify(MeasureButton); +// const MappifiedLayerTree = mappify(LayerTree); +const Map = mappify(onDropAware(MapComponent)); + +// const center = [ 788453.4890155146, 6573085.729161344 ]; +const projection = 'EPSG:3857'; //default +const projection4326 = 'EPSG:4326'; +// const projection = 'EPSG:4326'; // lat long +// Indonesia +const lat = -2.6000285; +const lon = 118.015776; +const zoom = 5; +const Indonesia = new fromLonLat([lon, lat], projection); +const Bali_bbox = [115.178638994694, -8.71934970794214, 115.269238650799, -8.59763413248024]; + +const osmLayer = new OlLayerTile({ + source: new OlSourceOsm(), + name: 'OSM', + type: 'base', + imageName: 'osm.PNG' +}); + +const esriLayer = new OlLayerTile({ + name: "ESRI", + source: new XYZSource({ + url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + projection: projection, + maxZoom: 18 + }), + type: 'base', + imageName: 'esri.PNG' +}); + +const denpasarLayer = new OlLayerTile({ + name: "Denpasar Map", + source: new XYZSource({ + url: 'http://siopas.co.id/bmd-denpasar/public/maps/mbtiles-mapservices.php?db=bali.mbtiles&z={z}&x={x}&y={y}', + projection: projection4326, + maxZoom: 18 + }), + type: 'base', + imageName: 'bali.PNG' +}); + +const citraDenpasarLayer = new OlLayerTile({ + name: "Citra Denpasar", + source: new XYZSource({ + url: 'http://siopas.co.id/bmd-denpasar/public/maps/mbtiles-mapservices.php?db=citra-denpasar-x.mbtiles&z={z}&x={x}&y={y}', + projection: projection4326, + maxZoom: 18 + }), + type: 'base', + imageName: 'bali_citra.PNG' +}); + +const googleLayer = new OlLayerTile({ + name: "Google", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 18 + }), + type: 'base', + imageName: 'google.PNG' +}) + +const googleStreetLayer = new OlLayerTile({ + name: "Google Street", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 18 + }), + type: 'base', + imageName: 'google_street.PNG' +}) +const baseLayers = [ + osmLayer, + esriLayer, + googleLayer, + googleStreetLayer, + denpasarLayer, + citraDenpasarLayer +] + +const baseLayerGroup = new OlLayerGroup({ + name: 'Base Layers', + layers: [ + osmLayer, + esriLayer + ], + type: 'baseGroup' +}); + +const layerGroupSekolah = new OlLayerGroup({ + name: 'Sekolah', + layers: [ + + new OlLayerTile({ + name: "Polygon Sekolah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sekolah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sekolah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Sekolah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_sekolah0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_sekolah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + ], + type: 'layerGroup' +}); + +const layerGroupRS = new OlLayerGroup({ + name: 'Rumah Sakit', + layers: [ + new OlLayerTile({ + name: "Polygon Rumah Sakit", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_rumah_sakit_puskesmas', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_rumah_sakit_puskesmas' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Rumah Sakit", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_rumah_sakit0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_rumah_sakit0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + ], + type: 'layerGroup' +}); + +const layerGroupKantorDesa = new OlLayerGroup({ + name: 'Kantor Desa dan Lurah', + layers: [ + new OlLayerTile({ + name: "Polygon Kantor Desa dan Lurah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_kantor_desa_dan_lurah0', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_kantor_desa_dan_lurah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Kantor Desa dan Lurah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_kantor_desa_lurah0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_kantor_desa_lurah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + + ] +}); + +const layerGroupSarPras = new OlLayerGroup({ + name: 'Sarana Prasarana', + layers: [ + new OlLayerTile({ + name: "Polygon Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sarana_prasarana', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sarana_prasarana' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }) + /*new OlLayerTile({ + name: "Point Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: {'LAYERS': appConfig.workspace_name+':', 'TILED': true}, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + }),*/ + ] +}); + +const layerGroupPasar = new OlLayerGroup({ + name: 'Sarana Prasarana', + layers: [ + new OlLayerTile({ + name: "Polygon Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_pasar_pertokoan', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_pasar_pertokoan' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_pasar_pertokoan0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_pasar_pertokoan0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }), + ], + type: 'layerGroup' +}); + +const tanah_kantor_instansi_pemerintah = new OlLayerTile({ + name: "Polygon Tanah Kantor Instansi Pemerintah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_kantor_instansi_pemerintah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_kantor_instansi_pemerintah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' +}); + + +/*const map = new OlMap({ + view: new OlView({ + center: Indonesia, + zoom: zoom, + }), + layers: [osmLayer, layerGroupSekolah, layerGroupRS, layerGroupKantorDesa, layerGroupSarPras] +});*/ + + +/*const setRequestMapHeader = () => { + axios.interceptors.request.use(function (config) { + // Do something before request is sent + // config.headers["Authorization"] = `Bearer ${token}`; + config.headers["Authorization"] = "Basic " + btoa(appConfig.geoserver_username + ":" + appConfig.geoserver_password) + // config.headers["crossDomain"] = true; + return config; + }, function (error) { + // Do something with request error + return Promise.reject(error); + }); +}*/ + +// map.on('postcompose', map.updateSize); + +/*const overlay = new Overlay({ + element: document.getElementById('popup'), + autoPan: true, + autoPanAnimation: { + duration: 250 + } +});*/ + +// const popupClick = () => { +// alert('hellawwww'); +// console.log('hellaawww'); +// } + +// const popupC = ( +// +// ) + +/*class PopupC extends Component { + constructor(props) { + super(props); + // this.popupClick = this.popupClick.bind(this); + } + + popupClick = () => { + alert('hellawwww'); + console.log('hellaawww'); + } + + render() { + return ( + + ) + } +}*/ + + + +class SiopasMap extends Component { + constructor(props) { + super(props); + this.state = { + drawerLayerVisible: false, + drawerSearchVisible: false, + wmsLayers: [], + totalLayerHasFeature: 0, + countGetFeature: 0, + countNotGetFeature: 0, + popupDataTemp: [], + evtCoordinate: null, + visibleLS: false, + visibleLSProp: 'hide', + // mapOnClickKey: null + }; + + this.popupRef = React.createRef(); + + this.layers = [ + osmLayer, + // layerGroupSekolah, + // layerGroupRS, + // layerGroupKantorDesa, + // layerGroupSarPras, + tanah_kantor_instansi_pemerintah + ]; + + // this.overlay = new Overlay({ + // element: document.getElementById('popup'), + // // element: ReactDOM.findDOMNode().querySelector('#popup'), + // autoPan: true, + // autoPanAnimation: { + // duration: 250 + // } + // }); + + /*this.overlay = new Overlay({ + element: , + autoPan: true, + autoPanAnimation: { + duration: 250 + } + });*/ + + this.overlay = new Overlay({ + // element: ReactDOM.findDOMNode(this).querySelector('#popup'), + element: document.getElementById('popup'), + autoPan: true, + autoPanAnimation: { + duration: 250 + } + }); + + this.olmap = new OlMap({ + view: new OlView({ + center: Indonesia, + zoom: zoom, + }), + layers: this.layers, + overlays: [this.overlay] + }); + + this.changeBaseLayer = this.changeBaseLayer.bind(this); + // this.popupRef = React.createRef(); + // console.log('this.popupRef', this.popupRef); + } + + componentDidMount = () => { + this.getHomeView(); + setRequestMapHeader(); + + // this.overlay = new Overlay({ + // element: document.getElementById('popup'), + // // element: ReactDOM.findDOMNode().querySelector('#popup'), + // autoPan: true, + // autoPanAnimation: { + // duration: 250 + // } + // }); + + // // Popup showing the position the user clicked + // this.overlay = new Overlay({ + // element: ReactDOM.findDOMNode(PopupContainer).querySelector('#popup') + // }); + + this.olmap.on("singleclick", (evt) => { + this.mapOnClick(evt); + }); + + // this.setState({mapOnClickKey: mapSingleClick}); + } + + componentDidUpdate = (prevProps, prevState) => { + if (this.state.popupDataTemp.length > 0) { + this.showPopup(); + console.log('popupDataTemp > 0'); + } + else { + this.closePopup(); + console.log('popupDataTemp < 1'); + } + } + + // shouldComponentUpdate(nextProps, nextState) { + // let center = this.olmap.getView().getCenter(); + // let zoom = this.olmap.getView().getZoom(); + // if (center === nextState.center && zoom === nextState.zoom) return false; + // return true; + // } + + loading = () =>
Loading...
+ + changeBaseLayer(item) { + console.log('change baselayer', item); + console.log(this.olmap.getLayers()); + if (this.olmap.getLayers().values_.length == 0) { + // check if layers empty, so just insert base layer to position 0 + this.olmap.getLayers().insertAt(0, item); + } + // check if layer exist + else if (this.olmap.getLayers().values_.length > 0) { + // check the position 0, if base layer then replace to new + if (this.olmap.getLayers().array_[0].get('type') === 'base') { + this.olmap.getLayers().removeAt(0); + this.olmap.getLayers().insertAt(0, item); + } + // else just insert base layer at position 0 + else { + this.olmap.getLayers().insertAt(0, item); + } + } + } + + getHomeView = () => { + this.olmap.getView().fit(new transformExtent(Bali_bbox, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + } + + mapOnClick = (evt) => { + // console.log('map on click', evt); + evt.preventDefault(); + let isDrawing = false; + let removedFeature = []; + let isRemoving = false; + + let mapInteractions = []; + this.olmap.getInteractions().forEach((interaction) => { + // console.log('interaction', interaction); + if (interaction instanceof Draw) { + // console.log('drawing is active!!'); + isDrawing = true; + } + // if (interaction instanceof Select) { + // console.log('select interaction is active'); + // isRemoving = true; + // } + }); + + if (isDrawing) { + return; + } + + // if (isRemoving) { + // this.olmap.getLayers().forEach((layer, i) => { + // if (layer.get('type') === 'vector') { + // let features = layer.getSource().getFeatures(); + // features.forEach((feature) => { + // layer.getSource().removeFeature(feature); + // }); + // } + // }); + // } + + let viewResolution = this.olmap.getView().getResolution(); + let viewProjection = this.olmap.getView().getProjection(); + let url = ''; + let promises = []; + let featureGet = []; + + this.olmap.getLayers().forEach((layer, i) => { + if (layer.get('type') !== 'base') { + if (layer.get('type') !== 'vector') { + if (layer.get('type') == 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + url = sublayer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + if (url) { + promises.push(axios.get(url)); + } + } + }); + } + else { + if (layer.getVisible()) { + url = layer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + if (url) { + promises.push(axios.get(url)); + } + } + } + } + } + }); + + axios.all(promises).then((results) => { + results.forEach((response) => { + // console.log('response promises', response); + if (response.data !== undefined) { + if (response.data.features.length > 0) { + for(let i=0; i < response.data.features.length; i++) { + featureGet.push(response.data.features[i]); + } + } + } + }) + + // console.log('featureGet', featureGet); + this.setState({ + popupDataTemp: featureGet, + evtCoordinate: evt.coordinate + }); + }); + } + + initPopup = () => { + let popupCloser = document.getElementById("popup-closer"); + // let showImageFeature = document.getElementById("showImageFeature"); + popupCloser.addEventListener("click", () => this.closePopup()); + // showImageFeature.addEventListener("click", () => this.closePopup()); + } + + showPopup = () => { + console.log('----------------- showPopup'); + this.initPopup(); + this.fillPopupContent(); + this.overlay.setPosition(this.state.evtCoordinate); + } + + fillPopupContent = () => { + const { popupDataTemp } = this.state; + console.log('popupDataTemp', popupDataTemp); + + if (popupDataTemp.length === 1) { + this.popupOneFeature(popupDataTemp[0]); + } + else if (popupDataTemp.length > 1) { + this.popupMoreFeature(); + } + } + + // renderPopup = () => { + // ReactDOM.render(, document.getElementById('popup-content')); + // } + + oneFeatureContent = (selectedPopupData) => { + // const { popupDataTemp } = this.state; + // console.log(popupDataTemp); + const popupId = selectedPopupData.id; + const popupProperties = selectedPopupData.properties; + console.log('selectedPopupData', selectedPopupData); + + return ( +
+
+

{ popupId }

+
+
+ + + { this.renderOneRowFeature(popupProperties) } + +
+
+
+
+ { this.renderButtonFooter(selectedPopupData) } +
+
+
+ ) + + // document.getElementById('showImageFeature').addEventListener("click", () => this.showImageFeature(selectedPopupData)); + } + + renderOneRowFeature = (popupProperties) => { + const row = []; + for (let key in popupProperties) { + row.push({key} : {popupProperties[key]}) + } + return row; + } + + moreFeatureContent = () => { + let self = this; + const { popupDataTemp } = self.state; + return ( +
+
+

Choose one feature

+
+
+ + + {/* self.renderSelectedPopup()}>*/} + { popupDataTemp.map((item, index) => { + return ( this.renderSelectedPopup(item)}>) + }) } + +
Testtttt
{item.id}
+
+
+
+ +
+
+
+ ) + } + + /*moreFeatureContent() { + return ( +
+
+

Choose one feature

+
+
+ +
+
+
+ +
+
+
+ ); + } */ + + /*renderListFeature = () => { + const { popupDataTemp } = this.state; + console.log('renderListFeature popupDataTemp', popupDataTemp); + return ( + + { popupDataTemp.map((item, index) => { + return ( this.renderSelectedPopup(item)}>{item.id}) + }) } + + ) + }*/ + + initFooterButtonsFunc = () => { + let showImageFeature = document.getElementById("showImageFeature"); + showImageFeature.addEventListener("click", () => this.closePopup()); + } + + popupOneFeature = (selectedPopupData) => { + console.log('-----------popupOneFeature'); + // const { popupDataTemp } = this.state; + let popupContent = document.getElementById('popup-content'); + ReactDOM.render(this.oneFeatureContent(selectedPopupData), popupContent); + // this.initFooterButtonsFunc(); + } + + popupMoreFeature = () => { + // generate list of feature that clicked in map, user choose one, then go to popupOneFeature + let popupContent = document.getElementById('popup-content'); + + // ReactDOM.render(, popupContent); + ReactDOM.render(this.moreFeatureContent(), popupContent); + // popupContent.innerHTML = "There are more feature"; + } + + popupOnClick = () => { + alert('click PopupContainer'); + } + + /*renderSelectedPopup = (selectedPopupData) => { + console.log('rendereSelectedPopup selectedPopupData', selectedPopupData); + }*/ + + renderSelectedPopup = (item) => { + console.log('renderSelectedPopup selectedPopupData', item); + } + + renderButtonFooter = (selectedPopupData) => { + // popupCloser.addEventListener("click", () => this.closePopup()); + return( + + + + Show Images + + + + Edit Feature + + + + Delete Feature + + + ) + + // let showImageFeature = document.getElementById("showImageFeature"); + // showImageFeature.addEventListener("click", () => this.closePopup()); + } + + showImageFeature = (selectedPopupData) => { + console.log('show images', selectedPopupData); + } + + editFeature = (selectedPopupData) => { + console.log('edit feature', selectedPopupData); + } + + deleteFeature = (selectedPopupData) => { + console.log('delete feature', selectedPopupData); + } + + closePopup = () => { + console.log('close popup'); + this.overlay.setPosition(undefined); + } + + render() { + return ( +
+ + + + + + {/**/} + {/**/} +
+ ); + } +} + +export default SiopasMap; + diff --git a/src/views/Map/Popup.css b/src/views/Map/Popup.css new file mode 100644 index 0000000..3e49bc7 --- /dev/null +++ b/src/views/Map/Popup.css @@ -0,0 +1,86 @@ +.ol-popup { + position: absolute; + background-color: white; + -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + padding: 15px; + border-radius: 10px; + border: 1px solid #cccccc; + bottom: 12px; + left: -50px; + min-width: 280px; +} +.ol-popup:after, .ol-popup:before { + top: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} +.ol-popup:after { + border-top-color: white; + border-width: 10px; + left: 48px; + margin-left: -10px; +} +.ol-popup:before { + border-top-color: #cccccc; + border-width: 11px; + left: 48px; + margin-left: -11px; +} +.ol-popup-closer { + text-decoration: none; + position: absolute; + top: 2px; + right: 8px; +} +.ol-popup-closer:after { + content: "✖"; +} +/*.popup-table { + height: 200px; + overflow-y: scroll; +}*/ +#popup-content { + max-height: 250px; + /*overflow: auto;*/ +} +/*.div_popoup_table { + height: 200px; + overflow: auto; +}*/ +.popup-body { + height: 200px; + overflow: auto; +} +/*.popup-table { + height: 200px; + overflow: auto; +}*/ +.popup-body>.popup-table>thead>tr>th, .popup-body>.popup-table>tbody>tr>th, .popup-body>.popup-table>tfoot>tr>th, .popup-body>.popup-table>thead>tr>td, .popup-body>.popup-table>tbody>tr>td, .popup-body>.popup-table>tfoot>tr>td, .popup-body>.popup-table>tr>td { + padding: 3px !important; + font-size: 11px; + text-align: left; + /*overflow-y: scroll;*/ +} +.popup-footer { + text-align: center !important; + /*padding: 0px;*/ + padding-top: 10px; + /*float: right;*/ + /*margin-right: 10px;*/ + /*z-index: 90;*/ + /*float: inherit !important;*/ + /*margin-bottom: 0px;*/ +} + +.popup-row-list-feature { + cursor: pointer; +} + +.image-slider { + height: 175px; +} \ No newline at end of file diff --git a/src/views/Map/package.json b/src/views/Map/package.json new file mode 100644 index 0000000..24eba08 --- /dev/null +++ b/src/views/Map/package.json @@ -0,0 +1,6 @@ +{ + "name": "Map", + "version": "0.0.0", + "private": true, + "main": "./Map.js" +} diff --git a/src/views/Map/react-geo.css b/src/views/Map/react-geo.css new file mode 100644 index 0000000..30f8097 --- /dev/null +++ b/src/views/Map/react-geo.css @@ -0,0 +1,360 @@ +/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */ +/* stylelint-disable no-duplicate-selectors */ +/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors */ +/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors */ +.react-geo-uploadbutton { + cursor: pointer; + display: inline-block; + position: relative; + overflow: hidden; +} +.react-geo-uploadbutton input[type="file"] { + cursor: pointer; + width: 100%; + position: absolute; + top: 0; + right: 0; + margin: 0; + padding: 0; + opacity: 0; +} +.react-geo-uploadbutton ::-webkit-file-upload-button { + cursor: pointer; +} +button.react-geo-simplebutton { + color: #fff; + background-color: #1890ff; + border-color: #1890ff; +} +button.react-geo-simplebutton:focus { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-simplebutton:hover { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-simplebutton:disabled { + background-color: #fff; + border-color: #e6e6e6; +} +button.react-geo-measurebutton { + color: #fff; + background-color: #1890ff; + border-color: #e6e6e6; +} +button.react-geo-measurebutton:focus { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-measurebutton:hover { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-measurebutton:disabled { + background-color: #fff; + border-color: #e6e6e6; +} +button.react-geo-measurebutton.btn-pressed { + background-color: #005cb1; + border-color: #e6e6e6; +} +.react-geo-measure-tooltip { + position: relative; + background: rgba(0, 0, 0, 0.5); + border-radius: 4px; + color: white; + padding: 4px 8px; + opacity: 0.7; + white-space: nowrap; +} +.react-geo-measure-tooltip-dynamic { + opacity: 1; + font-weight: bold; +} +.react-geo-measure-tooltip-static { + background-color: #ffcc33; + color: black; + border: 1px solid white; +} +.react-geo-measure-tooltip-dynamic:before, +.react-geo-measure-tooltip-static:before { + border-top: 6px solid rgba(0, 0, 0, 0.5); + border-right: 6px solid transparent; + border-left: 6px solid transparent; + content: ""; + position: absolute; + bottom: -6px; + margin-left: -7px; + left: 50%; +} +.react-geo-measure-tooltip-static:before { + border-top-color: #ffcc33; +} +button.react-geo-digitizebutton { + color: #fff; + background-color: #1890ff; + border-color: #1890ff; +} +button.react-geo-digitizebutton:focus { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-digitizebutton:hover { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-digitizebutton:disabled { + background-color: #fff; + border-color: #e6e6e6; +} +.vertical-toggle-group { + display: flex; + flex-direction: column; +} +.horizontal-toggle-group { + display: flex; + flex-direction: row; +} +button.react-geo-togglebutton { + color: #fff; + background-color: #1890ff; + border-color: #e6e6e6; +} +button.react-geo-togglebutton:focus { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-togglebutton:hover { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-togglebutton:disabled { + background-color: #fff; + border-color: #e6e6e6; +} +button.react-geo-togglebutton.btn-pressed { + background-color: #005cb1; + border-color: #e6e6e6; +} +.react-geo-circlemenuitem { + display: block; + position: absolute; + top: 50%; + left: 50%; + margin: -1.3em; + pointer-events: all; +} +.react-geo-circlemenu { + border: dashed 1px; + border-radius: 50%; + position: absolute; + pointer-events: none; + background: -moz-radial-gradient(center, ellipse cover, rgba(0, 0, 0, 0) 0%, rgba(200, 199, 58, 0.65) 100%); + background: -webkit-radial-gradient(center, ellipse cover, rgba(0, 0, 0, 0) 0%, rgba(200, 199, 58, 0.65) 100%); + background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 0%, rgba(200, 199, 58, 0.65) 100%); +} +.add-wms-layer-checkbox-line { + padding: 5px; + margin: 1px; + display: flex; + background: #ffffff; + flex: 1; + align-items: center; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} +.add-wms-layer-checkbox-line .add-wms-add-info-icon { + margin-left: 5px; +} +.add-wms-panel .body { + display: flex; + flex-direction: column; +} +.add-wms-panel .body .ant-checkbox-group { + flex: 1; + height: 100%; + overflow-y: auto; +} +.add-wms-panel .body .add-wms-layer-checkbox-line { + padding: 5px; + margin: 1px; + display: flex; + background: #ffffff; + flex: 1; + align-items: center; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} +.add-wms-panel .body .add-wms-layer-checkbox-line .add-wms-add-info-icon { + margin-left: 5px; +} +.add-wms-panel .body .react-geo-titlebar .react-geo-simplebutton { + margin-right: 5px; +} +.react-geo-feature-grid .react-geo-feature-grid-row.row-hover > td { + background: #e6f7ff; +} +.react-geo-propertygrid { + background-color: white; + display: flex; + flex-direction: column; + height: 100%; +} +.react-geo-propertygrid .ant-spin-nested-loading { + height: 100%; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container { + height: 100%; + display: flex; + flex-direction: column; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table { + flex: 1; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-fixed-header .ant-table-header { + margin-bottom: inherit !important; + padding-bottom: inherit !important; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content { + height: 100%; + display: flex; + flex-direction: column; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content .ant-table-header { + overflow: inherit; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content .ant-table-body { + overflow: auto; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content th { + text-align: center; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content tr:nth-child(odd) { + background-color: rgba(24, 144, 255, 0.05); +} +.react-geo-propertygrid .ant-pagination { + width: 100%; + text-align: center; +} +.react-geo-layertree-node { + transition: opacity 1s; +} +.react-geo-layertree-node.out-off-range { + opacity: 0.5; +} +.react-geo-floatingmaplogo { + position: absolute; + left: 5px; + bottom: 5px; + background-color: rgba(255, 255, 255, 0.5); + border-radius: 5px; + z-index: 10; +} +.react-geo-titlebar { + display: flex; + align-items: center; + overflow: auto; + background-color: #e6e6e6; + color: #1890ff; + font-weight: bold; +} +.react-geo-titlebar .title { + margin: 5px; + flex: 1; +} +.react-geo-titlebar .controls { + margin: 5px; + display: flex; +} +.react-geo-titlebar .controls > button { + margin-right: 2px; +} +.react-geo-panel { + z-index: 100; +} +.react-geo-panel .body { + background-color: #fff; +} +.react-geo-panel .resize-handle:hover.resize-handle-right { + border-right: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-left { + border-left: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-top { + border-top: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-bottom { + border-top: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-bottom-right { + border-bottom: 5px solid rgba(24, 144, 255, 0.5); + border-right: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-bottom-left { + border-bottom: 5px solid rgba(24, 144, 255, 0.5); + border-left: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-top-right { + border-top: 5px solid rgba(24, 144, 255, 0.5); + border-right: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-top-left { + border-top: 5px solid rgba(24, 144, 255, 0.5); + border-left: 5px solid rgba(24, 144, 255, 0.5); +} +.vertical-toolbar { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} +.vertical-toolbar button { + margin-top: 5px; +} +.horizontal-toolbar { + line-height: 30px; + pointer-events: all; + transition: all .3s; +} +.horizontal-toolbar button { + margin-right: 5px; + top: 5px; +} +.react-geo-userchip { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; +} +.react-geo-userchip .username { + margin-left: 5px; +} +.react-geo-window-portal { + z-index: 1000; + position: inherit !important; + -moz-box-shadow: 2px 2px 10px -2px #cccccc, -2px 2px 10px -2px #cccccc; + -webkit-box-shadow: 2px 2px 10px -2px #cccccc, -2px 2px 10px -2px #cccccc; + box-shadow: 2px 2px 10px -2px #cccccc, -2px 2px 10px -2px #cccccc; +} +.react-geo-window-portal .react-geo-simplebutton { + background-color: #fff; + color: #1890ff; +} +.react-geo-window-portal .react-geo-simplebutton:hover { + background-color: #4ba9ff; + color: #ffffff; +} +.react-geo-window-portal .ant-modal-header { + padding: 1px; + border-bottom: 2px dotted rgba(24, 144, 255, 0.25); + background-color: #ffffff; +} +.react-geo-window-portal .ant-modal-header .title { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} diff --git a/src/views/MapConfig/MapConfig.css b/src/views/MapConfig/MapConfig.css new file mode 100644 index 0000000..e69de29 diff --git a/src/views/MapConfig/MapConfig.js b/src/views/MapConfig/MapConfig.js new file mode 100644 index 0000000..88cf0a8 --- /dev/null +++ b/src/views/MapConfig/MapConfig.js @@ -0,0 +1,67 @@ +import React, { Component } from 'react' +// import './MapConfig.css' +import DataTable from '../../components/DataTable' + + +/*const columns = [{ + dataField: 'id', + text: 'Product ID', + sort: true +}, { + dataField: 'name', + text: 'Product Name', + sort: true +}, { + dataField: 'price', + text: 'Product Price', + sort: true +}];*/ + +const data = [ +{ + "id": 0, + "name": "Item name 0", + "price": 2100 +}, +{ + "id": 1, + "name": "Item name 1", + "price": 2101 +}, +{ + "id": 2, + "name": "Item name 2", + "price": 2102 +}, +{ + "id": 3, + "name": "Item name 3", + "price": 2103 +}, +{ + "id": 4, + "name": "Item name 4", + "price": 2104 +}, +{ + "id": 5, + "name": "Item name 5", + "price": 2105 +}]; + + + +class MapConfig extends Component { + render() { + return ( +
+ +
+ ) + } +} + +export default MapConfig; \ No newline at end of file diff --git a/src/views/MapConfig/package.json b/src/views/MapConfig/package.json new file mode 100644 index 0000000..f20011c --- /dev/null +++ b/src/views/MapConfig/package.json @@ -0,0 +1,6 @@ +{ + "name": "MapConfig", + "version": "0.0.0", + "private": true, + "main": "./MapConfig.js" +} diff --git a/src/views/Map_backup/CustomScroll.css b/src/views/Map_backup/CustomScroll.css new file mode 100644 index 0000000..c26a064 --- /dev/null +++ b/src/views/Map_backup/CustomScroll.css @@ -0,0 +1,19 @@ +.custom-scroll::-webkit-scrollbar-track { + /*-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);*/ + border-radius: 10px; + background-color: #F5F5F5; +} + +.custom-scroll::-webkit-scrollbar { + width: 10px; + height: 10px; + background-color: #F5F5F5; +} + +.custom-scroll::-webkit-scrollbar-thumb { + border-radius: 5px; + /*-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);*/ + -webkit-box-shadow: 0 0 3px rgba(0,0,0,0.3); + /*background-color: #ccc;*/ + background-color: #eaeaea; +} \ No newline at end of file diff --git a/src/views/Map_backup/Map.css b/src/views/Map_backup/Map.css new file mode 100644 index 0000000..6e825e7 --- /dev/null +++ b/src/views/Map_backup/Map.css @@ -0,0 +1,119 @@ +.App { + text-align: center; +} + +.map { + position: absolute; + top: 50px; + bottom: 0; + right: 0; + left: 0; +} + +.App-logo { + animation: App-logo-spin infinite 20s linear; + height: 40vmin; + pointer-events: none; +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +/*Override openlayers css*/ +.ol-zoom { + /*left: inherit !important;*/ + /*right: 10px !important;*/ + font-size: 20px; + left: 10px !important; + top: 10px !important; + display: none !important; +} + +.ol-zoom-in { + margin-bottom: 2px !important; +} + +.ol-control { + background-color: inherit !important; +} + +.ol-control button { + background-color: #1890ff !important; + /*min-width: 32px !important;*/ + padding-right: 0 !important; + padding-left: 0 !important; + text-align: center !important; + border-radius: 50% !important; +} + +.ol-control button:hover, .ol-control button:focus { + color: #fff !important; + background-color: #40a9ff !important; + border-color: #ffffff !important; + outline: 0; +} + +.ol-mouse-position { + top: inherit !important; + right: inherit !important; + left: 13px; + position: fixed !important; + bottom: 5px !important; +} + +.ol-scale-line { + bottom: 25px !important; + left: 13px !important; +} + +.ol-popup { + /*display: none;*/ +} + +#popup-closer { + cursor: pointer; +} + +.print-img { + visibility: hidden; +} + +@media print +{ + .no-print, .no-print * + { + display: none !important; + } +} + +.loader-container { + text-align: center; + align-content: center; + top: 100px; + position: fixed; + z-index: 999999; + align-self: center; + margin-left: 45%; + +} \ No newline at end of file diff --git a/src/views/Map_backup/Map.js b/src/views/Map_backup/Map.js new file mode 100644 index 0000000..e5005a8 --- /dev/null +++ b/src/views/Map_backup/Map.js @@ -0,0 +1,2866 @@ +import React, { Component, Suspense, Fragment } from 'react'; +import ReactDOM from 'react-dom'; +import { Container, Col, Row, Button, UncontrolledTooltip } from 'reactstrap'; + +import './Map.css'; +import './Popup.css'; +import './CustomScroll.css'; +import 'ol/ol.css'; +import 'antd/dist/antd.css'; +import './react-geo.css'; + +import OlMap from 'ol/Map'; +import OlView from 'ol/View'; +import OlLayerTile from 'ol/layer/Tile'; +import ImageLayer from 'ol/layer/Image'; +import OlSourceOsm from 'ol/source/OSM'; +import OlSourceTileJson from 'ol/source/TileJSON'; +import OlLayerGroup from 'ol/layer/Group'; +import OlSourceTileWMS from 'ol/source/TileWMS'; +import OlSourceImageWMS from 'ol/source/ImageWMS'; +// import OlLayerSwitcher from 'ol/control/LayerSwitcher'; +import {fromLonLat, transformExtent, transform} from 'ol/proj'; +import {Vector as VectorSource, XYZ as XYZSource, Cluster} from 'ol/source'; +import Overlay from 'ol/Overlay'; +import { Draw, Select, Modify } from 'ol/interaction'; +import Feature from 'ol/Feature'; +import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from 'ol/geom'; +import {Vector as VectorLayer} from 'ol/layer'; +import {Circle as CircleStyle, Fill, Stroke, Style, Text, Icon as IconOl} from 'ol/style'; +import {defaults as defaultControls, MousePosition, ScaleLine} from 'ol/control'; +import {createStringXY, toStringXY} from 'ol/coordinate'; +import GeoJSON from 'ol/format/GeoJSON'; +import WMSCapabilities from 'ol/format/WMSCapabilities'; + +import { Drawer } from 'antd'; +import { + SimpleButton, + MapComponent, + NominatimSearch, + MeasureButton, + // LayerTree, + MapProvider, + mappify, + onDropAware, + // AddWmsPanel +} from '@terrestris/react-geo'; + +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import LayerSwitcher from '@terrestris/react-geo/dist/LayerSwitcher/LayerSwitcher'; +import axios from 'axios'; +import PopupContainer from '../../components/PopupContainer'; +import { + AppHeader +} from '@coreui/react'; +import MapHeader from '../../components/MapHeader'; +import MapToolbar from '../../components/MapToolbar'; +import DrawingTool from '../../components/DrawingTool'; +import ImagePopup from '../../components/ImagePopup'; +import ImageSlider from '../../components/ImageSlider'; +import RoutingBar from '../../components/RoutingBar'; +// import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import { appConfig, setRequestMapHeader, layerStyleUrl, BMD_DENPASAR_MAPSERVICE_URL, IU_MAPSERVICE_URL, MAP_ID, emptyConstants, + WMS_CAPABILITIES_URL_2 } from '../../const/MapConst.js'; +import { Icon } from '@iconify/react'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import country_indonesia from '../../assets/json/indonesia.json'; +import { test, getGeomType, updateMap, getLayerAttribute, getLayerColor, getRandomColor } from '../../const/GeoserverFunc.js'; +import { API_UPDATE_MAP, API_LOAD_MAP, API_LAYER_SEARCH_LABEL, API_GET_CHART_KATEGORI, + API_GEOHR_OFFICE_MONITORING, API_GEOHR_SALES_MONITORING, API_GEOHR_WAYPOINT_SALES_MONITORING, API_GEOHR_CUSTOMER_MONITORING } from '../../const/ApiConst.js'; +import { getSalesRoutingApi, getSalesFeatures, getOfficeFeatures, getCustomerFeatures, getEmployeeFeatures, getEmployeeRoutingApi, getWaspangRoutingApi } from '../../const/GeohrApiFunc.js'; +import { SALES_FEATURES_STYLE, CUSTOMER_FEATURES_STYLE, OFFICE_FEATURES_STYLE, ROUTE_MAP_STYLES, EMPLOYEE_FEATURES_STYLE, PROJECT_FEATURES_STYLE, WASPANG_FEATURES_STYLE } from '../../const/GeohrMapStyles.js'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import moment from "moment"; +import salesGeojson from '../../dummy_data/sales.geojson'; +import routeDummy from '../../dummy_data/route2.json'; +import { demografiTree, analisaTree } from '../../const/LayerTreeConst.js' +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import pinRouteStart from '../../assets/img/map/pin_route_green.png'; +import pinRouteEnd from '../../assets/img/map/pin_route_red.png'; +import domtoimage from 'dom-to-image'; +import Loader from 'react-loader-spinner' +import "react-loader-spinner/dist/loader/css/react-spinner-loader.css" +import * as alasql from 'alasql'; +import * as lodash from 'lodash'; + +const MappifiedNominatimSearch = mappify(NominatimSearch); +const MappifiedMeasureButton = mappify(MeasureButton); +// const MappifiedLayerTree = mappify(LayerTree); +const Map = mappify(onDropAware(MapComponent)); + +// const center = [ 788453.4890155146, 6573085.729161344 ]; +const projection = 'EPSG:3857'; //default +const projection4326 = 'EPSG:4326'; +// const projection = 'EPSG:4326'; // lat long +// Indonesia +const lat = -2.6000285; +const lon = 118.015776; +const zoom = 5; +const Indonesia = new fromLonLat([lon, lat], projection); +const Bali_bbox = [115.178638994694, -8.71934970794214, 115.269238650799, -8.59763413248024]; + +const osmLayer = new OlLayerTile({ + source: new OlSourceOsm(), + name: 'OSM', + type: 'base', + imageName: 'osm.PNG' +}); + +const esriLayer = new OlLayerTile({ + name: "ESRI", + source: new XYZSource({ + url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + projection: projection, + maxZoom: 18 + }), + type: 'base', + imageName: 'esri.PNG' +}); + +const denpasarLayer = new OlLayerTile({ + name: "Denpasar Map", + source: new XYZSource({ + // url: 'http://siopas.co.id/bmd-denpasar/public/maps/mbtiles-mapservices-epsg3857.php?db=bali.mbtiles&z={z}&x={x}&y={y}&proj='+projection, + url: `${BMD_DENPASAR_MAPSERVICE_URL}?db=bali.mbtiles&z={z}&x={x}&y={y}&proj=${projection}`, + // url: 'http://127.0.0.1/osmap-php/assets/util/mbtiles-mapservices-epsg3857.php?db=bali.mbtiles&z={z}&x={x}&y={y}&proj='+projection, + // projection: projection4326, + maxZoom: 18 + }), + type: 'base', + imageName: 'bali.PNG' +}); + +const citraDenpasarLayer = new OlLayerTile({ + name: "Citra Denpasar", + source: new XYZSource({ + // url: 'http://siopas.co.id/bmd-denpasar/public/maps/mbtiles-mapservices-epsg3857.php?db=citra-denpasar-x.mbtiles&z={z}&x={x}&y={y}&proj='+projection, + url: `${BMD_DENPASAR_MAPSERVICE_URL}?db=citra-denpasar-x.mbtiles&z={z}&x={x}&y={y}&proj=${projection}`, + // url: 'http://127.0.0.1/osmap-php/assets/util/mbtiles-mapservices-epsg3857.php?db=citra-denpasar-x.mbtiles&z={z}&x={x}&y={y}&proj='+projection, + // projection: projection4326, + maxZoom: 18 + }), + type: 'base', + imageName: 'bali_citra.PNG' +}); + +const googleLayer = new OlLayerTile({ + name: "Google", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 18 + }), + type: 'base', + imageName: 'google.PNG' +}) + +const googleStreetLayer = new OlLayerTile({ + name: "Google Street", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 18 + }), + type: 'base', + imageName: 'google_street.PNG' +}) + +const iuLayer1 = new OlLayerTile({ + name: "Integrasia Utama Map", + source: new XYZSource({ + url: `${IU_MAPSERVICE_URL}?db=0-13.mbtiles&z={z}&x={x}&y={y}&proj=${projection}`, + projection: projection, + minZoom: 0, + maxZoom: 13 + }), + type: 'base', + imageName: 'iu_map.PNG' +}) + +const iuLayer2 = new OlLayerTile({ + name: "Integrasia Utama Map", + source: new XYZSource({ + url: `${IU_MAPSERVICE_URL}?db=14-18.mbtiles&z={z}&x={x}&y={y}&proj=${projection}`, + projection: projection, + minZoom: 14, + maxZoom: 18 + }), + type: 'base', + imageName: 'iu_map.PNG' +}) + +const iuLayerGroup = new OlLayerGroup({ + name: 'Integrasia Utama Map', + layers: [ + iuLayer1, + iuLayer2 + ], + type: 'base', + imageName: 'iu_map.PNG' +}); + +// let baseLayers = [ +// osmLayer, +// esriLayer, +// // googleLayer, +// // googleStreetLayer, +// denpasarLayer, +// citraDenpasarLayer, +// iuLayerGroup +// ]; + +let baseLayers = []; + +if (localStorage.getItem('u_group') == 'kominfo') { + baseLayers = [ + osmLayer, + esriLayer, + googleLayer, + googleStreetLayer, + // denpasarLayer, + // citraDenpasarLayer, + // iuLayerGroup + ]; +} +else { + baseLayers = [ + osmLayer, + esriLayer, + googleLayer, + googleStreetLayer, + // denpasarLayer, + // citraDenpasarLayer, + // iuLayerGroup + ]; +} + +const baseLayerGroup = new OlLayerGroup({ + name: 'Base Layers', + layers: [ + osmLayer, + esriLayer + ], + type: 'baseGroup' +}); + +const layerGroupSekolah = new OlLayerGroup({ + name: 'Sekolah', + layers: [ + + new OlLayerTile({ + name: "Polygon Sekolah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sekolah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sekolah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Sekolah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_sekolah0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_sekolah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + ], + type: 'layerGroup' +}); + +const layerGroupRS = new OlLayerGroup({ + name: 'Rumah Sakit', + layers: [ + new OlLayerTile({ + name: "Polygon Rumah Sakit", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_rumah_sakit_puskesmas', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_rumah_sakit_puskesmas' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Rumah Sakit", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_rumah_sakit0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_rumah_sakit0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + ], + type: 'layerGroup' +}); + +const layerGroupKantorDesa = new OlLayerGroup({ + name: 'Kantor Desa dan Lurah', + layers: [ + new OlLayerTile({ + name: "Polygon Kantor Desa dan Lurah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_kantor_desa_dan_lurah0', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_kantor_desa_dan_lurah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Kantor Desa dan Lurah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_kantor_desa_lurah0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_kantor_desa_lurah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + + ] +}); + +const layerGroupSarPras = new OlLayerGroup({ + name: 'Sarana Prasarana', + layers: [ + new OlLayerTile({ + name: "Polygon Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sarana_prasarana', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sarana_prasarana' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }) + /*new OlLayerTile({ + name: "Point Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: {'LAYERS': appConfig.workspace_name+':', 'TILED': true}, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + }),*/ + ] +}); + +const layerGroupPasar = new OlLayerGroup({ + name: 'Pasar / Pertokoan', + layers: [ + new OlLayerTile({ + name: "Polygon Pasar / Pertokoan", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_pasar_pertokoan', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_pasar_pertokoan' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Pasar / Pertokoan", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_pasar_pertokoan0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_pasar_pertokoan0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }), + ], + type: 'layerGroup' +}); + +const tanah_kantor_instansi_pemerintah = new OlLayerTile({ + name: "Polygon Tanah Kantor Instansi Pemerintah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_kantor_instansi_pemerintah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_kantor_instansi_pemerintah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' +}); + + +const polygon_rumah_sakit = new OlLayerTile({ + name: "Polygon Rumah Sakit", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + // 'LAYERS': appConfig.workspace_name+':tanah_rumah_sakit_puskesmas', + 'LAYERS': 'tanah_rumah_sakit_puskesmas', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_rumah_sakit_puskesmas' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' +}) + + + + +const point_sekolah = new OlLayerTile({ + name: "Point Sekolah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_sekolah0', + // 'LAYER': 'point_tanah_sekolah0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_sekolah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' +}) + +const paxel = new OlLayerTile({ + name: "paxel area code", + source: new OlSourceTileWMS({ + url: 'https://osmap.id/geoserver/wms', + params: { + 'LAYERS': 'geonode:paxel_area_code_20210426', + 'TILED': true, + 'SLD': 'https://osmap.id/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS=geonode:paxel_area_code_20210426' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' +}) + +const testLayer = new OlLayerTile({ + name: "demografi_jumlah_penduduk", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': 'geohr:demografi_jumlah_penduduk', + 'TILED': true, + 'SLD': 'https://osmap.id/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS=geohr:demografi_jumlah_penduduk' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + }), + type: 'layer', + geom_type: 'Polygon' +}); + +// let projectFeatures = []; + +class SiopasMap extends Component { + constructor(props) { + super(props); + + this.state = { + mapZoom: null, + mapCenter: null, + mapProjection: null, + drawerLayerVisible: false, + drawerSearchVisible: false, + wmsLayers: [], + totalLayerHasFeature: 0, + countGetFeature: 0, + countNotGetFeature: 0, + popupDataTemp: [], + activeListFeatureId: '', + evtCoordinate: null, + visibleLS: false, + visibleLSProp: 'hide', + popupRightVisible: false, + imagePopupVisible: false, + alert: false, + successAlert: false, + dangerAlert: false, + messageAlert: "", + editGeometryVisible: false, + layerNameDraw: '', + geomTypeDraw: '', + activeStateAddGeometry: false, + layer_attribute: [], + searchLabelData: [], + layerInfo: [], + // LayerTree Panel + checkedKeysSales: [], + checkedKeysCustomer: [], + checkedKeysOffice: [], + checkedKeysDemografi: [], + checkedKeysAnalisa: [], + checkedKeysEmployeeDivision: [], + checkedKeysProjectTree: [], + salesGroupTree: null, + employeeDivisionTree: null, + projectTree: null, + routingBarVisible: false, + isSearchingRoute: false, + isProcessing: false, + queryBuilderOutput:'', + queryBuilderType:'', + currentQbTree:'', + currentQbType:'', + routeType: '', + chosenProyekIds: [] + }; + + this.layers = [ + osmLayer, + // layerGroupSekolah, + // layerGroupRS, + // layerGroupKantorDesa, + // layerGroupSarPras, + // tanah_kantor_instansi_pemerintah, + // point_sekolah, + // polygon_rumah_sakit + // paxel + // testLayer + ]; + + this.overlay = new Overlay({ + element: document.getElementById('popup'), + autoPan: true, + autoPanAnimation: { + duration: 250 + } + }); + + this.scaleLineControl = new ScaleLine({ + units: 'metric', + bar: true, + steps: 4, + text: true, + // minWidth: 140 + }) + + this.mousePositionControl = new MousePosition({ + coordinateFormat: createStringXY(4), + projection: 'EPSG:4326', + // className: 'custom-mouse-position', + target: document.getElementById('custom-mouse-position'), + }); + + this.olmap = new OlMap({ + view: new OlView({ + center: Indonesia, + zoom: zoom, + projection: projection + }), + layers: this.layers, + overlays: [this.overlay], + controls: defaultControls().extend([this.scaleLineControl, this.mousePositionControl]) + }); + + this.changeBaseLayer = this.changeBaseLayer.bind(this); + this.openPopupRight = this.openPopupRight.bind(this); + this.closePopupRight = this.closePopupRight.bind(this); + + this.projectFeatures = []; + this.waspangFeatures = []; + } + + componentDidMount = () => { + // this.loadMap(); + setRequestMapHeader(); + this.getLayerSearchLabel(); + this.getLayerInfo(); + this.olmap.on("singleclick", (evt) => { + this.mapOnClick(evt); + }); + this.setState({mapProjection: this.olmap.getView().getProjection()}, () => console.log('mapProjection', this.state.mapProjection)); + } + + componentDidUpdate = (prevProps, prevState) => { + + if (!this.state.popupRightVisible && this.state.popupDataTemp.length > 0) { + // this.showPopup(); + this.openPopupRight(); + // this.setActiveListFeature(); + // console.log('popupDataTemp > 0'); + // console.log('popupDataTemp ', this.state.popupDataTemp); + } + else if (this.state.popupRightVisible && this.state.popupDataTemp.length < 1) { + // this.closePopup(); + this.closePopupRight(); + // console.log('popupDataTemp < 1'); + // console.log('popupDataTemp ', this.state.popupDataTemp); + } + + if (prevState.checkedKeysProjectTree !== this.state.checkedKeysProjectTree) { + this.setLayer('checkedKeysProjectTree'); + } + + if (prevState.routingBarVisible !== this.state.routingBarVisible) { + if (!this.state.routingBarVisible) { + this.removeLayerByName('routeLayer'); + } + } + + if (this.state.chosenProyekIds !== prevState.chosenProyekIds) this.getDataUserToProyek() + } + + loading = () =>
Loading...
+ + + getLayerSearchLabel = async() => { + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_LAYER_SEARCH_LABEL, param).then(response => response.json()).then(res => res) + if (result.data){ + if (result.data.length > 0) { + // console.log('getLayerSearchLabel result', result); + this.setState({ + searchLabelData: result.data + }, () => console.log('getLayerSearchLabel searchLabelData', this.state.searchLabelData)); + } + } else { + + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + // toast.warn(err.message.toString()); + } + } + + + getLayerInfo = async () => { + let layerInfo = []; + const param = { + method: 'GET', + header: JSON.stringify({'Content-Type': 'application/json'}), + } + + try { + const result = await fetch(API_GET_CHART_KATEGORI, param).then(response => response.json()).then(res => res) + // console.log(result) + + if(result.data){ + if (result.data.length > 0) { + for(let i=0; i < result.data.length; i++) { + let layer_name = result.data[i].layer_name; + let layer_title = result.data[i].layer_title; + let layer_geom_type = result.data[i].layer_geom_type; + let total_features = result.data[i].total_features; + + let SLD_URL = `${layerStyleUrl+layer_name}`;; + let reqColor = await getLayerColor(SLD_URL); + let color = ''; + if (reqColor.success) { + color = reqColor.result; + } + layerInfo.push({ + layer_name: layer_name, + layer_title: layer_title, + layer_geom_type: layer_geom_type, + layer_color: color, + total_features: total_features + }); + } + this.setState({layerInfo: layerInfo}, () => console.log('layerInfo',this.state.layerInfo)); + } + // this.setState({alert: true, messageAlert: result.code_message, successAlert: true, dangerAlert: false}) + } else { + // this.setState({chartKategori: pie}); + // this.setState({alert: true, messageAlert: result.code_message, successAlert: false, dangerAlert: true}) + } + } catch(err) { + console.log(err); + // alert(err.message.toString()); + // toast.warn(err.message.toString()); + // this.setState({alert: true, messageAlert: err.message.toString(), successAlert: false, dangerAlert: true}) + } + } + + setDefaultMap = () => { + this.olmap.getView().animate({ + zoom: zoom, + center: Indonesia + }); + } + + changeBaseLayer(item) { + console.log('change baselayer', item); + // console.log(this.olmap.getLayers()); + if (this.olmap.getLayers().values_.length == 0) { + // check if layers empty, so just insert base layer to position 0 + this.olmap.getLayers().insertAt(0, item); + } + // check if layer exist + else if (this.olmap.getLayers().values_.length > 0) { + // check the position 0, if base layer then replace to new + if (this.olmap.getLayers().array_[0].get('type') === 'base') { + this.olmap.getLayers().removeAt(0); + this.olmap.getLayers().insertAt(0, item); + } + // else just insert base layer at position 0 + else { + this.olmap.getLayers().insertAt(0, item); + } + } + } + + getHomeView = () => { + // this should request to siopas map api to get current map + // this.olmap.getView().fit(new transformExtent(Bali_bbox, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + const { mapZoom, mapCenter } = this.state; + if (mapZoom && mapCenter !== null) { + this.olmap.getView().animate({ + zoom: mapZoom, + center: mapCenter + }); + } + else { + // alert("Map Zoom and Map Center are not set yet"); + this.olmap.getView().animate({ + zoom: zoom, + center: Indonesia + }); + } + } + + mapOnClick = (evt) => { + // console.log('map on click', evt); + // console.log('getEventCoordinate', this.olmap.getEventCoordinate()); + // console.log('coordinate', evt.coordinate); + // console.log('pixel', evt.pixel); + evt.preventDefault(); + let isDrawing = false; + let removedFeature = []; + let isRemoving = false; + let transformedCoords4326 = transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326'); + // console.log('transformedCoords4326', transformedCoords4326); + + let mapInteractions = []; + this.olmap.getInteractions().forEach((interaction) => { + // console.log('interaction', interaction); + if (interaction instanceof Draw) { + console.log('drawing is active!!'); + isDrawing = true; + } + // if (interaction instanceof Select) { + // console.log('select interaction is active'); + // isRemoving = true; + // } + + if (interaction instanceof Modify) { + console.log('modify feature is active'); + isDrawing = true; + } + }); + + if (isDrawing) { + return; + } + + // if (isRemoving) { + // this.olmap.getLayers().forEach((layer, i) => { + // if (layer.get('type') === 'vector') { + // let features = layer.getSource().getFeatures(); + // features.forEach((feature) => { + // layer.getSource().removeFeature(feature); + // }); + // } + // }); + // } + + let viewResolution = this.olmap.getView().getResolution(); + let viewProjection = this.olmap.getView().getProjection(); + let url = ''; + let promises = []; + let featureGet = []; + + // this.olmap.getLayers().forEach((layer, i) => { + // if (layer.get('type') !== 'base') { + // if (layer.get('type') !== 'vector') { + // if (layer.get('type') == 'layerGroup') { + // layer.getLayers().forEach((sublayer, i) => { + // if (sublayer.getVisible()) { + // url = sublayer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + // if (url) { + // promises.push(axios.get(url)); + // } + // } + // }); + // } + // else { + // if (layer.getVisible()) { + // url = layer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + // if (url) { + // promises.push(axios.get(url)); + // } + // } + // } + // } + // } + // }); + + // axios.all(promises).then((results) => { + // results.forEach((response) => { + // // console.log('mapOnClick response promises', response); + // if (response.data !== undefined) { + // if (response.data.features.length > 0) { + // for(let i=0; i < response.data.features.length; i++) { + // featureGet.push(response.data.features[i]); + // } + // } + // } + // }) + + // this.setState({ + // popupDataTemp: featureGet, + // evtCoordinate: evt.coordinate + // }, () => this.setActiveListFeature()); + // }); + + let hitGeojson = null; + + this.olmap.getLayers().forEach((layer, i) => { + console.log('layer', layer.get('name')); + if (layer.get('type') !== 'base') { + if (layer.get('type') == 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + url = sublayer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + if (url) { + promises.push(axios.get(url)); + } + } + }); + } + else { + if (layer.getVisible()) { + if (layer.get('name') !== 'ChosenLayer') { + if (layer.get('source_type') && layer.get('source_type') === 'geojson') { + let layerSource = layer.getSource(); + // let geojsonVector = source.getFeatures(); + + // console.log('features', features); + // console.log('layer', layer); + // console.log('layerSource', layer.getSource()); + // console.log('layerFeatures', layer.getSource().getFeaturesAtCoordinate(toStringXY(evt.coordinates))); + // console.log('layer coordinates', layer.getSource().getClosestFeatureToCoordinate(transformedCoords4326)); + // layer.getSource().forEachFeature() + + // layerSource.getFeatures(evt.pixel).then((features) => { + // console.log('get geojson layer', features); + // }) + + + + // this.olmap.forEachFeatureAtPixel(evt.pixel, (feature, layer) => { + // console.log('forEachFeatureAtPixel', feature, layer); + + // feat.id = feature.id_; + // feat.geometry.type = feature.getGeometry().getType(); + // // feat.geometry.coordinates = transform(feature.getGeometry().getCoordinates(), 'EPSG:3857', 'EPSG:4326'); + // feat.geometry.coordinates = feature.getGeometry().getCoordinates(); + // feat.properties = feature.getProperties(); + // delete feat.properties["geometry"]; + + // console.log('feat', feat); + // featureGet.push(feat); + // }) + + + hitGeojson = this.olmap.getFeaturesAtPixel(evt.pixel); + console.log('hitGeojson', hitGeojson); + + // layerSource.getFeaturesAtCoordinate(evt.coordinate); + // console.log('layerSource.getFeatures', layerSource.getFeaturesAtCoordinate(transformedCoords4326)); + // console.log('transform', transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')); + } + else if (layer.get('source_type') === "wms") { + url = layer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + if (url) { + promises.push(axios.get(url)); + } + } + } + } + } + } + }); + + + if (hitGeojson && hitGeojson.length > 0) { + for (let i=0; i < hitGeojson.length; i++) { + let feature = hitGeojson[i]; + + let feat = { + "type": "Feature", + "id": "", + "geometry": { + "type": "", + "coordinates": [] + }, + "geometry_name": "the_geom", + "properties": {} + } + + feat.id = feature.id_; + feat.geometry.type = feature.getGeometry().getType(); + // feat.geometry.coordinates = transform(feature.getGeometry().getCoordinates(), 'EPSG:3857', 'EPSG:4326'); + feat.geometry.coordinates = feature.getGeometry().getCoordinates(); + feat.properties = feature.getProperties(); + delete feat.properties["geometry"]; + + console.log('feat', feat); + featureGet.push(feat); + } + + console.log('featureGet geojson', featureGet); + } + + + // kalo dari WMS + if (promises.length > 0) { + axios.all(promises).then((results) => { + results.forEach((response) => { + console.log('mapOnClick response promises', response); + if (response.data !== undefined) { + if (response.data.features.length > 0) { + for(let i=0; i < response.data.features.length; i++) { + /*data example: + { + "type": "FeatureCollection", + "totalFeatures": "unknown", + "features": [ + { + "type": "Feature", + "id": "paxel_area_code_20210426.5083", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + 106.522270001261, + -6.66321000044036 + ], + [ + 106.536260000699, + -6.66382999935416 + ], + [ + 106.556582195013, + -6.68497003983464 + ], + [ + 106.541310000657, + -6.71486999865072 + ], + [ + 106.542630000375, + -6.74058999942815 + ], + [ + 106.550740756582, + -6.7473471734325 + ], + [ + 106.533284161136, + -6.75148465469226 + ], + [ + 106.524773753407, + -6.76232523192311 + ], + [ + 106.498398062322, + -6.75218427058684 + ], + [ + 106.46682763259, + -6.75104031405846 + ], + [ + 106.459021692598, + -6.73381922767441 + ], + [ + 106.481760000125, + -6.72494999944212 + ], + [ + 106.504030000085, + -6.70412999979885 + ], + [ + 106.51431934866, + -6.65951255475977 + ], + [ + 106.522270001261, + -6.66321000044036 + ] + ] + ] + ] + }, + "geometry_name": "the_geom", + "properties": { + "fid": 37287, + "desa": "MALASARI", + "provinsi": "JAWA BARAT", + "kabkot": "BOGOR", + "kecamatan": "NANGGUNG", + "post_code": null, + "city_code": "", + "ph_code": "", + "locker_cod": "", + "area_code": "", + "id": 37287, + "cluster_area_code": "", + "service_ty": null + } + } + ], + "crs": { + "type": "name", + "properties": { + "name": "urn:ogc:def:crs:EPSG::4326" + } + } + } + + */ + featureGet.push(response.data.features[i]); + } + } + } + }) + + + console.log('featureGet WMS', featureGet); + + // adding it here because it's trapped on axios callback + this.setState({ + popupDataTemp: featureGet, + evtCoordinate: evt.coordinate + }, () => this.setActiveListFeature()); + }); + } + + if (hitGeojson && promises) { + this.setState({ + popupDataTemp: featureGet, + evtCoordinate: evt.coordinate + }, () => this.setActiveListFeature()); + } + } + + removeChosenLayer = () => { + this.olmap.getLayers().forEach((layer, i) => { + if (layer) { + if (layer.get('name') !== undefined && layer.get('name') === 'ChosenLayer' ) { + layer.getSource().clear(); + this.olmap.removeLayer(layer); + } + } + }); + } + + openPopupRight() { + // console.log('opening popup right...') + this.setState({popupRightVisible: true}, () => this.setActiveListFeature()); + } + + closePopupRight() { + // console.log('closing popup right...') + this.setState({popupRightVisible: false, popupDataTemp: []}, () => this.setActiveListFeature()); + this.removeChosenLayer(); // selected features + // this.removeLayerByName('routeLayer'); + this.setState({editGeometryVisible: false, routingBarVisible: false}); // disable editing when no ChosenLayer on Map + } + + /*toggleImagePopup() { + this.setState({imagePopupVisible: !this.state.imagePopupVisible}); + }*/ + + setPopupDataTemp = (feature) => { + // console.log('setPopupDataTemp', feature); + this.setState({popupDataTemp: [feature]}, () => this.setActiveListFeature()); + } + + reloadPopupData = () => { + const { evtCoordinate } = this.state; + } + + setActiveListFeature = () => { + // console.log('this.state.popupDataTemp', this.state.popupDataTemp); + if (this.state.popupRightVisible) { + if (this.state.popupDataTemp.length === 1) { + this.setState({activeListFeatureId: this.state.popupDataTemp[0].id}, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + let layerName = this.state.activeListFeatureId ? this.state.activeListFeatureId.substr(0, this.state.activeListFeatureId.indexOf('.')) : ''; + this.getLayerAttribute(layerName); + }) + } + else { + this.setState({activeListFeatureId: ''}, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + }) + } + } + else { + this.setState({activeListFeatureId: ''}, () => { + // console.log('activeListFeatureId', this.state.activeListFeatureId); + }) + } + } + + // getGeomType = async (layerName) => { + + // let res = await getGeomType(layerName); + // console.log('getGeomType', res); + // return res; + // } + + loadMap = async () => { + let response = await axios.get(API_LOAD_MAP).then(res => res).catch(error => error); + console.log('loadMap', response); + // this.getHomeView(response); + // this.olmap.getView().fit(new transformExtent(Bali_bbox, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + if (response.data !== undefined) { + if (response.data.length > 0) { + let dataMap = response.data[0]; + let layersToRemove = []; + + // if the API response about zoom is null + if (dataMap.zoom == null) { + this.setDefaultMap(); + } + // if the API response about center_x and center_y is null + else if (dataMap.center_x == null && dataMap.center_y == null ) { + this.setDefaultMap(); + } + // otherwise, add layer to map from map_layer response + else { + // set the zoom and center + this.setState({mapZoom: dataMap.zoom, mapCenter: [dataMap.center_x, dataMap.center_y]}); + this.olmap.getView().animate({ + zoom: dataMap.zoom, + center: [dataMap.center_x, dataMap.center_y] + }); + + // if map_layers from API is exist + if (dataMap.map_layers.length > 0) { + // first, removing all layers from the current map + this.olmap.getLayers().forEach((layer, i) => { + layersToRemove.push(layer); + }); + if (layersToRemove.length > 0) { + for(let i=0; i < layersToRemove.length; i++) { + this.olmap.removeLayer(layersToRemove[i]); + } + } + + // add map_layers from api to view + if (this.olmap.getLayers().array_.length < 1) { + // console.log('layer empty', response.data); + let map_layers = dataMap.map_layers; + if (map_layers !== undefined) { + // console.log('ada map_layers', map_layers); + if (map_layers.length > 0) { + for (let i=0; i < map_layers.length; i++) { + let newLayer = null; + + // if layer_type is not base + if (map_layers[i].layer_type !== 'base') { + newLayer = new ImageLayer({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new OlSourceImageWMS(map_layers[i].layer_source), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + // if layer_type is base + else { + // if base is OSM (OlSourceOsm); + if (map_layers[i].layer_name == 'OSM') { + newLayer = new OlLayerTile({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new OlSourceOsm(), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + // if base is other than OSM + else { + newLayer = new OlLayerTile({ + name: map_layers[i].layer_name, + title: map_layers[i].layer_title, + source: new XYZSource(map_layers[i].layer_source), + type: map_layers[i].layer_type, + geom_type: map_layers[i].layer_geom_type, + visible: map_layers[i].layer_visible, + // zIndex: map_layers[i].layer_position + }) + } + } + + // console.log('adding new layer', newLayer); + + // add the map_layers from API + this.olmap.addLayer(newLayer); + } + } + } + } + } + } + } + } + } + + saveMap = () => { + /* What map update data that are needed? + - title of map (o) + - zoom + - projection + - center_x + - center_y + - base_layer_id + - mapLayers[] + - layer_name + - layer_type (base / layer) + - layer_geom_type + - layer_source + - layer_visible + - layer_position + + Example of layer_source: + { + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sekolah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sekolah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous' + } + */ + let confirmation = window.confirm('Are you sure you want to save this map?'); + // let mapId = localStorage.getItem('u_group') === "kominfo" ? 2 : 1; + let mapId = MAP_ID; // get from m_group + let mapTitle = localStorage.getItem('u_group')+"_map"; // get from m_group + let mapZoom = this.olmap.getView().getZoom(); + let mapProjection = this.olmap.getView().getProjection().code_; + let mapCenter = this.olmap.getView().getCenter(); + let center_x = mapCenter[0]; // longitude + let center_y = mapCenter[1]; // latitude + let mapLayers = []; + let requestPayload = null; + let layerType = ''; + let layerGeomType = ''; + let count = 0; + + if (confirmation) { + this.olmap.getLayers().forEach( async (layer, i) => { + + // console.log('layer after confirmation i', i, layer); + layerType = layer.get('type') !== undefined ? layer.get('type') : 'layer'; + + if (layer.get('name') !== "DrawingLayer" && layer.get('name') !== "ChosenLayer") { + if (layer.get('type') === "base") { + if (layer.get('name') === "OSM") { + // if the baselayer is OSM, use the OSM function from openlayers + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: 'base', + layer_source: "OlSourceOsm", // i don't know why null in database + layer_visible: layer.getVisible(), + layer_position: i + }); + } + else { + // or if the baselayer is other than OSM (such as ESRI, Google, etc), use the XYZSource + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: 'base', + layer_source: { + // url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + url: layer.getSource().urls[0], + projection: mapProjection, + maxZoom: 18 + }, + layer_visible: layer.getVisible(), + layer_position: i + }); + } + } + else { + // if the layer is not base layer + // layerGeomType = layer.get('geom_type') !== undefined ? layer.get('geom_type') : await this.getGeomType(layer.get('name')); + let reqGeomType = await getGeomType(layer.get('name')); + if (reqGeomType.success) { + layerGeomType = reqGeomType.result; + + mapLayers.push({ + idx: i, + layer_name: layer.get('name'), + layer_type: layerType, + layer_geom_type: layerGeomType, + layer_source: { + url: appConfig.geoserver_host+'wms', + // url: appConfig.geoserver_host+'gwc/service/wms?SERVICE=WMS', + params: { + 'LAYERS': appConfig.workspace_name+':'+layer.get('name'), + 'TILED': true, + 'SLD': layerStyleUrl+layer.get('name') + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous' + }, + layer_visible: layer.getVisible(), + layer_position: i + }); + } + } + } + count = count + 1; + + // the last loop, so send to updateMap API + // console.log('count', count); + // console.log('this.olmap.getLayers().array_.length',this.olmap.getLayers().array_.length); + // console.log('if count==this.olmap.getLayers().array_length', count == this.olmap.getLayers().array_.length); + if (count == this.olmap.getLayers().array_.length) { + console.log('terakhirrrrrr'); + requestPayload = { + 'map_id': mapId, + 'map_title': mapTitle, + 'map_zoom': mapZoom, + 'map_projection': mapProjection, + 'center_x': center_x, + 'center_y': center_y, + 'map_layers': mapLayers + }; + console.log('requestPayload',requestPayload); + this.saveMapToApi(requestPayload); + } + }); + } + } + + saveMapToApi = async (requestPayload) => { + /*const config = { + headers: {'Content-Type': 'application/json'} + }; + console.log('saveMapToApi requestPayload', requestPayload); + let reqAxios = await axios.post(API_UPDATE_MAP, requestPayload, config).then(res => res).catch(error => error); + console.log('updateMap API',reqAxios);*/ + + + const param = { + method: 'POST', + header: JSON.stringify({'Content-Type': 'application/json'}), + body: JSON.stringify(requestPayload) + } + + try { + const result = await fetch(API_UPDATE_MAP, param).then(response => response.json()).then(res => res) + if(result.data){ + console.log('after save',result); + // this.(); + this.setState({alert: true, messageAlert: result.code_message, successAlert: true, dangerAlert: false}) + } else { + this.setState({alert: true, messageAlert: result.code_message, successAlert: false, dangerAlert: true}) + } + } catch(err) { + this.setState({alert: true, messageAlert: err.message.toString(), successAlert: false, dangerAlert: true}) + } + } + + toggleEditGeometry = (selectedPopupData) => { + let { editGeometryVisible } = this.state; + if (!editGeometryVisible) { + this.setState({ + editGeometryVisible: true, + layerNameDraw: selectedPopupData.id, + geomTypeDraw: selectedPopupData.geometry.type + }); + + } + else { + this.cancelDraw(); + } + } + + cancelDraw = () => { + this.setState({ + editGeometryVisible: false, + layerNameDraw: '', + geomTypeDraw: '' + }); + } + + toggleActiveStateAddGeometry = () => { + this.setState({activeStateAddGeometry: !this.state.activeStateAddGeometry}); + } + + printMap = () => { + /*let content = document.getElementById('map'); + content.print();*/ + + /*window.print();*/ + + /*let content = document.getElementById('map'); + console.log('content', content); + let pri = document.getElementById('ifmcontentstoprint').contentWindow; + pri.document.open(); + pri.document.write(content.innerHTML); + pri.document.close(); + pri.focus(); + pri.print();*/ + + // let canvas = document.getElementById("map").getElementsByClassName("ol-unselectable")[0]; + // console.log('canvas', canvas); + // canvas.setAttribute('crossorigin', true); + // let img = canvas.toDataURL("image/png"); + // console.log('img', img); + // let link = document.getElementById('image-download'); + // link.href = img; + // link.click(); + + // this.olmap.once('rendercomplete', () => { + // // var mapCanvas = document.createElement('canvas'); + // let mapCanvas = document.getElementById("map").getElementsByClassName("ol-unselectable")[0]; + // var size = this.olmap.getSize(); + // mapCanvas.width = size[0]; + // mapCanvas.height = size[1]; + // var mapContext = mapCanvas.getContext('2d'); + // console.log('mapContext', mapContext); + // Array.prototype.forEach.call( + // document.querySelectorAll('.ol-layer canvas'), + // (canvas) => { + // if (canvas.width > 0) { + // var opacity = canvas.parentNode.style.opacity; + // mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity); + // var transform = canvas.style.transform; + // // Get the transform parameters from the style's transform matrix + // var matrix = transform + // .match(/^matrix\(([^\(]*)\)$/)[1] + // .split(',') + // .map(Number); + // // Apply the transform to the export map context + // CanvasRenderingContext2D.prototype.setTransform.apply( + // mapContext, + // matrix + // ); + // mapContext.drawImage(canvas, 0, 0); + // } + // } + // ); + // if (navigator.msSaveBlob) { + // // link download attribuute does not work on MS browsers + // navigator.msSaveBlob(mapCanvas.msToBlob(), 'map.png'); + // } else { + // var link = document.getElementById('image-download'); + // link.href = mapCanvas.toDataURL(); + // link.click(); + // } + // }); + // this.olmap.renderSync(); + + this.olmap.once('rendercomplete', () => { + domtoimage + .toJpeg(this.olmap.getViewport()) + .then((dataUrl) => { + console.log('dataUrl', dataUrl); + // var pdf = new jsPDF('landscape', undefined, 'a4'); + // pdf.addImage(dataUrl, 'JPEG', 0, 0, 297, 210); + // pdf.save('map.pdf'); + + // let img = dataUrl.toDataURL("image/png"); + // console.log('img', img); + let link = document.getElementById('image-download'); + link.href = dataUrl; + link.click(); + toast.success("Success Print Map!") + }) + .catch(e => toast.error(e.toString())); + }); + + } + + signOut = (e) => { + e.preventDefault() + // localStorage.removeItem("u_group"); + // localStorage.removeItem("fullname"); + window.localStorage.clear(); + // emptyConstants(); + this.props.history.push('/login'); + } + + getLayerAttribute = async (layerName) => { + const res = await getLayerAttribute(layerName); + console.log('getLayerAttribute',res); + if (res.success) { + // console.log(res.result); + if (res.result.data) { + if (res.result.data.length > 0) { + this.setState({layer_attribute: res.result.data}, () => { + console.log(this.state.layer_attribute); + }); + } + } + } + else { + // alert(res.result); + toast.warn(res.result); + } + } + + // when checking chekbox on Layer Tree Panel + onCheckOpt = (state, checkedKeys) => { + this.setState({[state]: checkedKeys}); + } + + setLayer = async (state) => { + console.log('setLayer', state); + + // console.log('salesGeojson', salesGeojson); + + // let extent = this.olmap.getView().calculateExtent(this.olmap.getSize()); + + // console.log('extent', extent); + + await this.setState({isProcessing: true}); + + this.closePopupRight(); + // this.clearMapLayers(); + + const { checkedKeysSales, checkedKeysCustomer, checkedKeysOffice, checkedKeysDemografi, checkedKeysAnalisa, checkedKeysEmployeeDivision, + salesGroupTree, employeeDivisionTree, queryBuilderOutput, queryBuilderType, checkedKeysProjectTree, projectTree } = this.state; + let layersToAdd = []; + let newLayer = null; + let vectorSource = null; + let vectorLayer = null; + + // Sales + if (state === 'checkedKeysSales') { + + let filterGroup = []; + let sales_group_id = null; + + // first remove officeLayer & routeLayer + this.removeLayerByName('salesLayer'); + this.removeLayerByName('routeLayer'); + + if (checkedKeysSales && checkedKeysSales.length > 0) { + + // let sales = await this.getSalesFeatures(); + // console.log('sales', sales); + + // vectorSource = new VectorSource({ + // format: new GeoJSON(), + // url: salesGeojson + // }) + + // vectorLayer = new VectorLayer({ + // name: 'salesLayer', + // source_type: 'geojson', + // source: vectorSource + // }); + + // layersToAdd.push(vectorLayer); + + // kalo centang parent nya + if (checkedKeysSales.includes('sales-0')) { + // ambil group_id children + for (let i=0; i < salesGroupTree[0].children.length; i++) { + // console.log('select all salesGroupTree', salesGroupTree[0].children[i]); + sales_group_id = salesGroupTree[0].children[i].sales_group_id; + filterGroup.push(sales_group_id); + } + } + else { + // ambil berdasarkan yang diselect + for (let i=0; i < checkedKeysSales.length; i++) { + let obj = salesGroupTree[0].children.find(data => (data.key == checkedKeysSales[i])) + // console.log('selected checkedKeysSales obj', obj); + sales_group_id = obj.sales_group_id; + filterGroup.push(sales_group_id); + } + } + + // get the API data first + let sales = await getSalesFeatures(filterGroup, this.state.salesGroupTree); + console.log('sales', sales); + if(queryBuilderType === "Sales"){ + if(queryBuilderOutput !== ""){ + const data = sales.features; + let res = alasql(queryBuilderOutput, [data]); + console.log('res alasql', res); + sales = { + type:"FeatureCollection", + features:res + } + } + } + + // generate new layer + if (sales && sales.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(sales, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'salesLayer', + source_type: 'geojson', + source: vectorSource, + style: SALES_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + else { + toast.warn('Sales data is not found at this group'); + } + } + else if (checkedKeysSales && checkedKeysSales.length < 1) { + this.removeLayerByName('salesLayer'); + this.removeLayerByName('routeLayer'); + this.closePopupRight(); + } + } + + // Customer + else if (state === 'checkedKeysCustomer') { + + // first remove customerLayer + this.removeLayerByName('customerLayer'); + + // then add new customerLayer + if (checkedKeysCustomer && checkedKeysCustomer.length > 0) { + + // get the API data first + let customer = await getCustomerFeatures(); + console.log('customer', customer); + if(queryBuilderType === "Customer"){ + if(queryBuilderOutput !== ""){ + const data = customer.features; + let res = alasql(queryBuilderOutput, [data]); + customer = { + type:"FeatureCollection", + features:res + } + } + } + + + if (customer) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(customer, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'customerLayer', + source_type: 'geojson', + source: vectorSource, + style: CUSTOMER_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + + + } + else { + toast.warn('Unable to load customer data.'); + } + + } + else if (checkedKeysCustomer && checkedKeysCustomer.length < 1) { + this.removeLayerByName('customerLayer'); + this.closePopupRight(); + } + } + + // Office + else if (state === 'checkedKeysOffice') { + + // first remove officeLayer + this.removeLayerByName('officeLayer'); + + // then add new layer + if (checkedKeysOffice && checkedKeysOffice.length > 0) { + + // get the API data first + let office = await getOfficeFeatures(); + // console.log('office', office); + + if(queryBuilderType === "Office"){ + if(queryBuilderOutput !== ""){ + const data = office.features; + let res = alasql(queryBuilderOutput, [data]); + office = { + type:"FeatureCollection", + features:res + } + } + } + + if (office) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(office, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'officeLayer', + source_type: 'geojson', + source: vectorSource, + style: OFFICE_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + else { + toast.warn('Unable to load office data.'); + } + } + else if (checkedKeysOffice && checkedKeysOffice.length < 1) { + this.removeLayerByName('officeLayer'); + this.closePopupRight(); + } + } + + // Demografi (WMS) + else if (state === 'checkedKeysDemografi') { + // kalo checkedKeysDemografi mengandung parent keynya (demografi-0), otomatis di loop semua children nya + // otherwise, ya keluarin layer berdasarkan yang dicentang aja. + + // first remove all layers demografi + for (let i=0; i < demografiTree[0].children.length; i++) { + this.removeLayerByName(demografiTree[0].children[i].layers.name); + } + + // then populate new layer + if (checkedKeysDemografi.length > 0) { + if (checkedKeysDemografi.includes('demografi-0')) { + // ambil semua children + for (let i=0; i < demografiTree[0].children.length; i++) { + newLayer = this.generateLayerWMSByName(demografiTree[0].children[i].layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + else { + // ambil salah satu objectnya berdasarkan yang diselect + for (let i=0; i < checkedKeysDemografi.length; i++) { + let obj = demografiTree[0].children.find(data => (data.key == checkedKeysDemografi[i])) + console.log('selected checkedKeysDemografi obj', obj); + newLayer = this.generateLayerWMSByName(obj.layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + } + } + + // Analisa (WMS) + else if (state === 'checkedKeysAnalisa') { + // kalo checkedKeysAnalisa mengandung parent keynya (analisa-0), otomatis di loop semua children nya + // otherwise, ya keluarin layer berdasarkan yang dicentang aja, + // tapi dalemannya ngecek lagi ada yg punya children juga ngga + + console.log('checkedKeysAnalisa', checkedKeysAnalisa); + + // first remove all layers analisa + for (let i=0; i < analisaTree[0].children.length; i++) { + let childLayerToRemove = this.findChildLayerToRemove(analisaTree[0].children[i]) + // console.log('childLayerToRemove', childLayerToRemove) + if (childLayerToRemove.length > 0) { + for (let j=0; j < childLayerToRemove.length; j++) { + this.removeLayerByName(childLayerToRemove[j]); + } + } + } + + // // then populate new layer + // if (checkedKeysAnalisa.length > 0) { + // if (checkedKeysAnalisa.includes('analisa-0')) { + // // ambil semua children + // for (let i=0; i < analisaTree[0].children.length; i++) { + + // let parentObj = analisaTree[0].children[i]; + + // let childLayer = this.findChildLayerToAdd(parentObj, checkedKeysAnalisa); + + // layersToAdd.concat(childLayer); + + // // newLayer = this.generateLayerWMSByName(analisaTree[0].children[i].layers.name); + // // if (newLayer) { + // // layersToAdd.push(newLayer); + // // } + // } + // } + // else { + // // ambil salah satu objectnya berdasarkan yang diselect + // for (let i=0; i < checkedKeysAnalisa.length; i++) { + // let obj = analisaTree[0].children.find(data => (data.key == checkedKeysAnalisa[i])) + // console.log('selected checkedKeysAnalisa obj', obj); + // if (obj) { + // newLayer = this.generateLayerWMSByName(obj.layers.name); + // if (newLayer) { + // layersToAdd.push(newLayer); + // } + // } + // // else { + // // for (let i=0; i < analisaTree[0].children.length; i++) { + // // let parentObj = analisaTree[0].children[i]; + // // // this.findChildLayerToAdd(parentObj); + // // let childLayer = this.findChildLayerToAdd(parentObj, checkedKeysAnalisa); + // // layersToAdd = [...layersToAdd, ...childLayer]; + // // } + // // } + // } + // } + // } + + // if (checkedKeysAnalisa.length > 0) { + + // // if (checkedKeysAnalisa.includes('analisa-0')) { + // // semuanya sambil ngecek apakah childrennya punya child + // for (let i=0; i < analisaTree[0].children.length; i++) { + // let childLayer = this.findChildLayerToAdd(analisaTree[0].children[i], checkedKeysAnalisa); + // console.log('childLayer', childLayer); + // layersToAdd.concat(childLayer); + // } + // // } + // // else { + // // cek berdasarkan centang tapi ngecek apakah childrennya punya child + // // } + + // // for (let i=0; i < analisaTree[0].children.length; i++) { + // // let parentObj = analisaTree[0].children[i]; + // // console.log('parentObj', parentObj); + // // let childLayer = this.findChildLayerToAdd(parentObj, checkedKeysAnalisa); + // // console.log('childLayer', childLayer); + // // layersToAdd.concat(childLayer); + // // } + // } + + + // then populate new layer + if (checkedKeysAnalisa.length > 0) { + + // for (let i=0; i < analisaTree.length; i++) { + // let childLayer = this.findChildLayerToAdd(analisaTree[i], checkedKeysAnalisa); + // console.log('childLayer', childLayer); + // layersToAdd.concat(childLayer); + // } + + // const getChild = (item) => item.children; + // let arr = []; + + // analisaTree.map((item, index) => { + + // console.log('item', item); + // if (item.hasOwnProperty('children')) { + // // getChild(item); + // arr = [...arr, ...concat(getChild(item))]; + // } + // else { + // arr = [...arr, ...concat(item)]; + // } + // }) + + // console.log('flattenAnalisa'. flattenAnalisa); + // console.log('arr', arr); + + // let flattenAnalisa = lodash.flatten(analisaTree); + // console.log('flattenAnalisa', flattenAnalisa); + + + if (checkedKeysAnalisa.includes('analisa-0')) { + // ambil semua children + for (let i=0; i < analisaTree[0].children.length; i++) { + newLayer = this.generateLayerWMSByName(analisaTree[0].children[i].layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + else { + // ambil salah satu objectnya berdasarkan yang diselect + for (let i=0; i < checkedKeysAnalisa.length; i++) { + let obj = analisaTree[0].children.find(data => (data.key == checkedKeysAnalisa[i])) + console.log('selected checkedKeysAnalisa obj', obj); + newLayer = this.generateLayerWMSByName(obj.layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + } + + } + + else if (state === 'checkedKeysEmployeeDivision') { + let filterGroup = []; + let employee_division_id = null; + + // first remove officeLayer & routeLayer + this.removeLayerByName('employeeLayer'); + // this.removeLayerByName('routeLayer'); + + if (checkedKeysEmployeeDivision && checkedKeysEmployeeDivision.length > 0) { + + // kalo centang parent nya + if (checkedKeysEmployeeDivision.includes('employee-0')) { + // ambil group_id children + for (let i=0; i < employeeDivisionTree[0].children.length; i++) { + // console.log('select all employeeDivisionTree', employeeDivisionTree[0].children[i]); + employee_division_id = employeeDivisionTree[0].children[i].employee_division_id; + filterGroup.push(employee_division_id); + } + } + else { + // ambil berdasarkan yang diselect + for (let i=0; i < checkedKeysEmployeeDivision.length; i++) { + let obj = employeeDivisionTree[0].children.find(data => (data.key == checkedKeysEmployeeDivision[i])) + // console.log('selected checkedKeysEmployeeDivision obj', obj); + employee_division_id = obj.employee_division_id; + filterGroup.push(employee_division_id); + } + } + + // get the API data first + let employee = await getEmployeeFeatures(filterGroup, this.state.employeeDivisionTree); + console.log('employee', employee); + if(queryBuilderType === "Employee"){ + if(queryBuilderOutput !== ""){ + const data = employee.features; + let res = alasql(queryBuilderOutput, [data]); + console.log('res alasql', res); + employee = { + type: "FeatureCollection", + features: res + } + } + } + + // generate new layer + if (employee && employee.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(employee, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'employeeLayer', + source_type: 'geojson', + source: vectorSource, + style: EMPLOYEE_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + else { + toast.warn('Employee data is not found at this group'); + } + } + else if (checkedKeysEmployeeDivision && checkedKeysEmployeeDivision.length < 1) { + this.removeLayerByName('employeeLayer'); + // this.removeLayerByName('routeLayer'); + this.closePopupRight(); + } + } + + else if (state === 'checkedKeysProjectTree') { + console.log('checkedKeysProjectTree', checkedKeysProjectTree); + + // first remove projectLayer and its features + this.removeLayerByName('routeLayer'); + this.removeLayerByName('projectLayer'); + this.projectFeatures = []; + this.waspangFeatures = []; + + if (checkedKeysProjectTree && checkedKeysProjectTree.length > 0) { + + /* + Logic: + - looping all tree + - there are 3 variables + 1. features (Array) -> untuk nampung features yg ada di laporan planning + 2. projectTree (Array) + 3. checkedKeysProjectTree (Array) -> projectTree yg tercentang + + Jadi looping all nesting yg ada di projectTree (ambil object children) + Terus cek setiap levelnya dia ada di checkedKeysProjectTree gak? + Jika ada, cek apakah di object tersebut mengandung key namanya "plannings" tidak? + Jika iya, maka cek di dalamnya ada laporan_planning tidak? + Jika iya, maka push ke features[] dengan format: + { + "type": "Feature", + "properties": {}, // isian dari objectnya + "geometry": { + "type": "Point", + "coordinates": [ + 107.90771484375, // object.lon + -6.795535025719518 // object.lat + ] + } + } + */ + + // for (let i=0; i < projectTree[0].children.length; i++) { + // this.getChildrenTree(projectTree[0].children[i]); + // } + this.getChildrenTree(projectTree[0].children); + this.getWaspangFeatures(); + + console.log('projectFeatures', this.projectFeatures); + + // get the API data first + // let project = await getEmployeeFeatures(filterGroup, this.state.employeeDivisionTree); + // console.log('employee', employee); + // if(queryBuilderType === "Employee"){ + // if(queryBuilderOutput !== ""){ + // const data = employee.features; + // let res = alasql(queryBuilderOutput, [data]); + // console.log('res alasql', res); + // employee = { + // type: "FeatureCollection", + // features: res + // } + // } + // } + + let project = null; + project = { + type: "FeatureCollection", + features: this.projectFeatures + } + + let waspang = null; + waspang = { + type: "FeatureCollection", + features: this.waspangFeatures + } + + // console.log('project', project); + + + // generate new layer + if (project && project.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(project, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'projectLayer', + source_type: 'geojson', + source: vectorSource, + style: PROJECT_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + + + // generate new layer + if (waspang && waspang.features.length > 0) { + vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(waspang, { + dataProjection: projection4326, // from + featureProjection: projection // to + }) + }) + + vectorLayer = new VectorLayer({ + name: 'waspangLayer', + source_type: 'geojson', + source: vectorSource, + style: WASPANG_FEATURES_STYLE + }); + + layersToAdd.push(vectorLayer); + } + + else { + toast.warn('Data realisasi tidak ditemukan di project ini'); + } + } + else if (checkedKeysProjectTree && checkedKeysProjectTree.length < 1) { + this.removeLayerByName('projectLayer'); + this.removeLayerByName('routeLayer'); + this.projectFeatures = []; + this.waspangFeatures = []; + this.closePopupRight(); + } + } + + if (layersToAdd.length > 0) { + for (let i=0; i < layersToAdd.length; i++) { + + this.olmap.addLayer(layersToAdd[i]); // adding layer to exist map + + console.log('check addLayer'); + if (i === layersToAdd.length - 1) { + let extent = await this.getExtentLayerByName(layersToAdd[i]); + if (extent) { + this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + } + } + } + } + + // this.olmap.getLayers().forEach((layer, i) => { + // console.log('getLayers', layer); + // }); + + await this.setState({isProcessing: false}); + } + + // find the lat lon inside laporan_plannings that the planning has been checked on projectTree + getChildrenTree = (data) => { + data.map((item, index) => { + if (item.children && item.children.length > 0) { + this.getChildrenTree(item.children); + } + else if (item.laporan_plannings && item.laporan_plannings.length > 0) { + if (this.state.checkedKeysProjectTree.includes(item.key)) { + for (let i=0; i < item.laporan_plannings.length; i++) { + console.log('got features!!!!!'); + this.projectFeatures.push({ + "type": "Feature", + "id": `realisasi.${item.laporan_plannings[i].id}`, + "properties": {...item.laporan_plannings[i]}, + "geometry": { + "type": "Point", + "coordinates": [ + item.laporan_plannings[i].lon, + item.laporan_plannings[i].lat + ] + } + }) + } + } + } + }); + + // console.log('features end....', features); + + // return features; + } + + getWaspangFeatures = () => { + this.waspangFeatures = [{ + "type": "Feature", + "id": `m_waspang.1`, + "properties": { + "id": 1, + "user_id": 10, + "nama_user": "Ryan", + "nama_proyek": "FTTH Paket 2", + "mulai_tugas": "2021-11-01T09:31:32.775Z", + "akhir_tugas": "2021-11-30T09:31:32.775Z", + "_type": "waspang" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.89285278320312, + -6.228275226686636 + ] + } + }, + { + "type": "Feature", + "id": `m_waspang.2`, + "properties": { + "id": 2, + "user_id": 12, + "nama_user": "Effendi", + "nama_proyek": "FTTH Paket 2", + "mulai_tugas": "2021-11-01T09:31:32.775Z", + "akhir_tugas": "2021-11-30T09:31:32.775Z", + "_type": "waspang" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.9357681274414, + -6.267522831839373 + ] + } + }, + { + "type": "Feature", + "id": `m_waspang.3`, + "properties": { + "id": 1, + "user_id": 16, + "nama_user": "Waspang C", + "nama_proyek": "FTTH Paket 2", + "mulai_tugas": "2021-11-01T09:31:32.775Z", + "akhir_tugas": "2021-11-30T09:31:32.775Z", + "_type": "waspang" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 106.80564880371092, + -6.263086293713913 + ] + } + }] + } + + findChildLayerToRemove = (parentObj) => { + + let layersToRemove = []; + + if (parentObj.hasOwnProperty('children')) { + + // get semua children layer name nya + for (let i=0; i < parentObj.children.length; i++) { + // newLayer = this.generateLayerWMSByName(parentObj.children[i].layers.name); + // if (newLayer) { + // layersToAdd.push(newLayer); + // } + + layersToRemove.push(parentObj.children[i].layers.name) + } + } + else { + layersToRemove.push(parentObj.layers.name) + } + + return layersToRemove; + + } + + // findChildLayerToAdd = (parentObj) => { + // for (let i=0; i < parentObj.length; i++) { + + // } + // } + + findChildLayerToAdd = (parentObj, checkedKeys) => { + + let layersToAdd = []; + let newLayer = null; + + // 1. cek apakah parentObj itu kecentang atau ngga + // 2. cek apakah parentObj itu punya child apa ngga + // kalo kecentang parent nya ya berarti loop aja + // 3. cek apakah childnya itu kecentang ngga + + console.log('parentObj', parentObj); + + if (checkedKeys.includes(parentObj["key"])) { + if (parentObj.hasOwnProperty('children')) { + // loop aja semua child layernya + for (let i=0; i < parentObj.children.length; i++) { + newLayer = this.generateLayerWMSByName(parentObj.children[i].layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + else { + newLayer = this.generateLayerWMSByName(parentObj.layers.name); + if (newLayer) { + layersToAdd.push(newLayer); + } + } + } + else { + if (parentObj.hasOwnProperty('children')) { + + } + + } + + // // tapi dicek lagi kalo childrennya punya children + // if (parentObj.hasOwnProperty('children')) { + + // // cek apakah parent nya kecentang gak? (berdasarkan key) + // if (checkedKeys.includes(parentObj["key"])) { + + // // centang semua children nya + // for (let i=0; i < parentObj.children.length; i++) { + // newLayer = this.generateLayerWMSByName(parentObj.children[i].layers.name); + // if (newLayer) { + // layersToAdd.push(newLayer); + // } + // } + // } + // // just activate the selected nya aja + // else { + // // for (let i=0; i < checkedKeys.length; i++ ) { + // // let obj = parentObj.children.find(data => data.key == checkedKeys[i]) + // // console.log('ngecek obj', obj); + // // newLayer = this.generateLayerWMSByName(obj.layers.name); + // // if (newLayer) { + // // layersToAdd.push(newLayer); + // // } + // // } + // } + // } + // else { + // newLayer = this.generateLayerWMSByName(parentObj.layers.name); + // if (newLayer) { + // layersToAdd.push(newLayer); + // } + // } + + return layersToAdd; + + } + + getExtentLayerByName = async (layer) => { + + // console.log('getExtentLayerByName', layerName, source_type); + + let layerName = layer.get('name'); + let source_type = layer.get('source_type'); + + // if source_type is undefined = WMS + // otherwise source_type is not undefined = geojson + + let extent = null; + + if (source_type === 'wms') { + let getExt = await fetch(WMS_CAPABILITIES_URL_2).then((response) => { + return response.text(); + }).then((text) => text); + + let result = new WMSCapabilities().read(getExt); + if (result && result.Capability.Layer.Layer.find(l => l.Name === appConfig.workspace_name+':'+layerName)) { + extent = result.Capability.Layer.Layer.find(l => l.Name === appConfig.workspace_name+':'+layerName).EX_GeographicBoundingBox; + } + } + else if (source_type === 'geojson') { + // extent = layer.getSource().getExtent(); + + // layer.getSource().once('change',(e) => { + // if (layer.getSource().getState() === 'ready') { + // extent = layer.getSource().getExtent(); + // console.log('extent------------', extent); + // // map.getView().fit(extent, map.getSize()); + // } + // }); + extent = null + } + console.log('extent', extent); + return extent; + + } + + generateLayerWMSByName = (layerName) => { + let theLayer = null; + if (layerName) { + theLayer = new OlLayerTile({ + name: layerName, + source_type: 'wms', + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':'+layerName, + 'TILED': true, + 'SLD': layerStyleUrl+layerName, + // 'CQL_FILTER': CQL_QUERY + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + }), + type: 'layer', + geom_type: 'Polygon' + }); + } + return theLayer; + } + + clearMapLayers = () => { + // console.log('clearing layers'); + // clear map. removing all layer other than OSM + let removeLayers = []; + this.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') !== 'OSM') { + removeLayers.push(layer); + } + }); + // console.log('removeLayers', removeLayers); + if (removeLayers.length > 0) { + for (let i=0; i < removeLayers.length; i++) { + this.olmap.removeLayer(removeLayers[i]); + } + } + } + + removeLayerByName = (name) => { + + let removeLayers = []; + this.olmap.getLayers().forEach((layer, i) => { + if (layer.get('name') === name) { + removeLayers.push(layer); + } + }); + // console.log('removeLayers', removeLayers); + if (removeLayers.length > 0) { + for (let i=0; i < removeLayers.length; i++) { + this.olmap.removeLayer(removeLayers[i]); + } + } + } + + getRoute = () => { + fetch(routeDummy).then((response) => { + response.json().then((result) => { + console.log('getRoute', result); + }) + }); + } + + showRoute = (salesRoute) => { + const { mapProjection } = this.state; + console.log('showRoute', salesRoute); + + this.removeLayerByName('routeLayer'); + if (salesRoute.features && salesRoute.features.length < 1) { + toast.warn("Couldn't show route at selected time. Please select another range time."); + return; + } + + let route = null; + let polyline = null; + let extent = null; + + // polyline = routeDummy.routes[0].overview_polyline.points; + // console.log('polyline', polyline); + + // route = new Polyline({ + // }).readGeometry(polyline, { + // dataProjection: 'EPSG:4326', + // featureProjection: 'EPSG:4326', + // }); + + // let route = new Polyline().readGeometry(polyline); + + // polyline = { + // "type": "LineString", + // "coordinates": [ + // [ + // 106.78853, + // -6.26235 + // ], + // [ + // 106.78863, + // -6.26201 + // ], + // [ + // 106.80065, + // -6.24523 + // ] + // ] + // } + + // console.log('check route', salesRoute.features.length > 0 && (salesRoute.features[0].geometry && salesRoute.features[0].geometry.coordinates)); + + if (salesRoute.features.length > 0 && (salesRoute.features[0].geometry && salesRoute.features[0].geometry.coordinates)) { + polyline = salesRoute.features[0].geometry; + + route = new LineString(polyline.coordinates).transform('EPSG:4326', mapProjection); + extent = route.getExtent(); + + console.log('route', route); + console.log('extent', extent); + + let routeFeature = new Feature({ + type: 'route', + geometry: route, + geometry_name: salesRoute.features[0].geometry_name, + id: salesRoute.features[0].id, + properties: salesRoute.features[0].properties, + routeColor: getRandomColor() + }); + let geoMarker = new Feature({ + type: 'geoMarker', + geometry: new Point(route.getCoordinateAt(0)), + id: salesRoute.features[0].id, + properties: salesRoute.features[0].properties + }); + let startMarker = new Feature({ + type: 'pinRouteStart', + geometry: new Point(route.getCoordinateAt(0)), + id: salesRoute.features[0].id, + properties: salesRoute.features[0].properties + }); + let endMarker = new Feature({ + type: 'pinRouteEnd', + geometry: new Point(route.getCoordinateAt(1)), + id: salesRoute.features[0].id, + properties: salesRoute.features[0].properties + }); + + let animating = false; + + let vectorLayer = new VectorLayer({ + name: 'routeLayer', + source_type: 'routeLayer', + source: new VectorSource({ + features: [routeFeature, geoMarker, startMarker, endMarker], + }), + style: (feature, resolution) => { + // hide geoMarker if animation is active + if (animating && feature.get('type') === 'geoMarker') { + return null; + } + // return ROUTE_MAP_STYLES[feature.get('type')]; + return ROUTE_MAP_STYLES(feature, resolution); + }, + }); + + this.olmap.addLayer(vectorLayer); + + if (extent) { + // this.olmap.getView().fit(new transformExtent(extent, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + this.olmap.getView().fit(extent, { size: this.olmap.getSize(), duration: 500 }); + + } + } + } + + searchRouting = async (userId, dateString) => { + await this.setState({isSearchingRoute: true, isProcessing: true}); + + const {routeType} = this.state; + let routes = null; + + if (routeType === 'sales') { + routes = await getSalesRoutingApi(userId, dateString); + } + else if (routeType === 'employee') { + routes = await getEmployeeRoutingApi(userId, dateString); + } + else if (routeType === 'waspang') { + routes = await getWaspangRoutingApi(userId, dateString); + } + + if (routes) { + this.setState({isSearchingRoute: false, isProcessing: false}, () => this.showRoute(routes)) + } + else { + this.setState({isSearchingRoute: false, isProcessing: false}, () => toast.warn("Sorry. Couldn't get user waypoint")); + } + } + + handleQueryBuilder = (query, type, tree) => { + console.log("query builder "+type, query); + this.setState({ queryBuilderOutput:query, queryBuilderType:type, currentQbTree:tree,currentQbType:type }, () => { + if(type === "Sales"){ + this.setLayer('checkedKeysSales'); + }else if(type === "Customer"){ + this.setLayer('checkedKeysCustomer'); + }else if(type === "Office"){ + this.setLayer('checkedKeysOffice'); + } + }) + } + + handleQbReset = (type) => { + this.setState({ + queryBuilderOutput:'', + queryBuilderType:'', + currentQbTree:'', + currentQbType:'' }, () => { + if(type === "Sales"){ + this.setLayer('checkedKeysSales'); + }else if(type === "Customer"){ + this.setLayer('checkedKeysCustomer'); + }else if(type === "Office"){ + this.setLayer('checkedKeysOffice'); + } + }) + } + + + render() { + const { alert, successAlert, dangerAlert, messageAlert } = this.state; + return ( +
+ + this.setState({alert: false, successAlert: false, dangerAlert: false})}> + {messageAlert} + + + { this.state.isProcessing && +
+ +
+ } + this.saveMap()} + printMap={() => this.printMap()} + onLogout={(e) => this.signOut(e)} + /> + + + +
{this.state.popupRightVisible ? + this.setPopupDataTemp(feature)} + layerName={this.state.activeListFeatureId ? this.state.activeListFeatureId.substr(0, this.state.activeListFeatureId.indexOf('.')) : ''} + toggleEditGeometry={(selectedPopupData) => this.toggleEditGeometry(selectedPopupData)} + activeStateAddGeometry={this.state.activeStateAddGeometry} + fid={this.state.activeListFeatureId} + closePopupRight={this.closePopupRight} + getLayerAttribute={(layerName) => this.getLayerAttribute(layerName)} + layer_attribute={this.state.layer_attribute} + searchLabelData={this.state.searchLabelData} + layerInfo={this.state.layerInfo} + toggleRoutingBarVisible={() => this.setState({routingBarVisible: !this.state.routingBarVisible})} + setRouteType={(routeType) => this.setState({routeType: routeType})} + /> : null + }
+
+ + + + this.openPopupRight()} + closePopupRight={() => this.closePopupRight()} + popupDataTemp={this.state.popupDataTemp} + removeChosenLayer={() => this.removeChosenLayer()} + setPopupDataTemp={(feature) => this.setPopupDataTemp(feature)} + activeListFeatureId={this.state.activeListFeatureId} + editGeometryVisible={this.state.editGeometryVisible} + toggleActiveStateAddGeometry={() => this.toggleActiveStateAddGeometry()} + searchLabelData={this.state.searchLabelData} + layerInfo={this.state.layerInfo} + onCheckOpt={(state, checkedKeys) => this.onCheckOpt(state, checkedKeys)} + checkedKeysSales={this.state.checkedKeysSales} + checkedKeysCustomer={this.state.checkedKeysCustomer} + checkedKeysOffice={this.state.checkedKeysOffice} + checkedKeysDemografi={this.state.checkedKeysDemografi} + checkedKeysAnalisa={this.state.checkedKeysAnalisa} + checkedKeysEmployeeDivision={this.state.checkedKeysEmployeeDivision} + checkedKeysProjectTree={this.state.checkedKeysProjectTree} + salesGroupTree={this.state.salesGroupTree} + employeeDivisionTree={this.state.employeeDivisionTree} + setSalesGroupTree={(data) => this.setState({salesGroupTree: data})} + setEmployeeDivisionTree={(data) => this.setState({employeeDivisionTree: data})} + showRoute={(route) => this.showRoute(route)} + removeLayerByName={(layerName) => this.removeLayerByName(layerName)} + setIsProcessing={(data) => this.setState({isProcessing: data})} + handleQueryBuilder={this.handleQueryBuilder} + currentQbTree={this.state.currentQbTree} + currentQbType={this.state.currentQbType} + handleQbReset={this.handleQbReset} + projectTree={this.state.projectTree} + setProjectTree={(data) => this.setState({projectTree: data})} + /> + + + { + this.state.editGeometryVisible && + this.cancelDraw()} + layerName={this.state.layerNameDraw} + geomType={this.state.geomTypeDraw} + olmap={this.olmap} + /> + } + + { this.state.routingBarVisible && + this.setState({routingBarVisible: !this.state.routingBarVisible})} + popupDataTemp={this.state.popupDataTemp} + setPopupDataTemp={(feature) => this.props.setPopupDataTemp(feature)} + searchRouting={(userId, dateString) => this.searchRouting(userId, dateString)} + isSearchingRoute={this.state.isSearchingRoute} + setRouteType={(routeType) => this.setState({routeType: routeType})} + /> + } + + + {/**/} + + +
+
+
+ ); + } +} + +export default SiopasMap; + diff --git a/src/views/Map_backup/Map_backup.js b/src/views/Map_backup/Map_backup.js new file mode 100644 index 0000000..8b09655 --- /dev/null +++ b/src/views/Map_backup/Map_backup.js @@ -0,0 +1,965 @@ +/* +maplayer: +{ + name: "Polygon Tanah Kantor Instansi Pemerintah", + source: { + "url": "http://192.168.99.110/geoserver/wms", + "params": { + "LAYERS": "bmd_denpasar:tanah_kantor_instansi_pemerintah", + "TILED": true, + "SLD": "http://192.168.99.110/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS=bmd_denpasar:tanah_kantor_instansi_pemerintah" + }, + "serverType": "geoserver", + "transition": 0, + "crossOrigin": "anonymous" + }, + type: 'layer', + geom_type: 'Polygon' +}, + +{ + name: "Polygon Sekolah", + source: { + "url": "http://192.168.99.110/geoserver/wms", + "params": { + "LAYERS": "bmd_denpasar:tanah_sekolah", + "TILED": true, + "SLD": "http://192.168.99.110/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetStyles&LAYERS=bmd_denpasar:tanah_sekolah" + }, + "serverType": "geoserver", + "transition": 0, + "crossOrigin": "anonymous" + }, + type: 'layer', + geom_type: 'Polygon' +} + +baselayer +---------- +{ + name: 'OSM', + source: new OlSourceOsm(), + type: 'base', + imageName: 'osm.PNG' +} + +{ + name: "ESRI", + source: { + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", + "projection": "EPSG:3857", + "maxZoom": 18 + }, + type: 'base', + imageName: 'esri.PNG' +}); + + +*/ + +import React, { Component, Suspense, Fragment } from 'react'; +import ReactDOM from 'react-dom'; +// import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap'; +import { Button, UncontrolledTooltip } from 'reactstrap'; + +import './Map.css'; +import './Popup.css'; +import './CustomScroll.css'; +import 'ol/ol.css'; +import 'antd/dist/antd.css'; +import './react-geo.css'; + +import OlMap from 'ol/Map'; +import OlView from 'ol/View'; +import OlLayerTile from 'ol/layer/Tile'; +import OlSourceOsm from 'ol/source/OSM'; +import OlSourceTileJson from 'ol/source/TileJSON'; +import OlLayerGroup from 'ol/layer/Group'; +import OlSourceTileWMS from 'ol/source/TileWMS'; +// import OlLayerSwitcher from 'ol/control/LayerSwitcher'; +import {fromLonLat, transformExtent} from 'ol/proj'; +import {Vector as VectorSource, XYZ as XYZSource, Cluster} from 'ol/source'; +import Overlay from 'ol/Overlay'; +import { Draw, Select } from 'ol/interaction'; + +import { Drawer } from 'antd'; +import { + SimpleButton, + MapComponent, + NominatimSearch, + MeasureButton, + // LayerTree, + MapProvider, + mappify, + onDropAware, + // AddWmsPanel +} from '@terrestris/react-geo'; + +import CapabilitiesUtil from '@terrestris/ol-util/dist/CapabilitiesUtil/CapabilitiesUtil'; +import LayerSwitcher from '@terrestris/react-geo/dist/LayerSwitcher/LayerSwitcher'; +import axios from 'axios'; +// import { PopupContainer } from '../../components/PopupContainer'; +import PopupContainer from '../../components/PopupContainer'; +import { + AppHeader +} from '@coreui/react'; +// import { Badge, UncontrolledDropdown, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, +// InputGroup, Input, InputGroupAddon, InputGroupText, Button +// } from 'reactstrap'; + +// const DefaultHeader = React.lazy(() => import('../../containers/DefaultLayout/DefaultHeader')); +// import MapHeader from './MapHeader'; +import MapHeader from '../../components/MapHeader'; +import MapToolbar from '../../components/MapToolbar'; +// import MapLayerSwitcher from '../../components/MapLayerSwitcher'; +import { appConfig, setRequestMapHeader, layerStyleUrl } from '../../const/MapConst.js'; +import { Icon } from '@iconify/react'; +import imageOutline from '@iconify/icons-ion/image-outline'; +import trashOutline from '@iconify/icons-ion/trash-outline'; +import ellipsisVerticalSharp from '@iconify/icons-ion/ellipsis-vertical-sharp'; +import listOutline from '@iconify/icons-ion/list-outline'; +import brushIcon from '@iconify/icons-ion/brush'; +import createOutline from '@iconify/icons-ion/create-outline'; +import contractIcon from '@iconify/icons-ion/contract'; +import {Col, Row} from 'reactstrap'; + +// import { +// custom, //name spaces +// //group +// //objects +// } from "react-openlayers"; + + +const MappifiedNominatimSearch = mappify(NominatimSearch); +const MappifiedMeasureButton = mappify(MeasureButton); +// const MappifiedLayerTree = mappify(LayerTree); +const Map = mappify(onDropAware(MapComponent)); + +// const center = [ 788453.4890155146, 6573085.729161344 ]; +const projection = 'EPSG:3857'; //default +const projection4326 = 'EPSG:4326'; +// const projection = 'EPSG:4326'; // lat long +// Indonesia +const lat = -2.6000285; +const lon = 118.015776; +const zoom = 5; +const Indonesia = new fromLonLat([lon, lat], projection); +const Bali_bbox = [115.178638994694, -8.71934970794214, 115.269238650799, -8.59763413248024]; + +const osmLayer = new OlLayerTile({ + source: new OlSourceOsm(), + name: 'OSM', + type: 'base', + imageName: 'osm.PNG' +}); + +const esriLayer = new OlLayerTile({ + name: "ESRI", + source: new XYZSource({ + url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + projection: projection, + maxZoom: 18 + }), + type: 'base', + imageName: 'esri.PNG' +}); + +const denpasarLayer = new OlLayerTile({ + name: "Denpasar Map", + source: new XYZSource({ + url: 'http://siopas.co.id/bmd-denpasar/public/maps/mbtiles-mapservices.php?db=bali.mbtiles&z={z}&x={x}&y={y}', + projection: projection4326, + maxZoom: 18 + }), + type: 'base', + imageName: 'bali.PNG' +}); + +const citraDenpasarLayer = new OlLayerTile({ + name: "Citra Denpasar", + source: new XYZSource({ + url: 'http://siopas.co.id/bmd-denpasar/public/maps/mbtiles-mapservices.php?db=citra-denpasar-x.mbtiles&z={z}&x={x}&y={y}', + projection: projection4326, + maxZoom: 18 + }), + type: 'base', + imageName: 'bali_citra.PNG' +}); + +const googleLayer = new OlLayerTile({ + name: "Google", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 18 + }), + type: 'base', + imageName: 'google.PNG' +}) + +const googleStreetLayer = new OlLayerTile({ + name: "Google Street", + source: new XYZSource({ + url: 'http://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', + projection: projection, + // maxZoom: 18 + }), + type: 'base', + imageName: 'google_street.PNG' +}) +const baseLayers = [ + osmLayer, + esriLayer, + googleLayer, + googleStreetLayer, + denpasarLayer, + citraDenpasarLayer +] + +const baseLayerGroup = new OlLayerGroup({ + name: 'Base Layers', + layers: [ + osmLayer, + esriLayer + ], + type: 'baseGroup' +}); + +const layerGroupSekolah = new OlLayerGroup({ + name: 'Sekolah', + layers: [ + + new OlLayerTile({ + name: "Polygon Sekolah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sekolah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sekolah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Sekolah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_sekolah0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_sekolah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + ], + type: 'layerGroup' +}); + +const layerGroupRS = new OlLayerGroup({ + name: 'Rumah Sakit', + layers: [ + new OlLayerTile({ + name: "Polygon Rumah Sakit", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_rumah_sakit_puskesmas', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_rumah_sakit_puskesmas' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Rumah Sakit", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_rumah_sakit0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_rumah_sakit0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + ], + type: 'layerGroup' +}); + +const layerGroupKantorDesa = new OlLayerGroup({ + name: 'Kantor Desa dan Lurah', + layers: [ + new OlLayerTile({ + name: "Polygon Kantor Desa dan Lurah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_kantor_desa_dan_lurah0', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_kantor_desa_dan_lurah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Kantor Desa dan Lurah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_kantor_desa_lurah0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_kantor_desa_lurah0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }) + + ] +}); + +const layerGroupSarPras = new OlLayerGroup({ + name: 'Sarana Prasarana', + layers: [ + new OlLayerTile({ + name: "Polygon Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_sarana_prasarana', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_sarana_prasarana' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' + }) + /*new OlLayerTile({ + name: "Point Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: {'LAYERS': appConfig.workspace_name+':', 'TILED': true}, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + }),*/ + ] +}); + +const layerGroupPasar = new OlLayerGroup({ + name: 'Sarana Prasarana', + layers: [ + new OlLayerTile({ + name: "Polygon Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_pasar_pertokoan', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_pasar_pertokoan' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + }), + type: 'layer', + geom_type: 'Polygon' + }), + new OlLayerTile({ + name: "Point Sarana Prasarana", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':point_tanah_pasar_pertokoan0', + 'TILED': true, + 'SLD': layerStyleUrl+'point_tanah_pasar_pertokoan0' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Point' + }), + ], + type: 'layerGroup' +}); + +const tanah_kantor_instansi_pemerintah = new OlLayerTile({ + name: "Polygon Tanah Kantor Instansi Pemerintah", + source: new OlSourceTileWMS({ + url: appConfig.geoserver_host+'wms', + params: { + 'LAYERS': appConfig.workspace_name+':tanah_kantor_instansi_pemerintah', + 'TILED': true, + 'SLD': layerStyleUrl+'tanah_kantor_instansi_pemerintah' + }, + serverType: 'geoserver', + transition: 0, + crossOrigin: 'anonymous', + // projection: projection + }), + type: 'layer', + geom_type: 'Polygon' +}); + + +/*const map = new OlMap({ + view: new OlView({ + center: Indonesia, + zoom: zoom, + }), + layers: [osmLayer, layerGroupSekolah, layerGroupRS, layerGroupKantorDesa, layerGroupSarPras] +});*/ + + +/*const setRequestMapHeader = () => { + axios.interceptors.request.use(function (config) { + // Do something before request is sent + // config.headers["Authorization"] = `Bearer ${token}`; + config.headers["Authorization"] = "Basic " + btoa(appConfig.geoserver_username + ":" + appConfig.geoserver_password) + // config.headers["crossDomain"] = true; + return config; + }, function (error) { + // Do something with request error + return Promise.reject(error); + }); +}*/ + +// map.on('postcompose', map.updateSize); + +/*const overlay = new Overlay({ + element: document.getElementById('popup'), + autoPan: true, + autoPanAnimation: { + duration: 250 + } +});*/ + +// const popupClick = () => { +// alert('hellawwww'); +// console.log('hellaawww'); +// } + +// const popupC = ( +// +// ) + +/*class PopupC extends Component { + constructor(props) { + super(props); + // this.popupClick = this.popupClick.bind(this); + } + + popupClick = () => { + alert('hellawwww'); + console.log('hellaawww'); + } + + render() { + return ( + + ) + } +}*/ + + + +class SiopasMap extends Component { + constructor(props) { + super(props); + this.state = { + drawerLayerVisible: false, + drawerSearchVisible: false, + wmsLayers: [], + totalLayerHasFeature: 0, + countGetFeature: 0, + countNotGetFeature: 0, + popupDataTemp: [], + evtCoordinate: null, + visibleLS: false, + visibleLSProp: 'hide', + // mapOnClickKey: null + }; + + this.popupRef = React.createRef(); + + this.layers = [ + osmLayer, + // layerGroupSekolah, + // layerGroupRS, + // layerGroupKantorDesa, + // layerGroupSarPras, + tanah_kantor_instansi_pemerintah + ]; + + // this.overlay = new Overlay({ + // element: document.getElementById('popup'), + // // element: ReactDOM.findDOMNode().querySelector('#popup'), + // autoPan: true, + // autoPanAnimation: { + // duration: 250 + // } + // }); + + /*this.overlay = new Overlay({ + element: , + autoPan: true, + autoPanAnimation: { + duration: 250 + } + });*/ + + this.overlay = new Overlay({ + // element: ReactDOM.findDOMNode(this).querySelector('#popup'), + element: document.getElementById('popup'), + autoPan: true, + autoPanAnimation: { + duration: 250 + } + }); + + this.olmap = new OlMap({ + view: new OlView({ + center: Indonesia, + zoom: zoom, + }), + layers: this.layers, + overlays: [this.overlay] + }); + + this.changeBaseLayer = this.changeBaseLayer.bind(this); + // this.popupRef = React.createRef(); + // console.log('this.popupRef', this.popupRef); + } + + componentDidMount = () => { + this.getHomeView(); + setRequestMapHeader(); + + // this.overlay = new Overlay({ + // element: document.getElementById('popup'), + // // element: ReactDOM.findDOMNode().querySelector('#popup'), + // autoPan: true, + // autoPanAnimation: { + // duration: 250 + // } + // }); + + // // Popup showing the position the user clicked + // this.overlay = new Overlay({ + // element: ReactDOM.findDOMNode(PopupContainer).querySelector('#popup') + // }); + + this.olmap.on("singleclick", (evt) => { + this.mapOnClick(evt); + }); + + // this.setState({mapOnClickKey: mapSingleClick}); + } + + componentDidUpdate = (prevProps, prevState) => { + if (this.state.popupDataTemp.length > 0) { + this.showPopup(); + console.log('popupDataTemp > 0'); + } + else { + this.closePopup(); + console.log('popupDataTemp < 1'); + } + } + + // shouldComponentUpdate(nextProps, nextState) { + // let center = this.olmap.getView().getCenter(); + // let zoom = this.olmap.getView().getZoom(); + // if (center === nextState.center && zoom === nextState.zoom) return false; + // return true; + // } + + loading = () =>
Loading...
+ + changeBaseLayer(item) { + console.log('change baselayer', item); + console.log(this.olmap.getLayers()); + if (this.olmap.getLayers().values_.length == 0) { + // check if layers empty, so just insert base layer to position 0 + this.olmap.getLayers().insertAt(0, item); + } + // check if layer exist + else if (this.olmap.getLayers().values_.length > 0) { + // check the position 0, if base layer then replace to new + if (this.olmap.getLayers().array_[0].get('type') === 'base') { + this.olmap.getLayers().removeAt(0); + this.olmap.getLayers().insertAt(0, item); + } + // else just insert base layer at position 0 + else { + this.olmap.getLayers().insertAt(0, item); + } + } + } + + getHomeView = () => { + this.olmap.getView().fit(new transformExtent(Bali_bbox, 'EPSG:4326', this.olmap.getView().getProjection()), { size: this.olmap.getSize(), duration: 500 }); + } + + mapOnClick = (evt) => { + // console.log('map on click', evt); + evt.preventDefault(); + let isDrawing = false; + let removedFeature = []; + let isRemoving = false; + + let mapInteractions = []; + this.olmap.getInteractions().forEach((interaction) => { + // console.log('interaction', interaction); + if (interaction instanceof Draw) { + // console.log('drawing is active!!'); + isDrawing = true; + } + // if (interaction instanceof Select) { + // console.log('select interaction is active'); + // isRemoving = true; + // } + }); + + if (isDrawing) { + return; + } + + // if (isRemoving) { + // this.olmap.getLayers().forEach((layer, i) => { + // if (layer.get('type') === 'vector') { + // let features = layer.getSource().getFeatures(); + // features.forEach((feature) => { + // layer.getSource().removeFeature(feature); + // }); + // } + // }); + // } + + let viewResolution = this.olmap.getView().getResolution(); + let viewProjection = this.olmap.getView().getProjection(); + let url = ''; + let promises = []; + let featureGet = []; + + this.olmap.getLayers().forEach((layer, i) => { + if (layer.get('type') !== 'base') { + if (layer.get('type') !== 'vector') { + if (layer.get('type') == 'layerGroup') { + layer.getLayers().forEach((sublayer, i) => { + if (sublayer.getVisible()) { + url = sublayer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + if (url) { + promises.push(axios.get(url)); + } + } + }); + } + else { + if (layer.getVisible()) { + url = layer.getSource().getGetFeatureInfoUrl(evt.coordinate, viewResolution, viewProjection, {'INFO_FORMAT': 'application/json'}); + if (url) { + promises.push(axios.get(url)); + } + } + } + } + } + }); + + axios.all(promises).then((results) => { + results.forEach((response) => { + // console.log('response promises', response); + if (response.data !== undefined) { + if (response.data.features.length > 0) { + for(let i=0; i < response.data.features.length; i++) { + featureGet.push(response.data.features[i]); + } + } + } + }) + + // console.log('featureGet', featureGet); + this.setState({ + popupDataTemp: featureGet, + evtCoordinate: evt.coordinate + }); + }); + } + + initPopup = () => { + let popupCloser = document.getElementById("popup-closer"); + // let showImageFeature = document.getElementById("showImageFeature"); + popupCloser.addEventListener("click", () => this.closePopup()); + // showImageFeature.addEventListener("click", () => this.closePopup()); + } + + showPopup = () => { + console.log('----------------- showPopup'); + this.initPopup(); + this.fillPopupContent(); + this.overlay.setPosition(this.state.evtCoordinate); + } + + fillPopupContent = () => { + const { popupDataTemp } = this.state; + console.log('popupDataTemp', popupDataTemp); + + if (popupDataTemp.length === 1) { + this.popupOneFeature(popupDataTemp[0]); + } + else if (popupDataTemp.length > 1) { + this.popupMoreFeature(); + } + } + + // renderPopup = () => { + // ReactDOM.render(, document.getElementById('popup-content')); + // } + + oneFeatureContent = (selectedPopupData) => { + // const { popupDataTemp } = this.state; + // console.log(popupDataTemp); + const popupId = selectedPopupData.id; + const popupProperties = selectedPopupData.properties; + console.log('selectedPopupData', selectedPopupData); + + return ( +
+
+

{ popupId }

+
+
+ + + { this.renderOneRowFeature(popupProperties) } + +
+
+
+
+ { this.renderButtonFooter(selectedPopupData) } +
+
+
+ ) + + // document.getElementById('showImageFeature').addEventListener("click", () => this.showImageFeature(selectedPopupData)); + } + + renderOneRowFeature = (popupProperties) => { + const row = []; + for (let key in popupProperties) { + row.push({key} : {popupProperties[key]}) + } + return row; + } + + moreFeatureContent = () => { + let self = this; + const { popupDataTemp } = self.state; + return ( +
+
+

Choose one feature

+
+
+ + + {/* self.renderSelectedPopup()}>*/} + { popupDataTemp.map((item, index) => { + return ( this.renderSelectedPopup(item)}>) + }) } + +
Testtttt
{item.id}
+
+
+
+ +
+
+
+ ) + } + + /*moreFeatureContent() { + return ( +
+
+

Choose one feature

+
+
+ +
+
+
+ +
+
+
+ ); + } */ + + /*renderListFeature = () => { + const { popupDataTemp } = this.state; + console.log('renderListFeature popupDataTemp', popupDataTemp); + return ( + + { popupDataTemp.map((item, index) => { + return ( this.renderSelectedPopup(item)}>{item.id}) + }) } + + ) + }*/ + + initFooterButtonsFunc = () => { + let showImageFeature = document.getElementById("showImageFeature"); + showImageFeature.addEventListener("click", () => this.closePopup()); + } + + popupOneFeature = (selectedPopupData) => { + console.log('-----------popupOneFeature'); + // const { popupDataTemp } = this.state; + let popupContent = document.getElementById('popup-content'); + ReactDOM.render(this.oneFeatureContent(selectedPopupData), popupContent); + // this.initFooterButtonsFunc(); + } + + popupMoreFeature = () => { + // generate list of feature that clicked in map, user choose one, then go to popupOneFeature + let popupContent = document.getElementById('popup-content'); + + // ReactDOM.render(, popupContent); + ReactDOM.render(this.moreFeatureContent(), popupContent); + // popupContent.innerHTML = "There are more feature"; + } + + popupOnClick = () => { + alert('click PopupContainer'); + } + + /*renderSelectedPopup = (selectedPopupData) => { + console.log('rendereSelectedPopup selectedPopupData', selectedPopupData); + }*/ + + renderSelectedPopup = (item) => { + console.log('renderSelectedPopup selectedPopupData', item); + } + + renderButtonFooter = (selectedPopupData) => { + // popupCloser.addEventListener("click", () => this.closePopup()); + return( + + + + Show Images + + + + Edit Feature + + + + Delete Feature + + + ) + + // let showImageFeature = document.getElementById("showImageFeature"); + // showImageFeature.addEventListener("click", () => this.closePopup()); + } + + showImageFeature = (selectedPopupData) => { + console.log('show images', selectedPopupData); + } + + editFeature = (selectedPopupData) => { + console.log('edit feature', selectedPopupData); + } + + deleteFeature = (selectedPopupData) => { + console.log('delete feature', selectedPopupData); + } + + closePopup = () => { + console.log('close popup'); + this.overlay.setPosition(undefined); + } + + render() { + return ( +
+ + + + + + {/**/} + {/**/} +
+ ); + } +} + +export default SiopasMap; + diff --git a/src/views/Map_backup/Popup.css b/src/views/Map_backup/Popup.css new file mode 100644 index 0000000..3e49bc7 --- /dev/null +++ b/src/views/Map_backup/Popup.css @@ -0,0 +1,86 @@ +.ol-popup { + position: absolute; + background-color: white; + -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + padding: 15px; + border-radius: 10px; + border: 1px solid #cccccc; + bottom: 12px; + left: -50px; + min-width: 280px; +} +.ol-popup:after, .ol-popup:before { + top: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} +.ol-popup:after { + border-top-color: white; + border-width: 10px; + left: 48px; + margin-left: -10px; +} +.ol-popup:before { + border-top-color: #cccccc; + border-width: 11px; + left: 48px; + margin-left: -11px; +} +.ol-popup-closer { + text-decoration: none; + position: absolute; + top: 2px; + right: 8px; +} +.ol-popup-closer:after { + content: "✖"; +} +/*.popup-table { + height: 200px; + overflow-y: scroll; +}*/ +#popup-content { + max-height: 250px; + /*overflow: auto;*/ +} +/*.div_popoup_table { + height: 200px; + overflow: auto; +}*/ +.popup-body { + height: 200px; + overflow: auto; +} +/*.popup-table { + height: 200px; + overflow: auto; +}*/ +.popup-body>.popup-table>thead>tr>th, .popup-body>.popup-table>tbody>tr>th, .popup-body>.popup-table>tfoot>tr>th, .popup-body>.popup-table>thead>tr>td, .popup-body>.popup-table>tbody>tr>td, .popup-body>.popup-table>tfoot>tr>td, .popup-body>.popup-table>tr>td { + padding: 3px !important; + font-size: 11px; + text-align: left; + /*overflow-y: scroll;*/ +} +.popup-footer { + text-align: center !important; + /*padding: 0px;*/ + padding-top: 10px; + /*float: right;*/ + /*margin-right: 10px;*/ + /*z-index: 90;*/ + /*float: inherit !important;*/ + /*margin-bottom: 0px;*/ +} + +.popup-row-list-feature { + cursor: pointer; +} + +.image-slider { + height: 175px; +} \ No newline at end of file diff --git a/src/views/Map_backup/package.json b/src/views/Map_backup/package.json new file mode 100644 index 0000000..24eba08 --- /dev/null +++ b/src/views/Map_backup/package.json @@ -0,0 +1,6 @@ +{ + "name": "Map", + "version": "0.0.0", + "private": true, + "main": "./Map.js" +} diff --git a/src/views/Map_backup/react-geo.css b/src/views/Map_backup/react-geo.css new file mode 100644 index 0000000..30f8097 --- /dev/null +++ b/src/views/Map_backup/react-geo.css @@ -0,0 +1,360 @@ +/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */ +/* stylelint-disable no-duplicate-selectors */ +/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors */ +/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors */ +.react-geo-uploadbutton { + cursor: pointer; + display: inline-block; + position: relative; + overflow: hidden; +} +.react-geo-uploadbutton input[type="file"] { + cursor: pointer; + width: 100%; + position: absolute; + top: 0; + right: 0; + margin: 0; + padding: 0; + opacity: 0; +} +.react-geo-uploadbutton ::-webkit-file-upload-button { + cursor: pointer; +} +button.react-geo-simplebutton { + color: #fff; + background-color: #1890ff; + border-color: #1890ff; +} +button.react-geo-simplebutton:focus { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-simplebutton:hover { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-simplebutton:disabled { + background-color: #fff; + border-color: #e6e6e6; +} +button.react-geo-measurebutton { + color: #fff; + background-color: #1890ff; + border-color: #e6e6e6; +} +button.react-geo-measurebutton:focus { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-measurebutton:hover { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-measurebutton:disabled { + background-color: #fff; + border-color: #e6e6e6; +} +button.react-geo-measurebutton.btn-pressed { + background-color: #005cb1; + border-color: #e6e6e6; +} +.react-geo-measure-tooltip { + position: relative; + background: rgba(0, 0, 0, 0.5); + border-radius: 4px; + color: white; + padding: 4px 8px; + opacity: 0.7; + white-space: nowrap; +} +.react-geo-measure-tooltip-dynamic { + opacity: 1; + font-weight: bold; +} +.react-geo-measure-tooltip-static { + background-color: #ffcc33; + color: black; + border: 1px solid white; +} +.react-geo-measure-tooltip-dynamic:before, +.react-geo-measure-tooltip-static:before { + border-top: 6px solid rgba(0, 0, 0, 0.5); + border-right: 6px solid transparent; + border-left: 6px solid transparent; + content: ""; + position: absolute; + bottom: -6px; + margin-left: -7px; + left: 50%; +} +.react-geo-measure-tooltip-static:before { + border-top-color: #ffcc33; +} +button.react-geo-digitizebutton { + color: #fff; + background-color: #1890ff; + border-color: #1890ff; +} +button.react-geo-digitizebutton:focus { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-digitizebutton:hover { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-digitizebutton:disabled { + background-color: #fff; + border-color: #e6e6e6; +} +.vertical-toggle-group { + display: flex; + flex-direction: column; +} +.horizontal-toggle-group { + display: flex; + flex-direction: row; +} +button.react-geo-togglebutton { + color: #fff; + background-color: #1890ff; + border-color: #e6e6e6; +} +button.react-geo-togglebutton:focus { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-togglebutton:hover { + background-color: #4ba9ff; + border-color: #ffffff; +} +button.react-geo-togglebutton:disabled { + background-color: #fff; + border-color: #e6e6e6; +} +button.react-geo-togglebutton.btn-pressed { + background-color: #005cb1; + border-color: #e6e6e6; +} +.react-geo-circlemenuitem { + display: block; + position: absolute; + top: 50%; + left: 50%; + margin: -1.3em; + pointer-events: all; +} +.react-geo-circlemenu { + border: dashed 1px; + border-radius: 50%; + position: absolute; + pointer-events: none; + background: -moz-radial-gradient(center, ellipse cover, rgba(0, 0, 0, 0) 0%, rgba(200, 199, 58, 0.65) 100%); + background: -webkit-radial-gradient(center, ellipse cover, rgba(0, 0, 0, 0) 0%, rgba(200, 199, 58, 0.65) 100%); + background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 0%, rgba(200, 199, 58, 0.65) 100%); +} +.add-wms-layer-checkbox-line { + padding: 5px; + margin: 1px; + display: flex; + background: #ffffff; + flex: 1; + align-items: center; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} +.add-wms-layer-checkbox-line .add-wms-add-info-icon { + margin-left: 5px; +} +.add-wms-panel .body { + display: flex; + flex-direction: column; +} +.add-wms-panel .body .ant-checkbox-group { + flex: 1; + height: 100%; + overflow-y: auto; +} +.add-wms-panel .body .add-wms-layer-checkbox-line { + padding: 5px; + margin: 1px; + display: flex; + background: #ffffff; + flex: 1; + align-items: center; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} +.add-wms-panel .body .add-wms-layer-checkbox-line .add-wms-add-info-icon { + margin-left: 5px; +} +.add-wms-panel .body .react-geo-titlebar .react-geo-simplebutton { + margin-right: 5px; +} +.react-geo-feature-grid .react-geo-feature-grid-row.row-hover > td { + background: #e6f7ff; +} +.react-geo-propertygrid { + background-color: white; + display: flex; + flex-direction: column; + height: 100%; +} +.react-geo-propertygrid .ant-spin-nested-loading { + height: 100%; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container { + height: 100%; + display: flex; + flex-direction: column; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table { + flex: 1; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-fixed-header .ant-table-header { + margin-bottom: inherit !important; + padding-bottom: inherit !important; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content { + height: 100%; + display: flex; + flex-direction: column; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content .ant-table-header { + overflow: inherit; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content .ant-table-body { + overflow: auto; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content th { + text-align: center; +} +.react-geo-propertygrid .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-content tr:nth-child(odd) { + background-color: rgba(24, 144, 255, 0.05); +} +.react-geo-propertygrid .ant-pagination { + width: 100%; + text-align: center; +} +.react-geo-layertree-node { + transition: opacity 1s; +} +.react-geo-layertree-node.out-off-range { + opacity: 0.5; +} +.react-geo-floatingmaplogo { + position: absolute; + left: 5px; + bottom: 5px; + background-color: rgba(255, 255, 255, 0.5); + border-radius: 5px; + z-index: 10; +} +.react-geo-titlebar { + display: flex; + align-items: center; + overflow: auto; + background-color: #e6e6e6; + color: #1890ff; + font-weight: bold; +} +.react-geo-titlebar .title { + margin: 5px; + flex: 1; +} +.react-geo-titlebar .controls { + margin: 5px; + display: flex; +} +.react-geo-titlebar .controls > button { + margin-right: 2px; +} +.react-geo-panel { + z-index: 100; +} +.react-geo-panel .body { + background-color: #fff; +} +.react-geo-panel .resize-handle:hover.resize-handle-right { + border-right: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-left { + border-left: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-top { + border-top: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-bottom { + border-top: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-bottom-right { + border-bottom: 5px solid rgba(24, 144, 255, 0.5); + border-right: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-bottom-left { + border-bottom: 5px solid rgba(24, 144, 255, 0.5); + border-left: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-top-right { + border-top: 5px solid rgba(24, 144, 255, 0.5); + border-right: 5px solid rgba(24, 144, 255, 0.5); +} +.react-geo-panel .resize-handle:hover.resize-handle-top-left { + border-top: 5px solid rgba(24, 144, 255, 0.5); + border-left: 5px solid rgba(24, 144, 255, 0.5); +} +.vertical-toolbar { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} +.vertical-toolbar button { + margin-top: 5px; +} +.horizontal-toolbar { + line-height: 30px; + pointer-events: all; + transition: all .3s; +} +.horizontal-toolbar button { + margin-right: 5px; + top: 5px; +} +.react-geo-userchip { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; +} +.react-geo-userchip .username { + margin-left: 5px; +} +.react-geo-window-portal { + z-index: 1000; + position: inherit !important; + -moz-box-shadow: 2px 2px 10px -2px #cccccc, -2px 2px 10px -2px #cccccc; + -webkit-box-shadow: 2px 2px 10px -2px #cccccc, -2px 2px 10px -2px #cccccc; + box-shadow: 2px 2px 10px -2px #cccccc, -2px 2px 10px -2px #cccccc; +} +.react-geo-window-portal .react-geo-simplebutton { + background-color: #fff; + color: #1890ff; +} +.react-geo-window-portal .react-geo-simplebutton:hover { + background-color: #4ba9ff; + color: #ffffff; +} +.react-geo-window-portal .ant-modal-header { + padding: 1px; + border-bottom: 2px dotted rgba(24, 144, 255, 0.25); + background-color: #ffffff; +} +.react-geo-window-portal .ant-modal-header .title { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} diff --git a/src/views/Master/ConfigAlert/DialogForm.js b/src/views/Master/ConfigAlert/DialogForm.js new file mode 100644 index 0000000..dfbd43b --- /dev/null +++ b/src/views/Master/ConfigAlert/DialogForm.js @@ -0,0 +1,99 @@ +import React, { useEffect, useState } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input, Col, Row } from 'reactstrap'; +import { Select } from 'antd'; +import moment from 'moment'; +import 'antd/dist/antd.css'; + +const { Option } = Select + +const DialogForm = ({openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, dataMenu}) => { + const [id, setId] = useState(0) + const [name, setName] = useState('') + const [descripton, setDescription] = useState('') + const [status, setStatus] = useState('') + + + useEffect(()=> { + if(typeDialog==="Edit"){ + console.log("data edit", dataEdit) + setId(dataEdit.id) + setName(dataEdit.nama) + setDescription(dataEdit.keterangan) + setStatus(dataEdit.status) + }else{ + setId(0) + setName('') + setDescription('') + setStatus('') + } + },[dataEdit,openDialog]) + + const handleSave = () => { + let data = ''; + if(typeDialog==="Save"){ + data = { + nama:name, + keterangan:descripton, + status + } + + closeDialog('save', data); + }else{ + data = { + id, + nama:name, + keterangan:descripton, + status + } + + + closeDialog('edit', data); + } + } + + const handleCancel = () => { + closeDialog('cancel', 'none') + setId(0) + setName('') + setDescription('') + setStatus('') + } + + + const renderForm = () => { + return( +
+ + + setName(e.target.value)} placeholder={`Waspang tidak sesuai target..`}/> + + + + setDescription(e.target.value)} placeholder={`Rencana vs actual tidak sesuai..`} /> + + + + setStatus(e.target.value)} placeholder={`Warning..`} /> + +
+ ) + } + + + return ( + + {typeDialog=="Save" ? `Tambah` : "Edit"} Config Alert + + {renderForm()} + + + {' '} + + + + ) + +} + +export default DialogForm; \ No newline at end of file diff --git a/src/views/Master/ConfigAlert/index.js b/src/views/Master/ConfigAlert/index.js new file mode 100644 index 0000000..df6337c --- /dev/null +++ b/src/views/Master/ConfigAlert/index.js @@ -0,0 +1,195 @@ +import React, { useState, useEffect } from 'react'; +import { Card, CardBody, CardHeader, Input } from 'reactstrap'; +import axios from 'axios'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Card as ACard,Row, Col, Switch } from 'antd'; +// import moment from 'moment'; +import { CALERTUSER_SEARCH,CONFIGALERT_SEARCH,CALERTUSER_DELETE, CALERTUSER_ADD } from '../../../const/ApiConst.js'; +import { Icon } from '@iconify/react'; +import Bell from '@iconify/icons-ion/notifications-outline'; +import BellOff from '@iconify/icons-ion/notifications-off-outline'; + +const token = window.localStorage.getItem('token'); +const userID = window.localStorage.getItem('user_id'); + +const Index = ({params}) => { + const [dataTable, setDatatable] = useState([]) + const [alertUser, setAlertUser] = useState([]) + const [checked, setChecked] = useState({}) + const [onSetSwitch, setOnSetSwitch] = useState(false) + const pageName = params.name; + + const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } + }; + + + useEffect(()=> { + getDataConfigAlert(); + // getDataConfigAlertUser(); + },[]) + + useEffect(() => { + getDataConfigAlertUser(); + },[dataTable]) + + + + const getDataConfigAlert = async () => { + const payload = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name": "nama", "logic_operator": "like", "value": "", "operator": "AND"} + ], + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + + + + const result = await axios + .post(CONFIGALERT_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + let resData = result.data.data + let checkedAlert = {} + resData.map((val, index) => { + checkedAlert[val.id] = false + }); + setChecked(checkedAlert); + setDatatable(resData); + // getDataConfigAlertUser(); + }else{ + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + const getDataConfigAlertUser = async () => { + const payload = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name": "user_id", "logic_operator": "=", "value": localStorage.getItem('user_id'), "operator": "AND"} + ], + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + + const result = await axios + .post(CALERTUSER_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + + + if(result && result.data && result.data.code == 200){ + let resData = result.data.data + // console.log("resData", resData) + // console.log("dataTable", dataTable) + let configAlert = []; + resData.map((val, index) => { + configAlert.push(val.config_alert_id); + let indexRes = dataTable.findIndex(x => x.id === val.config_alert_id); + // console.log("indexRes", indexRes) + if(indexRes >= 0){ + let id = dataTable[indexRes].id; + let tempChecked = checked; + tempChecked[id] = true; + // console.log("tempChecked", tempChecked) + setChecked(tempChecked); + } + }); + window.localStorage.setItem('userConfigAlert', configAlert.join()); + console.log("configAlert.join()", configAlert.join()) + setOnSetSwitch(false) + setAlertUser(result.data.data); + }else{ + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + const onChange = (checked, id) => { + setOnSetSwitch(true) + // console.log(`switch to ${checked} ${id}`); + if(checked){ + addConfigAlertUser(id); + }else{ + setDeleteConfigAlertUser(id) + } + } + + const addConfigAlertUser = async (id) => { + const formData = { + "user_id":parseInt(localStorage.getItem('user_id')), + "config_alert_id":id + } + + const result = await axios.post(CALERTUSER_ADD, formData, config) + .then(res => res) + .catch((error) => error.response); + + if(result && result.data && result.data.code===200){ + getDataConfigAlertUser(); + // NotificationManager.success(`Data menu berhasil ditambah`, 'Success!!'); + } + } + + const setDeleteConfigAlertUser = async (id) => { + let indexCek = alertUser.findIndex(x => x.user_id === parseInt(localStorage.getItem('user_id')) && x.config_alert_id === id); + // console.log("cekcekckecke", indexCek) + if(indexCek >= 0){ + let idAlert = alertUser[indexCek].id + // console.log("cek idAlert", idAlert); + deleteConfigAlertUser(idAlert) + } + } + + const deleteConfigAlertUser = async (id) => { + const url = CALERTUSER_DELETE(id) + + const result = await axios.delete(url, config) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + getDataConfigAlert(); + } + } + + + return ( +
+ + + +

{pageName}

+
+ + + {dataTable.map((val, index) => ( + + onChange(checked, val.id)} disabled={onSetSwitch} />, + ]}> +
+ +

{val.nama}

+
+
+ + ))} +
+
+
+
+ ) +} + +export default Index; \ No newline at end of file diff --git a/src/views/Master/MasterAbsensi/DialogForm.js b/src/views/Master/MasterAbsensi/DialogForm.js new file mode 100644 index 0000000..951f805 --- /dev/null +++ b/src/views/Master/MasterAbsensi/DialogForm.js @@ -0,0 +1,207 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Select from 'react-select' +import axios from 'axios'; +import moment from 'moment'; +import { DatePicker } from 'antd'; +import 'antd/dist/antd.css'; +import { USER_SEARCH } from '../../../const/ApiConst.js'; + +const token = window.localStorage.getItem('token'); +const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } +}; + +const format = 'HH:mm'; +const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; +let countError = 0; + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + idEmployee:0, + description:"", + dateAbsent: moment(moment().format("YYYY-MM-DD")), + openDialog: false, + isParentClick: false, + currentSelectVal: null, + dataEmployee:[], + listEmployeeSelect:[], + selectDisable: false, + } + } + + async componentDidMount(){ + this.getDataEmployee(); + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + if(this.props.typeDialog==="Edit"){ + const { dataEdit } = this.props + if(dataEdit.user_id!=="" && dataEdit.user_id!==null){ + this.searchEmployee(dataEdit.user_id); + } + + this.setState({ + id:dataEdit.id, + idEmployee:dataEdit.user_id, + description:dataEdit.description || "", + dateAbsent:dataEdit.created_date ? moment(moment(dataEdit.created_date).format("YYYY-MM-DD")) : moment(moment().format("YYYY-MM-DD")), + selectDisable: true, + }) + }else{ + this.setState({ + id:0, + idEmployee:0, + description:"", + dateAbsent:moment(moment().format("YYYY-MM-DD")), + selectDisable: false, + currentSelectVal:null + }) + } + this.setState({isParentClick:false}); + } + } + + getDataEmployee = async () => { + const formData = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name": "name", "logic_operator": "like", "value": "", "operator": "AND"} + ], + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + const result = await axios + .post(USER_SEARCH, formData, config) + .then(res => res) + .catch((error) => error.response ); + // console.log('cek', result) + if(result && result.data && result.data.code==200 ){ + this.setState({ dataEmployee:result.data.data },()=>{ + this.setDataEmployee(); + }) + }else{ + + } + } + + setDataEmployee = () => { + const { dataEmployee } = this.state + const listEmployee = [] + dataEmployee.map((val, index)=> { + listEmployee.push({ + value:val.id, + label:val.name + }) + }) + this.setState({ listEmployeeSelect:listEmployee }) + } + + searchEmployee = (id) => { + let getIndex = this.getIndexDataEmployee(id); + let data = this.state.dataEmployee[getIndex] + this.setState({ idEmployee:data.id,currentSelectVal:{value:data.id,label:data.name} }) + } + + getIndexDataEmployee = (val) => { + let index = this.state.dataEmployee.findIndex(obj => obj.id === val); + return index + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + const { + id, + idEmployee, + description, + dateAbsent, + } = this.state + + let data = ''; + if(this.props.typeDialog==="Save"){ + data = { + idEmployee, + description, + dateAbsent, + } + this.props.closeDialog('save', data); + }else{ + data = { + id, + idEmployee, + description, + dateAbsent, + } + this.props.closeDialog('edit', data); + } + + this.setState({ id:0,idEmployee:0,currentSelectVal:null }); + + } + + handleCancel = () => { + this.setState({ id:0,currentSelectVal:null }); + this.props.closeDialog('cancel', 'none') + } + + handleSelectGs = (inputValue, actionMeta) => { + this.setState({ idEmployee:inputValue.value,currentSelectVal:{ value:inputValue.value,label:inputValue.label } }) + } + + handleChangeDate = (date, dateString) => { + this.setState({ dateAbsent:date }) + } + + renderForm = () => { + return( +
+ + + + + this.setState({description:e.target.value})} placeholder="deskripsi...." /> + + + + + +
+ ) + } + + render() { + return ( + + {this.props.typeDialog=="Save" ? "Tambah" : "Edit"} Absensi Karyawan + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterAbsensi/index.js b/src/views/Master/MasterAbsensi/index.js new file mode 100644 index 0000000..c45d71b --- /dev/null +++ b/src/views/Master/MasterAbsensi/index.js @@ -0,0 +1,608 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input, InputGroup } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination, Tooltip } from 'antd'; +import { DatePicker,Select } from 'antd'; +import * as XLSX from 'xlsx'; +import { ABSENSI_SEARCH, ABSENSI_ADD, ABSENSI_DELETE, ABSENSI_EDIT,PROYEK_SEARCH, USERPROYEK_SEARCH } from '../../../const/ApiConst.js'; +import MapConfig from '../../MapConfig/MapConfig'; +const { RangePicker } = DatePicker; +const { Option } = Select +const token = window.localStorage.getItem('token'); +const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } +}; + + +const BASE_URL = ""; + + +const column = [ + { name: "Nama Human Resource" }, + { name: "Deskripsi"}, + { name: "Tanggal Absensi"}, +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + dataExport: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentDay: 'today', + tooltipTambah:false, + tooltipExport:false, + currentProyek:parseInt(localStorage.getItem('role_id'))===2 ? parseInt(localStorage.getItem('proyek_id')) : null, + allUserToProyek:[], + allDataProyek:[], + finishSetupOption:false + } + } + + async componentDidMount() { + this.getDataAbsensi(); + this.getAllProyek(); + this.setUpFirstProyek(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search,startDate, dataExport,currentProyek,allUserToProyek } = this.state + if (search !== prevState.search) this.getDataAbsensi() + if (startDate !== prevState.startDate) this.getDataAbsensi() + if (dataExport !== prevState.dataExport){ + if(dataExport.length > 0){ + this.exportExcel() + } + } + if(currentProyek!== prevState.currentProyek){ + if(localStorage.getItem('role_id')!==2){ + this.getUserProyek(); + } + } + if(allUserToProyek!== prevState.allUserToProyek){ + this.getDataAbsensi(); + } + } + + setUpFirstProyek = () => { + // console.log("cek role id", localStorage.getItem('role_id')); + if(parseInt(localStorage.getItem('role_id'))===2){ + // console.log("cek proyek 2", parseInt(proyekId)); + // this.setState({currentProyek:parseInt(proyekId)},() => { + this.setState({disableProyek:true}); + // }) + }else{ + // console.log("cek setUp current proyek") + this.setState({currentProyek:null}) + } + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getAllProyek = async () => { + const payload = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name": "nama", "logic_operator": "like", "value": "", "operator": "AND"} + ], + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + + const result = await axios + .post(PROYEK_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + // console.log("cek all data proyek", result.data.data) + this.setState({allDataProyek:result.data.data}) + // this.setUpFirstProyek(); + }else{ + // NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + + getUserProyek = async () => { + const payload = { + "paging": {"start": 0, "length": -1}, + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + + if(parseInt(localStorage.getItem('role_id'))===1){ + if(this.state.currentProyek && this.state.currentProyek > 0){ + payload['columns'] = [ + {"name": "proyek_id", "logic_operator": "=", "value": `${this.state.currentProyek}`, "operator": "AND"} + ] + } + }else{ + payload['columns'] = [ + {"name": "proyek_id", "logic_operator": "=", "value": `${localStorage.getItem('proyek_id')}`, "operator": "AND"} + ] + } + + // console.log("cek payload", payload) + + const result = await axios + .post(USERPROYEK_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + // console.log("cek result", result.data.data) + this.setState({allUserToProyek:result.data.data}) + }else{ + // NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + getDataAbsensi = async () => { + let url = ""; + + let start = 0; + if (this.state.currentPage !== 1 && this.state.currentPage > 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + let dateStart = moment(this.state.startDate).format("YYYY-MM-DD 00:00:00"); + let dateEnd = moment(this.state.endDate).format("YYYY-MM-DD 23:59:59"); + + const payload = { + "paging":{ + "start":start, + "length":this.state.rowsPerPage + }, + "filter_columns":[ + { + "name":"name", + "value":"", + "table_name":"m_users" + } + ], + "columns":[ + {"name": "created_at", "logic_operator": "range", "value": dateStart, "value1": dateEnd, "operator": "AND"}, + {"name": "name", "logic_operator": "like", "value": this.state.search, "operator": "AND", "table_name":"m_users"} + ], + "joins":[ + { + "name":"m_users", + "column_join":"user_id", + "column_results":[ + "name", + ] + } + ], + "orders":{ + "columns":[ + "id" + ], + "ascending":false + } + } + + + + const result = await axios + .post(ABSENSI_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + console.log("cek res", result.data.data) + let totalRecord = result.data.totalRecord + let resData = result.data.data || [] + + this.setState({ dataTable: resData, totalPage: totalRecord }); + }else{ + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = (type, data) => { + if (type === "save") { + this.saveAbsensi(data); + } else if (type === "edit") { + this.editDataAbsensi(data); + } + + this.setState({ openDialog: false }) + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmDelete = async () => { + const { idDelete } = this.state + let url = ABSENSI_DELETE(idDelete) + const result = await axios.delete(url, MapConfig) + .then(res => res) + .catch((error) => error.response); + + if(result && result.data && result.data.code==200){ + this.getDataAbsensi() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success('Data absensi berhasil dihapus!!', 'Success!!'); + }else{ + this.getDataAbsensi() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error('Data absensi gagal dihapus!!', 'Failed!!'); + } + } + + saveAbsensi = async (data) => { + + const formData = { + user_id:data.idEmployee, + description:data.description, + created_at:data.dateAbsent + } + const result = await axios.post(ABSENSI_ADD, formData, config) + .then(res => res) + .catch((error) => error.response); + + if(result && result.data && result.data.code==200){ + this.getDataAbsensi(); + NotificationManager.success('Data absensi berhasil ditambahkan!!', 'Success!!'); + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + } + + editDataAbsensi = async (data) => { + let url = ABSENSI_EDIT(data.id) + + const formData = { + user_id:data.idEmployee, + description:data.description, + created_at:data.dateAbsent + } + + const result = await axios.put(url, formData, config) + .then(res => res) + .catch((error) => error.response); + if(result && result.data && result.data.code==200){ + this.getDataAbsensi(); + NotificationManager.success('Data absensi berhasil diedit!!', 'Success!!'); + }else{ + NotificationManager.error('Data absensi gagal diedit!!', 'Failed!!'); + } + + } + + + handleEdit = (data) => { + this.setState({ dataEdit: data }); + this.handleOpenDialog('Edit'); + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataAbsensi(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataAbsensi(); + }) + } + + toggle = (param) => { + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "tambah") { + this.setState(prevState => ({ tooltipTambah: !prevState.tooltipTambah })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + } + + handleDatePicker = (date, dateString) => { + this.setState({ startDate:date[0],endDate:date[1] },()=>{ + this.getDataAbsensi(); + }) + } + + handleTipe = (e) => { + this.setState({ typeClock:e.target.value }, () => { + this.getDataAbsensi(); + }); + } + + handleExportExcel = async () => { + let dateStart = moment(this.state.startDate).format("YYYY-MM-DD 00:00:00"); + let dateEnd = moment(this.state.endDate).format("YYYY-MM-DD 23:59:59"); + const payload = { + "paging":{ + "start":0, + "length":-1 + }, + "filter_columns":[ + { + "name":"name", + "value":"", + "table_name":"m_users" + } + ], + "columns":[ + {"name": "created_at", "logic_operator": "range", "value": dateStart, "value1": dateEnd, "operator": "AND"}, + {"name": "name", "logic_operator": "like", "value": this.state.search, "operator": "AND", "table_name":"m_users"} + ], + "joins":[ + { + "name":"m_users", + "column_join":"user_id", + "column_results":[ + "name", + ] + } + ], + "orders":{ + "columns":[ + "id" + ], + "ascending":false + } + } + + + + const result = await axios + .post(ABSENSI_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + let resData = result.data.data || [] + const excelData = []; + + resData.map((n, index) => { + let dataRow = { + "Nama Human Resource":n.join_first_name ? n.join_first_name : "", + "Deskripsi":n.description ? n.description : "", + "Tanggal Absensi" :n.created_at!==null ? moment(n.created_at).format("DD-MM-YYYY") : "-", + } + excelData.push(dataRow) + }) + // console.log("cek excel data", excelData) + this.setState({dataExport:excelData}) + // exportExcel(); + }else{ + NotificationManager.error('Gagal Export Data!!', 'Failed'); + } + } + + exportExcel = () => { + const dataExcel = this.state.dataExport || []; + const fileName = `Data Absensi.xlsx`; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, `Data Absensi`); + + XLSX.writeFile(wb, fileName); + this.setState({dataExport:[] }) + } + + setupSelectProyek = () => { + let allDataProyek = this.state.allDataProyek + return( + <> + {allDataProyek.map((val, index)=> { + return( + + ) + }) + } + + ) + } + + onChangeProyek = (val) => { + this.setState({currentProyek:val}); + } + + renderTable = () => { + const dataTable2 = this.state.dataTable || []; + // const dataTable2 = []; + return ( + + {dataTable2.length!==0 ? dataTable2.map((n) => { + return ( + + {/* + this.handleDelete(n.id)}> + this.toggle("delete")}> + Delete + + + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + */} + {n.join_first_name ? n.join_first_name : ""} + { n.description ? n.description : "-" } + { n.created_at ? moment(n.created_at).format("YYYY-MM-DD") : "-" } + + ) + }) : + No Data Available + + } + + ) + } + + handleChangeDay = (e) => { + const val = e.target.value; + this.setState({ currentDay:val }); + if(val==="today"){ + console.log("test 1 test",val); + this.setState({ + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else if(val==="3 day"){ + console.log("test test",val); + this.setState({ + startDate:moment(moment().subtract(3, "days").format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else if(val==="7 day"){ + console.log("test test",val); + this.setState({ + startDate:moment(moment().subtract(7, "days").format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else{ + console.log("test 2 test",val); + this.setState({ + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + } + } + + render() { + const { tooltipTambah,tooltipExport,dataTable, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete } = this.state + return ( +
+ + this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data Absensi karyawan akan terhapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + + +

{this.props.params.name}

+
+ {/* */} + + + + {/* */} + {/* this.toggle("tambah")}> + Tambah Absensi + */} +
+
+ +
+
+
+ this.handleChangeDay(e)} defaultValue={this.state.currentDay}> + + + + +
+
+ {' '} + +
+
+ +
+ + + + {/* */} + {column.map((i, index) => { + return ( + + ) + })} + + + { this.renderTable() } +
Actions{i.name}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterBroadcast/DialogDetail.js b/src/views/Master/MasterBroadcast/DialogDetail.js new file mode 100644 index 0000000..8d71ffd --- /dev/null +++ b/src/views/Master/MasterBroadcast/DialogDetail.js @@ -0,0 +1,117 @@ +import 'antd/dist/antd.css'; +import React, { Component } from 'react'; +import moment from 'moment'; +import { Button, Table, FormFeedback, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import Select from 'react-select'; +import axios from 'axios'; +import { BASE_URL_GEOHR_API } from '../../../const/ApiConst'; +import { Transfer } from 'antd'; + +const ERROR_TITLE = "judul is required!" +const ERROR_MESSAGE = "message is required!" +const BASE_URL = "https://oslog.id/geohr-api/"; +let countError = 0; +export default class DialogDetail extends Component { + constructor(props) { + super(props) + this.state = { + openDialog: false, + isParentClick: false, + dataListDetail:[], + id:0, + } + } + + async componentDidMount() { + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate() { + if (this.state.isParentClick === true) { + const { dataDetail } = this.props + console.log("cek data detail", dataDetail) + this.setState({ + id:dataDetail.id + },() => { + this.getDataDetail(); + this.setState({ isParentClick: false }); + }); + } + } + + getDataDetail = async () => { + countError++; + let url = BASE_URL_GEOHR_API+`/broadcast-detail/search?broadcastId=${this.state.id}`; + const payload = { + "paging": {"start": 0, "length": 25}, + "orders": {"columns": ["id"], "ascending": true}, + "columns": [ + {"name": "status_send", "logic_operator": "like", "value":"", "operator": "AND"} + ], + "joins": [ + {"name": "m_broadcast", "column_results": ["title_notif","message_notif","description","status_send"], "column_join":"broadcast_id"} + ] + } + const result = await axios + .post(url,payload) + .then(res => res) + .catch((error) => error.response ); + console.log('cek data detail', result.data) + + if(result && result.data && result.data.code === 200){ + if (result.data.data && result.data.data.broadcast_details) { + this.setState({ dataListDetail:result.data.data.broadcast_details }) + } + }else{ + if(countError<6){ + this.getDataDetail(); + } + } + } + + showDialog = () => { + this.setState({ isParentClick: true }); + } + + handleCloseDialog = () => { + this.props.closeDialog() + } + + render() { + return ( + + Detail Broadcast + + + + + + + + + + + + + {this.state.dataListDetail.map((val, index) => { + return( + + + + + + + + ) + })} + +
Status SendTanggal KirimDeskripsiJudul NotifikasiPesan Notifikasi
{val.join.status_send==="" ? "-" :val.status_send}{val.join.created_date==="" ? "-" : moment(val.created_date).format("YYYY-MM-DD HH:mm:ss")}{val.join.broadcast_description==="" ? "-" : val.join.broadcast_description}{val.join.broadcast_title_notif==="" ? "-" : val.join.broadcast_title_notif}{val.join.broadcast_message_notif==="" ? "-" : val.join.broadcast_message_notif}
+ +
+ + + +
+ ) + } +} diff --git a/src/views/Master/MasterBroadcast/DialogForm.js b/src/views/Master/MasterBroadcast/DialogForm.js new file mode 100644 index 0000000..f5a192b --- /dev/null +++ b/src/views/Master/MasterBroadcast/DialogForm.js @@ -0,0 +1,434 @@ +import 'antd/dist/antd.css'; +import React, { Component } from 'react'; +import { Button, Form, FormFeedback, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import Select from 'react-select'; +import axios from 'axios'; +import { BASE_URL_GEOHR_API2, ROLE_SEARCH, USER_WASPANG } from '../../../const/ApiConst'; +import { Transfer } from 'antd'; + + +const token = window.localStorage.getItem('token'); + +const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } +}; + + +const ERROR_TITLE = "judul is required!" +const ERROR_MESSAGE = "message is required!" +const BASE_URL = "https://oslog.id/geohr-api/"; +const roleName = window.localStorage.getItem('role_name'); +let countError = 0; +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + title: "", + description: "", + message: "", + openDialog: false, + isParentClick: false, + errorTitle: "", + errorMessage: "", + errorDivision: "", + errorEmployee: "", + penerima:"all", + displayKaryawan: "none", + displayDivisi: "none", + listOrganizationSelect: [], + currentSelectDiv: null, + dataEmployee: [], + dataSourceEmployee: [], + idOrganization: 0, + targetKeys:[], + selectedKeys: [], + allEmployeeId:[], + idEmployeeDivision: [], + idOrganization:[], + disableTransfer:true + } + } + + async componentDidMount() { + this.getDataUsers(); + this.getDataRole(); + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate() { + if (this.state.isParentClick === true) { + if (this.props.typeDialog === "Edit") { + const { dataEdit } = this.props + this.setState({ + id: dataEdit.id, + title: dataEdit.title_notif, + description: dataEdit.description, + message: dataEdit.message_notif + }) + } else { + this.setState({ + id: 0, + title: "", + description: "", + message: "" + }) + } + this.setState({ isParentClick: false }); + } + } + + + showDialog = () => { + this.setState({ isParentClick: true }); + } + + + + validate = () => { + let isError = false + const { title, message, penerima } = this.state + console.log(`www`, title.length < 3 || title.length > 100) + if (title === "") { + isError = true + this.setState({ errorTitle: ERROR_TITLE }) + } else if (title.length < 3 || title.length > 100) { + isError = true + this.setState({ errorTitle: "Title minimum 3-100 karakter!" }) + } + + if (message === "") { + isError = true + this.setState({ errorMessage: ERROR_MESSAGE }) + } else if (message.length < 3 || message.length > 200) { + isError = true + this.setState({ errorMessage: "message minimum 3-200 karakter!" }) + } + + if(penerima=== "division"){ + if(this.state.idOrganization===0){ + this.setState({ errorDivision: "Silahkan pilih divisi penerima!" }) + } + }else if(penerima==="karyawan"){ + if(this.state.selectedKeys.length===0){ + this.setState({ errorEmployee: "Silahkan pilih karyawan penerima!" }) + } + } + return isError + } + handleSave = (param) => { + let err = this.validate() + if (!err) { + const { + id, + title, + description, + message, + penerima + } = this.state + + let idSend = []; + let send_to_type = "all"; + if(penerima==="all"){ + idSend = this.state.allEmployeeId; + send_to_type = "all"; + }else if(penerima==="division"){ + idSend = this.state.idOrganization; + send_to_type = "roles"; + }else if(penerima==="karyawan"){ + idSend = this.state.targetKeys; + send_to_type = "users"; + } + + idSend = idSend.map(function(e){ + return e.toString() + }); + + const data = { + title, + description, + message, + send_to_type, + id:idSend + } + + if (param === 'registered' || param === 'send') { + this.props.handleSaveBroadcast(param, data) + } + this.setState({ id: 0,idOrganization:0,currentSelectDiv:null,targetKeys:[],penerima:"all",displayKaryawan:"none",displayDivisi:"none",disableTransfer:true }); + } + } + + handleCancel = () => { + this.props.handleSaveBroadcast('cancel', 'none') + } + + handleChangePenerima = type => { + if(type==="organization"){ + this.setState({ displayDivisi:"block",displayKaryawan:"none",disableTransfer:true,errorDivision:"",targetKeys:[] }) + }else if(type==="karyawan"){ + this.setState({ displayKaryawan:"block",displayDivisi:"none",disableTransfer:false,errorEmployee:"",idEmployeeDivision:[],currentSelectDiv:null }) + }else{ + this.setState({ displayKaryawan:"none",displayDivisi:"none",disableTransfer:true,errorDivision:"",errorEmployee:"",targetKeys:[],idEmployeeDivision:[],currentSelectDiv:null }) + } + this.setState({ penerima:type }); + } + + handleSelectOrganization = (inputValue, actionMeta) => { + this.setState({ idOrganization:inputValue.value,currentSelectDiv:{ value:inputValue.value,label:inputValue.label },errorDivision:"" }, () => { + this.setEmployeeOrganization(); + }); + } + + setEmployeeOrganization = () => { + let arrEd = this.state.dataEmployee; + let cek = arrEd.filter(this.filterId) + + // console.log("cekk", cek); + this.setState({ idEmployeeDivision:cek }) + } + + filterId= (val) => { + return val.organization_id == this.state.idOrganization + } + + getDataRole = async () => { + const payload = { + + "orders": {"columns": ["id"], "ascending": false} + } + + const result = await axios + .post(ROLE_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + console.log('test role', result) + + if(result && result.data && result.data.code === 200){ + this.setState({ dataDivision:result.data.data },()=>{ + this.setDataOrganization(); + }) + }else{ + // if(countError<6){ + // this.getDataOrganization(); + // } + } + } + + setDataOrganization = () => { + const { dataDivision } = this.state + const listOrganization = [] + dataDivision.map((val, index)=> { + listOrganization.push({ + value:val.id, + label:val.name + }) + }) + this.setState({ listOrganizationSelect:listOrganization }) + } + + getDataUsers = async () => { + + + const payload = { + "paging":{ + "start":0, + "length":10 + }, + "filter_columns":[ + { + "name":"name", + "value":"", + "table_name":"m_users" + } + ], + "columns":[ + { + "name":"name", + "logic_operator":"like", + "value":"", + "operator":"AND", + "table_name":"m_users" + }, + { + "name":"id", + "logic_operator":"=", + "value":"3", + "operator":"AND", + "table_name":"m_roles" + } + ], + "joins":[ + { + "name":"m_users", + "column_join":"user_id", + "column_results":[ + "username", + "name", + "email", + "gender", + "phone_number", + "address", + "birth_place", + "birth_date" + ] + }, + { + "name":"m_roles", + "column_join":"role_id", + "column_results":[ + "name" + ] + } + ], + "orders":{ + "columns":[ + "id" + ], + "ascending":false + } + } + + const result = await axios + .post(USER_WASPANG, payload, config) + .then(res => res) + .catch((error) => error.response); + console.log('test role', result) + + if (result && result.data && result.code == "200") { + // console.log("result", result); + this.setState({ dataEmployee: result.data }, ()=> { + this.setDataEmployee(); + }); + } else { + // NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + setDataEmployee = () => { + const listEmployee = []; + const allIdEmployee = []; + this.state.dataEmployee.map((val, index)=> { + allIdEmployee.push(val.id); + listEmployee.push({ + key: val.id, + id: val.id, + title: val.join.m_users_name + }); + }) + + this.setState({ dataSourceEmployee: listEmployee, allEmployeeId:allIdEmployee }) + } + + handleChangeTransfer = (nextTargetKeys, direction, moveKeys) => { + this.setState({ targetKeys: nextTargetKeys,errorEmployee:"" }); + + // console.log('targetKeys: ', nextTargetKeys); + // console.log('direction: ', direction); + // console.log('moveKeys: ', moveKeys); + }; + + handleSelectChangeTransfer = (sourceSelectedKeys, targetSelectedKeys) => { + this.setState({ selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys],errorEmployee:"" }); + + // console.log('sourceSelectedKeys: ', sourceSelectedKeys); + // console.log('targetSelectedKeys: ', targetSelectedKeys); + }; + + renderForm = () => { + const { errorTitle, errorMessage } = this.state + return ( +
+ + + this.handleChangePenerima(e.target.value )}> + + + + + + + + + this.setState( + { + errorTitle: e.target.value !== "" ? "" : ERROR_TITLE, + title: e.target.value + })} + placeholder="judul.." /> + {errorTitle && ( + {errorTitle} + )} + + + + this.setState({ errorMessage: e.target.value !== "" ? "" : ERROR_MESSAGE, message: e.target.value })} placeholder="pesan.." /> + {errorMessage && ( + {errorMessage} + )} + + + + this.setState({ description: e.target.value })} placeholder="deskripsi.." /> + +
+ ) + } + handleCloseDialog = () => { + console.log(this.state.targetKeys); + this.props.closeDialog() + this.setState({ + errorTitle: "", + errorMessage: "", + errorDivision: "", + errorEmployee: "" + }) + } + render() { + return ( + + Broadcast + + {this.renderForm()} + + + {' '} + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterBroadcast/index.js b/src/views/Master/MasterBroadcast/index.js new file mode 100644 index 0000000..0d4c556 --- /dev/null +++ b/src/views/Master/MasterBroadcast/index.js @@ -0,0 +1,730 @@ +import { DatePicker, Pagination } from 'antd'; +import axios from 'axios'; +import moment from 'moment'; +import React, { Component } from 'react'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Button, Card, CardBody, CardHeader, DropdownItem, DropdownMenu, DropdownToggle, Input, InputGroup, InputGroupButtonDropdown, Table, Row, Col } from 'reactstrap'; +import * as XLSX from 'xlsx'; +import { API_BROADCAST_SIMPRO, BASE_SIMPRO, BASE_URL_GEOHR_API2 } from '../../../const/ApiConst'; +import DialogForm from './DialogForm'; +import DialogDetail from './DialogDetail'; +import { Tooltip } from 'reactstrap'; + + +const { RangePicker } = DatePicker; +const token = window.localStorage.getItem('token'); +const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } +}; + +const id_org = window.localStorage.getItem('id_org'); +const roleName = window.localStorage.getItem('role_name'); + +const column = [ + { name: "Aksi" }, + + { name: "Judul" }, + { name: "Pesan" }, + { name: "Deskripsi" }, + { name: "Status" }, + { name: "Tanggal" }, +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + dataExport: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + typeClock: "All", + startDate: moment(moment().format("YYYY-M-D")), + endDate: moment(moment().format("YYYY-M-D")), + dataExport:[], + currentDay: 'today', + searchDetail: "Judul", + searchDetailField: "title_notif", + splitButtonOpen: false, + alertBroadcast: false, + statusSend: '', + idSend: 0, + dataDetail:[], + openDialogDetail: false, + toltipDetail:false, + tooltipresend:false, + tooltipsend:false, + tooltipTambah:false, + tooltipExport:false, + } + } + + async componentDidMount() { + await this.getDataBroadcast(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search, startDate } = this.state + if (search !== prevState.search) this.getDataBroadcast() + if (startDate !== prevState.startDate) this.getDataBroadcast() + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataBroadcast = async () => { + // let url = `${API_BROADCAST}/search`; + let url = BASE_SIMPRO + `/broadcast/search`; + const { searchDetail } = this.state + + let start = 0; + if (this.state.currentPage !== 1 && this.state.currentPage > 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + let dateStart = moment(this.state.startDate).format("YYYY-MM-DD 00:00:00"); + let dateEnd = moment(this.state.endDate).format("YYYY-MM-DD 23:59:59"); + + const payload = { + "columns": [ + { + "logic_operator": "range", + "name": "created_at", + "operator": "AND", + "value": `${dateStart}`, + "value1": `${dateEnd}` + }, + { + "logic_operator": "like", + "name": "title_notif", + "operator": "AND", + "value": "" + } + ], + "orders": { + "ascending": false, + "columns": [ + "created_at" + ] + }, + "paging": { + "length": this.state.rowsPerPage, + "start": start + } + } + + // const formData = new FormData(); + // formData.append("start", start); + // formData.append("id_org", id_org); + // formData.append("length", this.state.rowsPerPage); + // formData.append("startDate", dateStart.format("YYYY-M-D")+" 00:00:00"); + // formData.append("endDate", dateEnd.format("YYYY-M-D")+" 23:59:59"); + // formData.append('field', this.state.searchDetailField); + if(this.state.search!=="" && this.state.search!==null){ + // formData.append('value', this.state.search); + } + + // const param = { + // "paging": { "start": start, "length": this.state.rowsPerPage }, + // ...searchDetail === "Semua" && + // { + // "filter_columns": [ + // { "name": "title_notif", "value": this.state.search.toString() }, + // { "name": "message_notif", "value": this.state.search.toString() }, + // { "name": "description", "value": this.state.search.toString() }, + // { "name": "status_send", "value": this.state.search.toString() }, + // ], + // }, + // ...searchDetail !== "Semua" && + // { + // "columns": [ + // { "name": this.state.searchDetailField, "logic_operator": "like", "operator": "and", "value": this.state.search.toString() } + // ] + // }, + // "orders": { "columns": ["created_date"], "descending": true } + // } + + // const result = await axios + // .post(url, param) + // .then(res => res) + // .catch((error) => error.response); + + const result = await axios + .post(url, payload, config) + .then(res => res) + .catch((error) => error.response); + if (result && result.data) { + if(result.data.code===200){ + this.setState({ dataTable: result.data.data, totalPage: result.data.totalRecord }); + }else{ + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + + // console.log("data result", result) + // if(result && result.data){ + // if (result.status === 200) { + // this.setState({ dataTable: result.data.data, totalPage: result.data.totalRecord }); + // } else { + // NotificationManager.error('Gagal Menerima Data!!', 'Failed'); + // } + // } + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = (type, data) => { + this.setState({ openDialog: false }) + } + handleSaveBroadcast = (type, data) => { + this.setState({ openDialog: false }) + this.saveBroadcast(type, data) + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmBroadcast = async () => { + const { idSend, statusSend } = this.state + let url = `${API_BROADCAST_SIMPRO}/edit-status-send/${idSend}`; + let payload = { + "status_send": statusSend + } + const result = await axios + .put(url, payload, config) + .then(res => res) + .catch((error) => error.response); + if (result.status === 200) { + NotificationManager.success(`Broadcast berhasil di ${statusSend === 'send' ? 'Kirim' : 'Kirim Ulang'}`) + this.getDataBroadcast() + this.setState({ alertBroadcast: false }) + } else { + NotificationManager.error(result.message, 'Failed!!'); + this.setState({ alertBroadcast: false }) + } + } + + saveBroadcast = async (type, data) => { + // let url = `${API_BROADCAST}/add`; + let url = BASE_SIMPRO + `/broadcast/add`; + // const param = { + // "title_notif": data.title, + // "message_notif": data.message, + // "description": data.description, + // "status_send": type + // } + + const param = { + "title_notif": data.title, + "status_send": type, + "send_to_type": "all", + "message_notif": data.message, + "description": data.description, + } + + const paramRoles = { + "title_notif": data.title, + "status_send": type, + "send_to_type": "roles", + "message_notif": data.message, + "description": data.description, + "send_to_id": data.id + } + + const paramUsers = { + "title_notif": data.title, + "status_send": type, + "send_to_type": "users", + "message_notif": data.message, + "description": data.description, + "send_to_id": data.id.map((id, index) => id) + } + // const formData = new FormData(); + // formData.append("title", data.title); + // formData.append("message", data.message); + // formData.append("description", data.description); + // formData.append("send_to_type", data.send_to_type); + // if(type==="send"){ + // formData.append('status_send', "complete"); + // }else{ + // formData.append('status_send', "registered"); + // } + + + // if(data.send_to_type==="roles"){ + // formData.append('id', data.id); + // }else if(data.send_to_type==="users"){ + // data.id.map((id, index)=> { + // formData.append('id[]', id); + // console.log("test", id); + // }) + // }else{ + // formData.append('id', "all"); + // } + // const param = { + // "title_notif": data.title, + // "message_notif": data.message, + // "description": data.description, + // "status_send": type, + // "send_to_type": data.send_to_type, + // "send_to_id": data.id + // } + // console.log("cek param", param) + + if(data.send_to_type === "all") { + const result = await axios.post(url, param, config) + .then(res => res) + .catch((error) => error.response); + + if (result) { + if(result.data){ + if(result.data.code===200){ + this.getDataBroadcast(); + NotificationManager.success('Broadcast berhasil terkirimkan', 'Success!!'); + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + } else if(data.send_to_type === "roles") { + const result = await axios.post(url, paramRoles, config) + .then(res => res) + .catch((error) => error.response); + + console.log('test result broadcast all', result) + if (result) { + if(result.data){ + if(result.data.code===200){ + this.getDataBroadcast(); + NotificationManager.success('Broadcast berhasil terkirimkan', 'Success!!'); + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + } else if(data.send_to_type === "users") { + const result = await axios.post(url, paramUsers, config) + .then(res => res) + .catch((error) => error.response); + + if (result) { + if(result.data){ + if(result.data.code === 200){ + this.getDataBroadcast(); + NotificationManager.success('Broadcast berhasil terkirimkan', 'Success!!'); + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + }else{ + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + } + + handleEdit = (id, type) => { + this.setState({ alertBroadcast: true, statusSend: type, idSend: id }) + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataBroadcast(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataBroadcast(); + }) + } + + toggle = (param) => { + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "tambah") { + this.setState(prevState => ({ tooltipTambah: !prevState.tooltipTambah })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + } + + handleDatePicker = (date, dateString) => { + this.setState({ startDate: date[0], endDate: date[1] }, () => { + this.getDataBroadcast(); + }) + } + + handleTipe = (e) => { + this.setState({ typeClock: e.target.value }, () => { + this.getDataBroadcast(); + }); + } + + handleExportExcel = async () => { + let url = BASE_SIMPRO + `/broadcast.php?act=get_data&role_name=${roleName}`; + const { searchDetail } = this.state + + let start = 0; + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + let dateStart = this.state.startDate; + let dateEnd = this.state.endDate; + + const formData = new FormData(); + formData.append("startDate", dateStart.format("YYYY-M-D")+" 00:00:00"); + formData.append("endDate", dateEnd.format("YYYY-M-D")+" 23:59:59"); + formData.append('field', this.state.searchDetailField); + formData.append("start", start); + formData.append("length", "all"); + if(this.state.search!=="" && this.state.search!==null){ + formData.append('value', this.state.search); + } + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => error.response); + if (result && result.data) { + if(result.data.code_status===200){ + this.setState({ dataExport: result.data.data }, () => { + this.exportExcel(); + }); + }else{ + // NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } else { + // NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + exportExcel = () => { + const dataExcel = this.state.dataExport || []; + const dataExport = []; + // {n.title_notif} + // {n.message_notif !== "" ? n.message_notif : "-"} + // {n.description !== "" ? n.description : "-"} + // {n.status_send !== "" ? n.status_send : "-"} + // {n.created_date !== "" ? moment.utc(n.created_date).for + dataExcel.map((val) => { + let row = { + "Tanggal Broadcast": moment(val.created_date).format("YYYY-MM-DD HH:mm:ss"), + "Penerima": val.employee_name, + "Judul": val.title_notif, + "Pesan": val.message_notif, + "Deskripsi": val.description, + "Status": val.status_send + } + dataExport.push(row); + }); + const fileName = "Broadcast.xlsx"; + const ws = XLSX.utils.json_to_sheet(dataExport); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'Broadcast'); + + XLSX.writeFile(wb, fileName); + } + + handleDetail = (data) => { + console.log("ceh handle detail", data) + this.setState({ dataDetail:data },()=> { + this.showDetailDialog(); + this.setState({ openDialogDetail:true }); + }); + } + + toggleTooltip = (type) => { + if(type==="detail"){ + this.setState({ toltipDetail:!this.state.toltipDetail }) + }else if(type==="resend"){ + this.setState({ tooltipresend:!this.state.tooltipresend }) + }else if(type==="send"){ + this.setState({ tooltipsend:!this.state.tooltipsend }) + } + } + + renderBtnResend = (n) => { + return ( + + {/* */} + this.handleEdit(n, 'resend')}> + this.toggleTooltip("resend")}> + Kirim Ulang + + + ) + } + + renderBtnSend = (n) => { + // + return ( + + this.handleEdit(n, 'send')}> + this.toggleTooltip("send")}> + Kirim + + + ) + } + + renderTable = () => { + const dataTable2 = this.state.dataTable || []; + return ( + + {dataTable2.length !== 0 ? dataTable2.map((n) => { + return ( + + + + {n.status_send === 'completed' || n.status_send === 'failed' || n.status_send === 'send' ? + this.renderBtnResend(n.id) : + this.renderBtnSend(n.id) + } + + this.handleDetail(n)}> + this.toggleTooltip("detail")}> + Detail + + + + + + {n.title_notif} + {n.message_notif !== "" ? n.message_notif : "-"} + {n.description !== "" ? n.description : "-"} + {n.status_send !== "" ? n.status_send : "-"} + {n.created_date !== "" ? moment.utc(n.created_date).format("YYYY-MM-DD HH:mm:ss") : "-"} + + ) + }) : + Data Tidak Tersedia + + } + + ) + } + + toggleDropDown = () => { + this.setState({ splitButtonOpen: !this.state.splitButtonOpen }) + } + + handleCloseDetail = () => { + this.setState({ openDialogDetail:false }) + } + + toggleDialogDetail = () => { + this.setState({ openDialogDetail:!this.state.openDialogDetail }) + } + + handleChangeDay = (e) => { + const val = e.target.value; + this.setState({ currentDay:val }); + if(val==="today"){ + // console.log("test 1 test",val); + this.setState({ + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else if(val==="3 day"){ + // console.log("test test",val); + this.setState({ + startDate:moment(moment().subtract(3, "days").format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else if(val==="7 day"){ + // console.log("test test",val); + this.setState({ + startDate:moment(moment().subtract(7, "days").format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else{ + console.log("test 2 test",val); + this.setState({ + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + } + } + + render() { + const { tooltipTambah, tooltipExport,dataTable, splitButtonOpen, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete, statusSend } = this.state + return ( +
+ + this.setState({ alertBroadcast: false })} + focusCancelBtn + > + {`Yakin ingin ${statusSend === 'send' ? 'Mengirimkan' : 'Mengirimkan Ulang'} Broadcast?`} + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + this.toggleDialogDetail} + dataDetail={this.state.dataDetail} + showDialog={showDialog => this.showDetailDialog = showDialog} + /> + + +

{this.props.params.name}

+ + + + + {/* */} + this.toggle("tambah")}> + Tambah Broadcast + + this.toggle("export")}> + Export Excel + + + +
+ +
+
+
+ this.handleChangeDay(e)} defaultValue={this.state.currentDay}> + + + + +
+
+ {' '} + +
+
+ + + + + + {/* this.setState({ + searchDetail: "Nama Karyawan", + searchDetailField: "name" + })}>Nama Karyawan */} + this.setState({ + searchDetail: "Judul", + searchDetailField: "title_notif" + })}>Judul + this.setState({ + searchDetail: "Deskripsi", + searchDetailField: "description" + })}>Deskripsi + this.setState({ + searchDetail: "Pesan", + searchDetailField: "message_notif" + })}>Pesan + this.setState({ + searchDetail: "Status", + searchDetailField: "status_send" + })}>Status + + + +
+ + + + {column.map((i, index) => { + return ( + + ) + })} + + + {this.renderTable()} +
{i.name}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterCountry/MasterCountry.css b/src/views/Master/MasterCountry/MasterCountry.css new file mode 100644 index 0000000..e69de29 diff --git a/src/views/Master/MasterCountry/MasterCountry.js b/src/views/Master/MasterCountry/MasterCountry.js new file mode 100644 index 0000000..66351e8 --- /dev/null +++ b/src/views/Master/MasterCountry/MasterCountry.js @@ -0,0 +1,53 @@ +import React, { Component } from 'react' +// import './MasterCountry.css' +import DataTable from '../../../components/DataTable' +import {API_LIST_DATA_COUNTRY, API_INSERT_DATA_COUNTRY, API_UPDATE_DATA_COUNTRY, API_DELETE_DATA_COUNTRY } from '../../../const/ApiConst.js' + + +// important! +const columns = [{ + dataField: 'id', + alias: "Id", + showInput: false, + type: "number", + state: 0 +}, { + dataField: 'country_name', + alias: "Country Name", + showInput: true, + type: "text", + state: "" +}, { + dataField: 'last_updated', + alias: "Last Updated", + showInput: false, + type: "text", + state: "" +}]; + +class MasterCountry extends Component { + constructor(props) { + super(props) + this.state = {} + } + + componentDidMount() {} + + render() { + + return ( +
+ +
+ ) + } +} + +export default MasterCountry; \ No newline at end of file diff --git a/src/views/Master/MasterCountry/package.json b/src/views/Master/MasterCountry/package.json new file mode 100644 index 0000000..e270c3c --- /dev/null +++ b/src/views/Master/MasterCountry/package.json @@ -0,0 +1,6 @@ +{ + "name": "MasterCountry", + "version": "0.0.0", + "private": true, + "main": "./MasterCountry.js" +} diff --git a/src/views/Master/MasterCustomer/DialogForm.js b/src/views/Master/MasterCustomer/DialogForm.js new file mode 100644 index 0000000..3f46502 --- /dev/null +++ b/src/views/Master/MasterCustomer/DialogForm.js @@ -0,0 +1,412 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input, FormText, CardImg, FormFeedback } from 'reactstrap'; +import { ListGroup, ListGroupItem } from 'reactstrap'; +// import Map from './Map' +import axios from 'axios' +import { NotificationContainer, NotificationManager } from 'react-notifications'; +// import ShowImage from './ShowImage'; + + +const API = `https://oslog.id/geohr-api` + + +const formState = { + files: [], + company: "", + name: "", + employes: "", + address: "", + description: "", + geom: "", + openMap: false, + showImg: false, + getUrl: "", + namePhoto: "", + lat: "", + lon: "", + latlon: "", + buffer_radius: null, + idData: 0, + ref_id: "", + latlonMessage: "", + companyMessage: "", + buffer_radiusMessage: "", + store_manager: "", + income: "" +} + +const ERROR_LATLON = "Geom cannot be empty" +const ERROR_COMPANY = "Company cannot be empty" +const ERROR_NAME = "name cannot be empty" +const ERROR_RADIUS = "Buffer Radius cannot be empty" +const ERROR_INCOME = "Income cannot be empty" +const ERROR_STORE_MANAGER = "Store Manager cannot be empty" + + +export default class DialogForm extends Component { + state = { + ...formState + } + + + fileSelectedHandler = (e) => { + this.setState({ files: [...this.state.files, ...e.target.files] }) + } + + handleCancel = () => { + this.props.closeDialog() + this.setState({ + ...formState + }) + } + + cancelMap = () => { + this.setState({ openMap: false }) + } + handleOpenMap = () => { + this.setState({ openMap: true }) + } + + getLocation = (param) => { + console.log(`getLocation`, param) + this.setState({ + latlonMessage: param != "" ? "" : ERROR_LATLON, + lat: param.lat, + lon: param.lng, + latlon: `lat:${param.lat} lng:${param.lng}` + }) + } + + handleSave = async () => { + const err = this.handleValidation(); + const { files, company, name, employes, address, description, geom, lat, lon, idData, buffer_radius, store_manager, income } = this.state + const { methodAct } = this.props + + // console.log(`idData`, idData) + + if (methodAct === 'Save') { + if (!err) { + const payload = { + name, + address, + buffer_radius: parseInt(buffer_radius), + store_manager, + lat: lat === "" ? null : lat, + lon: lon === "" ? null : lon, + income: parseInt(income), + // files + } + console.log(`payload`, JSON.stringify(payload)) + + const result = await axios.post(`${API}/customer/add`, payload).then(response => response).catch(err => err.response) + + console.log('test result save', result) + console.log(`result add`, result.data.data) + + if (result.data.code === 200) { + this.setState({ + ...formState, + // ref_id: result.data.data.id + }) + this.props.closeDialog() + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + // handlesave foto + let ref_id = parseInt(result.data.data.id) + if (ref_id) { + if (files.length > 0) { + const formData = new FormData() + files.map((res) => { + if (res) + formData.append("ref_id", ref_id); + formData.append("files", res); + }) + await axios.post(`${API}/image/customer/upload`, formData).then(response => response).catch(err => err.response) + } + } + } + + } else if (methodAct === 'Edit') { + if (!err) { + const payload = { + "company": parseInt(company), + name, + employes, + address, + description, + lat: lat === "" ? null : lat, + lon: lon === "" ? null : lon, + buffer_radius: parseInt(buffer_radius) + // geom, + // files + } + console.log(`payload edit`, payload) + const result = await axios.put(`${API}/customer/${idData}/edit`, payload).then(response => response).catch(err => err.response) + console.log(`result edit`, result.data) + if (result.data.code === 200) { + this.setState({ + ...formState + }) + this.props.closeDialog() + } + } + } else if(methodAct === 'Edit Photo') { + let ref_id = parseInt(idData) + console.log('test id', idData) + if (ref_id) { + if (files.length > 0) { + const formData = new FormData() + files.map((res) => { + if (res) + formData.append("ref_id", ref_id); + formData.append("files", res); + }) + await axios.post(`${API}/image/customer/upload`, formData).then(response => response).catch(err => err.response) + this.props.closeDialog() + + } + } + } + + // handlesave edit foto + if (files.length > 0) { + const formData = new FormData() + files.map((res) => { + if (res) + formData.append("ref_id", idData); + formData.append("files", res); + }) + await axios.post(`${API}/image/customer/upload`, formData).then(response => response).catch(err => err.response) + } + } + + handleChange = (e) => { + console.log(`targeeetttt`, e.target.name) + if (e.target.name === "company") { + this.setState({ companyMessage: e.target.value !== "" ? "" : ERROR_COMPANY }) + } + if (e.target.name === "name") { + this.setState({ nameMessage: e.target.value !== "" ? "" : ERROR_NAME }) + } + if (e.target.name === "buffer_radius") { + this.setState({ buffer_radiusMessage: e.target.value !== "" ? "" : ERROR_RADIUS }) + } + if (e.target.name === "income") { + this.setState({ income: e.target.value !== "" ? "" : ERROR_INCOME }) + } + if (e.target.name === "store_manager") { + this.setState({ store_manager: e.target.value !== "" ? "" : ERROR_STORE_MANAGER }) + } + e.preventDefault() + this.setState({ + [e.target.name]: e.target.value, + }) + } + + setUrlPhotos = async (param) => { + let checkImg = null + if (param.image) { + checkImg = `${API}/assets/images/customer/${param.image}` + } else { + checkImg = URL.createObjectURL(param) + } + this.setState({ + showImg: true, getUrl: checkImg, + // namePhoto: param.name ? param.name : param.image + }) + // console.log(`param photos`, setUrlImg) + } + + closeImg = () => { + this.setState({ showImg: false }) + } + + getUrlImagery = async (param) => { + const payload = { + "paging": { "start": 0, "length": -1 }, + "columns": [ + { "name": "category", "logic_operator": "=", "value": "office", "operator": "and" }, + { "name": "ref_id", "logic_operator": "=", "value": param.toString(), "operator": "and" } + ], + "orders": { "columns": ["id"], "ascending": true } + } + const resultUrl = await axios.post(`${API}/image/search`, payload).then(response => response).catch(err => err.response) + this.setState({ files: resultUrl.data.data }) + console.log(`resultUrl`, resultUrl.data.data) + + } + componentDidUpdate(prevProps, prevState) { + const { editData, methodAct } = this.props + // console.log(`editData`, editData) + if ((prevProps.editData !== editData) && (methodAct == "Edit" || methodAct == "View" || methodAct == "Edit Photo")) { + this.getUrlImagery(editData.id) + this.setState({ + idData: editData.id, + company: editData.company, + name: editData.name, + employes: editData.employes, + address: editData.address, + description: editData.description, + namePhoto: editData, + lat: editData.lat, + lon: editData.lon, + latlon: `${editData.lat},${editData.lon}`, + buffer_radius: editData.buffer_radius, + store_manager: editData.store_manager, + income: editData.income + // geom: editData.geom + }) + } + } + + handleValidation = () => { + const { files, openMap, company, name, employes, address, description, geom, showImg, getUrl, namePhoto, buffer_radius, latlon, lat, lon } = this.state + let isError = false; + const errors = { + latlonMessage: "", + companyMessage: "", + nameMessage: "", + }; + // if (lat === "" && lon === "") { + // isError = true; + // errors.latlonMessage = ERROR_LATLON; + // } + + if (name === "") { + isError = true + errors.nameMessage = ERROR_NAME + } + if (buffer_radius === null) { + isError = true + errors.buffer_radiusMessage = ERROR_RADIUS + } + this.setState({ + latlonMessage: "", + companyMessage: "", + nameMessage: "", + buffer_radiusMessage: "", + ...errors, + }); + + return isError; + } + render() { + const { files, openMap, company, name, employes, address, description, geom, showImg, getUrl, namePhoto, buffer_radius, latlon, lat, lon, latlonMessage, companyMessage, nameMessage, buffer_radiusMessage, store_manager, income } = this.state + const { methodAct } = this.props + console.log(`geom`, geom) + + + return ( +
+ + {/* */} + + SAVE CUSTOMER + + + +
+ + + + + + {nameMessage && ( + {nameMessage} + )} + + + + + + + {companyMessage && ( + {companyMessage} + )} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {latlonMessage && ( + {latlonMessage} + )} + + + + + + {methodAct !== "View" && ( + <> + + + Max Size 1Mb + + )} +
+ {files.map((item) => { + // this.setUrlPhotos(item) + let checkImg = null + if (item.image) { + checkImg = `${API}/assets/images/customer/${item.image}` + } else { + checkImg = URL.createObjectURL(item) + } + return ( + ... + ) + })} +
+
+
+ + + {/*
+ +
*/} + +
+
+ {methodAct !== "View" && ( + + {' '} + + + )} +
+
+ ) + } +} diff --git a/src/views/Master/MasterCustomer/SettingCustomer.js b/src/views/Master/MasterCustomer/SettingCustomer.js new file mode 100644 index 0000000..8fe7d0c --- /dev/null +++ b/src/views/Master/MasterCustomer/SettingCustomer.js @@ -0,0 +1,362 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input, Modal, ModalHeader, ModalBody, ModalFooter, FormGroup, Label } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager} from 'react-notifications'; +import { Pagination } from 'antd'; +// npm install --save-dev @iconify/react @iconify-icons/ant-design +import { Icon, InlineIcon } from '@iconify/react'; +import closeCircleOutline from '@iconify/icons-ion/close-circle-outline'; +import settingOutlined from '@iconify/icons-ant-design/setting-outlined'; +import {Link} from 'react-router-dom' +import Select from 'react-select' +import { formatLabel } from '../../../const/CustomFunc.js'; + +import { SketchPicker } from 'react-color'; + + +const BASE_URL = "https://oslog.id/geohr-api/"; + + +const column = [ + { name: "Group Sales" }, + { name: "Name" }, + { name: "Phone Number" }, + { name: "Email" }, + { name: "Address" }, +] + +const ruleCustomer = [ + { + "id": 1, + "rule_name": "default", + "style": { + "fill_color": "#000000", + }, + "condition": [] + }, + // { + // "id": 2, + // "rule_name": "sales yang punya nama mohammad", + // "style": { + // "fill_color": "#22194D", + // }, + // "condition": [{ + // "column": "name", + // "operator": "like", + // "value": "mohammad" + // }] + // }, +] + +const options = [ + { value: 'name', label: 'Name' }, + { value: 'store_manager', label: 'Store Manager' }, + { value: 'income', label: 'Income' }, + { value: 'buffer_radius', label: 'Buffer Radius' }, + { value: 'address', label: 'Address' } + + ] + + const logicOperator = [ + { value: '==', label: '==' }, + { value: '<>', label: '<>' }, + { value: '<', label: '<' }, + { value: '>', label: '>' }, + { value: '<=', label: '<=' }, + { value: '>=', label: '>=' }, + { value: 'like', label: 'like' }, + { value: 'between', label: 'between' }, + ] + + +const LENGTH_DATA = 10 +export default class SettingCustomer extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + typeDialog: '', + alertDelete: false, + idDelete: 0, + dataGs: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + ruleCustomer: [], + modalEdit: false, + modalAdd: false, + rule_name: "", + dataEdit: null, + color: "#000", + id: 0, + fill_color: "", + column: "", + operator: "", + value: "" + } + } + + async componentDidMount () { + this.getRuleCustomer() + } + + async componentDidUpdate (prevProps, prevState) { + + } + + getRuleCustomer = () => { + this.setState({ruleCustomer: ruleCustomer}) + } + + handleOpenDialog = (type) => { + this.setState({modalEdit: true, typeDialog: type }, () => this.handleSetData()); + } + + handleSetData = () => { + const { dataEdit, typeDialog, ruleCustomer} = this.state + if (typeDialog === "edit") { + // do nothing + } + else if (typeDialog === "add") { + this.setState({ + // id: 0, + dataEdit: { + "id": ruleCustomer.length + 1, + "rule_name": "", + "style": { + "fill_color": "#000000", + }, + "condition": [] + } + }) + } + } + + handleEdit = (data) => { + this.setState({dataEdit: data}, () => this.handleOpenDialog('edit')); + } + + handleDelete = (data) => { + this.setState({alertDelete: true}) + } + + handleCancel = () => { + this.setState({ + modalEdit: false, + typeDialog: '', + id: 0, + dataEdit: null, + }); + } + + handleSave = () => { + + const { typeDialog, ruleCustomer, dataEdit } = this.state; + let idx = null; + + // checking the dialog type first + + // if edit, get the id to replace the object on ruleCustomer by id + if (typeDialog === 'edit') { + idx = ruleCustomer.findIndex(x => x.id === dataEdit.id); + console.log('idx edited', idx); + ruleCustomer.splice(idx, 1, dataEdit); // this will remove the selected old rule and replace with the new one + console.log('after edit', ruleCustomer); + this.setState(prevState => ({ + ...prevState, + ruleCustomer: [...prevState.ruleCustomer], + modalEdit: false, + typeDialog: '', + id: 0, + dataEdit: null + })) + } + + // if add, append the dataEdit to ruleCustomer + else if (typeDialog === 'add') { + this.setState(prevState => ({ + ...prevState, + ruleCustomer: [...prevState.ruleCustomer, this.state.dataEdit], + modalEdit: false, + typeDialog: '', + id: 0, + dataEdit: null + })) + } + } + + handleChangeColorComplete = (color, event) => { + console.log('handleChangeColorComplete',color, event); + // this.setState({ dataEdit["style"]["fill_color"]: color.hex }); + this.setState(prevState => ({ + dataEdit: { + ...prevState.dataEdit, + style: { + ...prevState.dataEdit.style, + fill_color: color.hex + } + } + }) ); + } + + addCondition = () => { + let newCondition = { + "column": "", + "operator": "", + "value": "" + } + this.setState(prevState => ({ + dataEdit: { + ...prevState.dataEdit, + condition: [...prevState.dataEdit.condition, newCondition] + } + })) + } + + removeCondition = (index) => { + const { dataEdit } = this.state; + + let currentCondition = dataEdit.condition; + dataEdit.condition.splice(index, 1); // this where the item removed, changing the original array + + this.setState(prevState => ({ + dataEdit: { + ...prevState.dataEdit, + condition: [...currentCondition] + } + })) + } + + onChangeRuleName = (e) => { + e.persist(); + console.log('onChangeRuleName', e); + console.log('e.target.value', e.target.value); + this.setState((prevState) => ({ + dataEdit: { + ...prevState.dataEdit, + rule_name: e.target.value + } + }) ) + } + + render() { + const { ruleCustomer, modalEdit, modalAdd, search, openDialog, currentPage, rowsPerPage,totalPage, dataTable, dataEdit, + typeDialog } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete:false })} + onCancel={() => this.setState({ alertDelete:false })} + focusCancelBtn + > + This rule will be removed! + + + + +

Rules Customer

+
+ + +
+
+ + + {ruleCustomer.map((n) => { + return( + + + + + + +
+ + + + {n.rule_name} + + +
+ + +
+ + + + + + + ) + }) + } + + + + + this.handleCancel()}> {typeDialog && formatLabel(typeDialog)} Rule Name + +
+ + + this.onChangeRuleName(e)} /> + + + + + this.handleChangeColorComplete(color, event)} + /> + + + +
this.addCondition()}>+ Add Condition
+ { dataEdit && dataEdit.condition && dataEdit.condition.length > 0 && dataEdit.condition.map((item, index) => { + return ( + + + option.value === item.operator) } options={logicOperator} /> + + + + + + this.removeCondition(index)} style={{cursor: 'pointer', textAlign: 'center', verticalAlign: 'sub'}} title="Remove Condition"> + + + + + ) + }) } +
+
+
+ + {' '} + + +
+
+ ) + } +} diff --git a/src/views/Master/MasterCustomer/index.js b/src/views/Master/MasterCustomer/index.js new file mode 100644 index 0000000..344ca18 --- /dev/null +++ b/src/views/Master/MasterCustomer/index.js @@ -0,0 +1,325 @@ +import React, { Component } from 'react' +import { Card, CardBody, CardHeader, Col, Row, Table, Input, InputGroupButtonDropdown, DropdownToggle, DropdownItem, DropdownMenu, InputGroup } from 'reactstrap'; +import { Button } from 'reactstrap'; +import DialogForm from './DialogForm' +import axios from 'axios' +import SweetAlert from 'react-bootstrap-sweetalert'; +import { Pagination } from 'antd'; +import { Icon, InlineIcon } from '@iconify/react'; +import settingOutlined from '@iconify/icons-ant-design/setting-outlined'; +import { Link } from 'react-router-dom'; +import imagesIcon from '@iconify/icons-fa-solid/images'; + +import { Tooltip } from 'reactstrap'; + + +const API = `https://oslog.id/geohr-api` +const column = [ + { name: "Name" }, + { name: "Store Manager" }, + { name: "Income" }, + { name: "Buffer Radius" }, + { name: "Address" }, +] + +const LENGTH_DATA = 10 +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + editData: "", + methodAct: "Save", + alertWarning: false, + alert: false, + idDelete: 0, + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + splitButtonOpen: false, + searchDetail: "All", + searchDetailField: "name", + tooltipDetail: false, + tooltipPhoto: false, + } + } + + handleOpenDialog = () => { + this.setState({ openDialog: true, methodAct: "Save" }) + } + + handleCloseDialog = () => { + this.setState({ openDialog: false }) + this.getDataCustomer() + } + + getDataCustomer = async () => { + const { searchDetail, searchDetailField } = this.state + let start = 0; + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + const payload = { + "paging": { "start": start, "length": this.state.rowsPerPage }, + // "columns": [ + // { + // "name": "name", + // "logic_operator": "like", + // "value": this.state.search.toString(), + // "operator": "and" + // } + // ], + ...searchDetail === "All" && + { + "filter_columns": [ + { "name": "name", "value": this.state.search.toString() }, + { "name": "income", "value": this.state.search.toString() }, + { "name": "buffer_radius", "value": this.state.search.toString() }, + { "name": "address", "value": this.state.search.toString() }, + ], + }, + ...searchDetail !== "All" && + { + "columns": [ + { "name": searchDetailField, "logic_operator": "like", "operator": "and", "value": this.state.search.toString() } + ] + }, + "orders": { "columns": ["name"], "ascending": true } + } + const result = await axios.post(`${API}/customer/search`, payload) + .then((response) => response) + .catch((error) => error.response) + + if (result.data.code === 200) { + // console.log('test data customer', result) + this.setState({ dataTable: result.data.data, totalPage: result.data.totalRecord }) + } + } + + componentDidMount = async () => { + await this.getDataCustomer() + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataCustomer() + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + handleEditData = (data) => { + this.setState({ editData: data, openDialog: true, methodAct: "Edit" }) + // console.log(`handleEditData`, data) + } + handleDetail = (data) => { + this.setState({ editData: data, openDialog: true, methodAct: "View" }) + console.log(`handleEditData`, data) + } + + handleEditPhoto = (data) => { + this.setState({ editData: data, openDialog: true, methodAct: "Edit Photo" }) + console.log(`handleEditData`, data) + + } + + handleDeleted = async (param) => { + const { idDelete } = this.state + // console.log(`idDelete`, idDelete) + // console.log(`param`, param) + + if (param === "delete") { + + const result = await axios.delete(`${API}/office/${idDelete}/delete`) + .then((response) => response) + .catch((error) => error.response) + // console.log(`result delete`, result.data) + this.setState({ + alertWarning: false, + alert: true + }) + await this.getDataOffices() + } else { + this.setState({ + alertWarning: false + }) + } + } + + onDelete = (id) => { + this.setState({ + alertWarning: true, + idDelete: id + }) + } + + toggleDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onShowSizeChange = (current, pageSize) => { + // console.log("show size", pageSize); + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataCustomer(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataCustomer(); + }) + } + + toggleDropDown = () => { + this.setState(prevState => ({ splitButtonOpen: !prevState.splitButtonOpen })) + } + + toggle = (param) => { + // console.log(`ee`, param) + if (param === "detail") { + this.setState(prevState => ({ tooltipDetail: !prevState.tooltipDetail })) + } else if (param === "photo") { + this.setState(prevState => ({ tooltipPhoto: !prevState.tooltipPhoto })) + } + } + + render() { + const { search, totalPage, rowsPerPage, currentPage, openDialog, dataTable, editData, methodAct, alertWarning, alert, searchDetail, searchDetailField, splitButtonOpen, tooltipDetail, tooltipPhoto } = this.state + // console.log(`parent`, editData) + + return ( +
+ this.setState({ alert: false })} onCancel={() => this.setState({ alert: false })}> + You clicked the button! + + this.handleDeleted("delete")} + onCancel={() => this.handleDeleted("cancel")} + focusCancelBtn + > + This will be deleted + + this.handleCloseDialog()} + toggleDialog={() => this.toggleDialog()} + editData={editData} + methodAct={methodAct} + /> + + +

Customers

+
+ {/* */} + + + + +
+
+ + + + + + + this.setState({ + searchDetail: "All", + searchDetailField: "name" + })}>All + this.setState({ + searchDetail: "Name", + searchDetailField: "name" + })}>Name + this.setState({ + searchDetail: "Store Manager", + searchDetailField: "store_manager" + })}>Store Manager + this.setState({ + searchDetail: "Income", + searchDetailField: "income" + })}>Income + this.setState({ + searchDetail: "Buffer Radius", + searchDetailField: "buffer_radius" + })}>Buffer Radius + this.setState({ + searchDetail: "Address", + searchDetailField: "address" + })}>Address + + + + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataTable.map((n) => { + return ( + + + + + + + + + ) + })} + +
Actions{i.name}
+ {/* detail */} + this.handleDetail(n)}> + this.toggle("detail")}> + Detail + + this.handleEditPhoto(n)}> + + + this.toggle("photo")}> + Add Photo + + {n.name}{n.store_manager}{n.income}{n.buffer_radius}{n.address}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterCustomer/package.json b/src/views/Master/MasterCustomer/package.json new file mode 100644 index 0000000..485d34e --- /dev/null +++ b/src/views/Master/MasterCustomer/package.json @@ -0,0 +1,6 @@ +{ + "name": "MasterCustomer", + "version": "0.0.0", + "private": true, + "main": "./MasterCustomer.js" + } \ No newline at end of file diff --git a/src/views/Master/MasterCuti/DialogForm.js b/src/views/Master/MasterCuti/DialogForm.js new file mode 100644 index 0000000..1cffb5d --- /dev/null +++ b/src/views/Master/MasterCuti/DialogForm.js @@ -0,0 +1,165 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Select from 'react-select' +import axios from 'axios'; +import moment from 'moment'; +import { TimePicker } from 'antd'; +import 'antd/dist/antd.css'; + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + name:"", + statusApp:"", + nik:"", + divisi_id:"", + type_leave:"", + start_leave:"", + end_leave:"", + approved_by_leave:"", + employee_id:"", + openDialog: false, + isParentClick: false, + disable: false + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + if(this.props.typeDialog==="Edit"){ + const { dataEdit } = this.props + if(dataEdit.status_approval==="Approved"){ + this.setState({disable:true}); + }else{ + this.setState({disable:false}); + } + this.setState({ + id:dataEdit.id, + name:dataEdit.name, + statusApp:dataEdit.status_approval, + nik:dataEdit.nik, + divisi_id:dataEdit.divisi_id, + type_leave:dataEdit.type_leave, + start_leave:dataEdit.start_leave, + end_leave:dataEdit.end_leave, + approved_by_leave:dataEdit.approved_by_leave, + employee_id:dataEdit.employee_id + }) + }else{ + this.setState({ + id:0, + name:"", + statusApp:"waiting to approval", + nik:"", + divisi_id:"", + type_leave:"", + start_leave:"", + end_leave:"", + approved_by_leave:0, + employee_id:0 + }) + } + this.setState({isParentClick:false}); + } + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + const { + name, + statusApp, + nik, + divisi_id, + type_leave, + start_leave, + end_leave, + approved_by_leave, + employee_id, + id + } = this.state + + let data = ''; + if(this.props.typeDialog==="Save"){ + data = { + name, + statusApp, + nik, + divisi_id, + type_leave, + start_leave, + end_leave, + approved_by_leave, + employee_id, + id + } + this.props.closeDialog('save', data); + }else{ + data = { + name, + statusApp, + nik, + divisi_id, + type_leave, + start_leave, + end_leave, + approved_by_leave, + employee_id, + id + } + this.props.closeDialog('edit', data); + } + + this.setState({ id:0 }); + + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + renderForm = () => { + return( +
+ + + this.setState({ name:e.target.value })} placeholder="name division.." /> + + + + this.setState({ statusApp:e.target.value })}> + + + + + +
+ ) + } + + render() { + return ( + + Add Division + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterCuti/index.js b/src/views/Master/MasterCuti/index.js new file mode 100644 index 0000000..282e928 --- /dev/null +++ b/src/views/Master/MasterCuti/index.js @@ -0,0 +1,427 @@ +import React, { Component } from 'react'; +import { Card, Tooltip, CardBody, CardHeader, Table, Input,InputGroup, InputGroupButtonDropdown, DropdownToggle, DropdownItem, DropdownMenu, Row, Col } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +import { DatePicker } from 'antd'; +import * as XLSX from 'xlsx'; + +const { RangePicker } = DatePicker; + +const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; + +const id_org = window.localStorage.getItem('id_org'); +const roleName = window.localStorage.getItem('role_name'); + +const column = [ + { name: "Status Penyetujuan" }, + { name: "Nama" }, + { name: "Nik" }, + { name: "Nama Divisi" }, + { name: "Tipe Izin" }, + { name: "Mulai Izin" }, + { name: "Akhir Izin" }, + { name: "Penyetuju" } +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + dataExport: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + splitButtonOpen:false, + typeClock: "All", + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + searchDetail: "Nama", + searchDetailField: "name", + tooltipExport:false, + } + } + + async componentDidMount() { + this.getDataLeave(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataLeave() + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataLeave = async () => { + + + let start = 0; + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + let url = BASE_URL + `paid_leave.php?act=get_data&role_name=${roleName}`; + + let dateStart = this.state.startDate; + let dateEnd = this.state.endDate; + + const formData = new FormData(); + formData.append("id_org", id_org); + formData.append("startDate", dateStart.format("YYYY-M-D")+" 00:00:00"); + formData.append("endDate", dateEnd.format("YYYY-M-D")+" 23:59:59"); + formData.append("start", start); + formData.append("length", this.state.rowsPerPage); + formData.append('field', this.state.searchDetailField); + // formData.append('type', this.state.typeClock); + if(this.state.search !== ""){ + formData.append('value', this.state.search); + } + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if(result.data!==undefined){ + if (result.data.code_status == 200) { + this.setState({ dataTable: result.data.data, totalPage: result.data.total_record,dataExport:result.data.data_excel }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = (type, data) => { + console.log(data); + if (type === "save") { + this.saveDataLeave(data); + } else if (type === "edit") { + this.editDataLeave(data); + } + + this.setState({ openDialog: false }) + } + + // toggleAddDialog = () => { + // this.setState({ openDialog: !this.state.openDialog }) + // } + + // onConfirmDelete = async () => { + // const { idDelete } = this.state + // let url = BASE_URL + `employee_devision.php?act=delete&id=${idDelete}`; + // const result = await axios.post(url) + // .then(res => res) + // .catch((error) => error.response); + + // if(result.message!==undefined){ + // if (result.message === "Data Has Been Deleted") { + // this.getDataLeave() + // this.setState({ idDelete: 0, alertDelete: false }) + // NotificationManager.success('Data division berhasil dihapus!!', 'Success!!'); + // } else { + // this.setState({ idDelete: 0, alertDelete: false }) + // NotificationManager.error('Data division gagal dihapus!!', 'Failed!!'); + // } + // }else{ + // if (result.data.message === "Data Has Been Deleted") { + // this.getDataLeave() + // this.setState({ idDelete: 0, alertDelete: false }) + // NotificationManager.success('Data division berhasil dihapus!!', 'Success!!'); + // } else { + // this.setState({ idDelete: 0, alertDelete: false }) + // NotificationManager.error('Data division gagal dihapus!!', 'Failed!!'); + // } + // } + // } + + // saveDataLeave = async (data) => { + // let url = BASE_URL + "employee_devision.php?act=input"; + + // const formData = new FormData() + // formData.append('name', data.name); + // formData.append('description', data.description); + // const result = await axios.post(url, formData) + // .then(res => res) + // .catch((error) => error.response); + + // if(result.message!==undefined){ + // if (result.message === "Data Has Been Saved") { + // this.getDataLeave(); + // NotificationManager.success('Data division berhasil ditambahkan!!', 'Success!!'); + // } else { + // NotificationManager.error(`${result.data.message}`, 'Failed!!'); + // } + // }else{ + // if (result.data.message === "Data Has Been Saved") { + // this.getDataLeave(); + // NotificationManager.success('Data division berhasil ditambahkan!!', 'Success!!'); + // } else { + // NotificationManager.error(`${result.data.message}`, 'Failed!!'); + // } + // } + + // } + + editDataLeave = async (data) => { + let url = BASE_URL + `paid_leave.php?act=edit&id=${data.id}&role_name=${roleName}`; + + const formData = new FormData(); + formData.append('employee_id', data.employee_id); + formData.append('nik', data.nik); + formData.append('divisi_id', data.divisi_id); + formData.append('type_leave', data.type_leave); + formData.append('start_leave', data.start_leave); + formData.append('end_leave', data.end_leave); + formData.append('approved_by_leave', data.approved_by_leave); + formData.append('status_approval', data.statusApp); + + const result = await axios.post(url, formData) + .then(res => res) + .catch((error) => error.response); + + if(result.message!==undefined){ + if (result.message === "Data Has Been Edited") { + this.getDataLeave(); + NotificationManager.success('Data annual leave berhasil diedit!!', 'Success!!'); + } else { + NotificationManager.error('Data annual leave gagal diedit!!', 'Failed!!'); + } + }else{ + if (result.data.message === "Data Has Been Edited") { + this.getDataLeave(); + NotificationManager.success('Data annual leave berhasil diedit!!', 'Success!!'); + } else { + NotificationManager.error('Data annual leave gagal diedit!!', 'Failed!!'); + } + } + + } + + + handleEdit = (data) => { + this.setState({ dataEdit: data }); + this.handleOpenDialog('Edit'); + } + + // handleDelete = (id) => { + // this.setState({ alertDelete: true, idDelete: id }); + // } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataLeave(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataLeave(); + }) + } + + toggle = (param) => { + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + } + + handleDatePicker = (date, dateString) => { + this.setState({ startDate:date[0],endDate:date[1] },()=>{ + this.getDataLeave(); + }) + } + + handleExportExcel = () => { + const dataExcel = this.state.dataExport || []; + const fileName = "Laporan Izin Karyawan"+this.state.startDate.format("D-M-YYYY")+" - "+this.state.startDate.format("D-M-YYYY")+".xlsx"; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'Laporan Izin Karyawan'); + + XLSX.writeFile(wb, fileName); + } + + renderTable = () => { + const dataTable2 = this.state.dataTable || []; + return ( + + {dataTable2.length!==0 ? dataTable2.map((n) => { + return ( + + + {/* this.handleDelete(n.id)}> + this.toggle("delete")}> + Delete + */} + + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + + { n.status_approval!==null ? n.status_approval : "-" } + { n.name } + { n.nik!==null ? n.nik : "-" } + { n.division_name!==null ? n.division_name : "-" } + { n.type_leave!==null ? n.type_leave : "-" } + { n.start_leave!==null ? n.start_leave : "-" } + { n.end_leave!==null ? n.end_leave : "-" } + { n.approver!==null ? n.approver : "-" } + + ) + }) : + No Data Available + + } + + ) + } + + toggleDropDown = () => { + this.setState({splitButtonOpen:!this.state.splitButtonOpen}) + } + + render() { + const { tooltipExport,dataTable, openDialog, splitButtonOpen, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete } = this.state + return ( +
+ + {/* this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data tipe karyawan akan terhapus!! + */} + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + + +

Izin Karyawan

+ + + + + + + + this.setState({ + searchDetail: "Nama", + searchDetailField: "name" + })}>Nama + this.setState({ + searchDetail: "Nama Divisi", + searchDetailField: "division_name" + })}>Divisi + this.setState({ + searchDetail: "Tipe Izin", + searchDetailField: "type_leave" + })}>Tipe Izin + this.setState({ + searchDetail: "Penyetuju", + searchDetailField: "approver" + })}>Penyetuju + this.setState({ + searchDetail: "Status Penyetujuan", + searchDetailField: "status_approval" + })}>Status Penyetujuan + + + + + + + {/* */} + this.toggle("export")}> + Export Excel + + + +
+ + {/*
+
*/} + {/* {' '} */} + {/* */} + {/*
*/} + {/*
*/} + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + { this.renderTable() } +
Actions{i.name}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterDataStyles.css b/src/views/Master/MasterDataStyles.css new file mode 100644 index 0000000..29ba07d --- /dev/null +++ b/src/views/Master/MasterDataStyles.css @@ -0,0 +1,14 @@ +.formLabel { + font-weight: bold; +} + +.formDivTitle { + font-weight: bold; + margin-bottom: 10px; +} + +.formDivScroll { + max-height: 350px; + overflow-y: auto; + overflow-x: hidden; +} \ No newline at end of file diff --git a/src/views/Master/MasterDistrict/MasterDistrict.css b/src/views/Master/MasterDistrict/MasterDistrict.css new file mode 100644 index 0000000..e69de29 diff --git a/src/views/Master/MasterDistrict/MasterDistrict.js b/src/views/Master/MasterDistrict/MasterDistrict.js new file mode 100644 index 0000000..4828d44 --- /dev/null +++ b/src/views/Master/MasterDistrict/MasterDistrict.js @@ -0,0 +1,52 @@ +import React, { Component } from 'react' +// import './MasterDistrict.css' +import DataTable from '../../../components/DataTable' +import { API_LIST_DATA_DISTRICT, API_INSERT_DATA_DISTRICT, API_UPDATE_DATA_DISTRICT, API_DELETE_DATA_DISTRICT } from '../../../const/ApiConst.js' + +// important! +const columns = [{ + dataField: 'id', + alias: "Id", + showInput: false, + type: "number", + state: 0 + }, { + dataField: 'district_name', + alias: "District Name", + showInput: true, + type: "text", + state: "" + }, { + dataField: 'last_updated', + alias: "Last Updated", + showInput: false, + type: "text", + state: "" + }]; + +class MasterDistrict extends Component { + constructor(props) { + super(props) + this.state = {} + } + + componentDidMount() {} + + render() { + + return ( +
+ +
+ ) + } +} + +export default MasterDistrict; \ No newline at end of file diff --git a/src/views/Master/MasterDistrict/package.json b/src/views/Master/MasterDistrict/package.json new file mode 100644 index 0000000..b75be5b --- /dev/null +++ b/src/views/Master/MasterDistrict/package.json @@ -0,0 +1,6 @@ +{ + "name": "MasterDistrict", + "version": "0.0.0", + "private": true, + "main": "./MasterDistrict.js" +} diff --git a/src/views/Master/MasterGroupSales/DialogForm.js b/src/views/Master/MasterGroupSales/DialogForm.js new file mode 100644 index 0000000..aaaf1fa --- /dev/null +++ b/src/views/Master/MasterGroupSales/DialogForm.js @@ -0,0 +1,159 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import axios from 'axios' + + +const BASE_URL = "https://oslog.id/geohr-api/"; +const formState = { + dataTable: [], + openDialog: false, + name: '', + description: '', + id: 0, + idData: 0, + nameMessage: "", +} + +const ERROR_NAME = "name cannot be empty" +export default class DialogForm extends Component { + state = { + ...formState + } + + componentDidUpdate(prevProps, prevState) { + const { dataEdit, methodAct } = this.props + if ((prevProps.dataEdit !== dataEdit) && (methodAct == "Edit" || methodAct == "View")) { + // this.getUrlImagery(dataEdit.id) + this.setState({ + idData: dataEdit.id, + name: dataEdit.name, + description: dataEdit.description, + }) + } + } + + handleSave = async () => { + const err = this.handleValidation(); + let url = BASE_URL + "group-sales/add" + const { name, description, idData } = this.state + const { methodAct } = this.props + console.log(`methodAct`, methodAct) + if (methodAct === "Add") { + if (!err) { + + const payload = { + name, + description, + } + const result = await axios.post(url, payload) + .then(res => res) + .catch((error) => error.response); + console.log(`result`, result) + if (result.data.message === "OK") { + this.props.closeDialog() + this.setState({ + ...formState, + }) + NotificationManager.success('Data group sales berhasil ditambahkan!!', 'Success!!'); + } else { + // this.props.closeDialog() + NotificationManager.error(result.data.message); + } + } + } else if (methodAct === "Edit") { + if (!err) { + let urlEdit = BASE_URL + `group-sales/${idData}/edit`; + const payload = { + name, + description, + } + + const result = await axios.put(urlEdit, payload) + .then(res => res) + .catch((error) => error.response); + console.log(`result edit`, result) + + if (result.data.message === "OK") { + this.props.closeDialog() + this.setState({ + ...formState, + }) + NotificationManager.success('Data group sales berhasil diedit!!', 'Success!!'); + } else { + // this.props.closeDialog() + NotificationManager.error(result.data.message); + } + } + } + + } + + handleCancel = () => { + this.props.closeDialog() + this.setState({ + ...formState + }) + } + + handleValidation = () => { + const { name } = this.state + let isError = false; + const errors = { + nameMessage: "", + } + if (name === "") { + isError = true + errors.nameMessage = ERROR_NAME + } + this.setState({ + nameMessage: "", + ...errors, + }); + + return isError; + } + + renderForm = () => { + const { name, description, nameMessage } = this.state + return ( + + + + + this.setState({ + name: e.target.value, + nameMessage: e.target.value !== "" ? "" : ERROR_NAME + })} placeholder="name" invalid={nameMessage} /> + + + + + + this.setState({ description: e.target.value })} placeholder="description " /> + + + + ) + } + + render() { + const { openDialog, closeDialog, methodAct } = this.props + return ( + + {methodAct} Group Sales + +
+ {this.renderForm()} +
+
+ + {' '} + + +
+ ) + } +} diff --git a/src/views/Master/MasterGroupSales/index.js b/src/views/Master/MasterGroupSales/index.js new file mode 100644 index 0000000..fe27258 --- /dev/null +++ b/src/views/Master/MasterGroupSales/index.js @@ -0,0 +1,306 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input, InputGroupButtonDropdown, DropdownToggle, DropdownItem, DropdownMenu, InputGroup } from 'reactstrap'; +import BootstrapTable from 'react-bootstrap-table-next'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit'; +// import { Pagination, PaginationItem, PaginationLink } from 'reactstrap'; +import { Pagination } from 'antd'; +import { Tooltip } from 'reactstrap'; + +const BASE_URL = "https://oslog.id/geohr-api/"; + +const { SearchBar } = Search; + +const NoDataIndication = () => ( +
+
+
+
+
+); + +const column = [ + { name: "Name" }, + { name: "Description" }, +] + +const LENGTH_DATA = 10 +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + data: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + search: "", + alert: false, + methodAct: "Add", + page: 0, + totalPage: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + splitButtonOpen: false, + searchDetail: "All", + searchDetailField: "name", + tooltipDelete: false, + tooltipEdit: false + } + } + + async componentDidMount() { + const { page, rowsPerPage } = this.state + this.getDataGroupSales(page, rowsPerPage); + } + async componentDidUpdate(prevProps, prevState) { + const { search, rowsPerPage, page } = this.state + // console.log(`componentDidUpdate`, page) + + if (page !== prevState.page) { + this.getDataGroupSales(page, rowsPerPage) + // this.setState({ page: 0 }) + } + + if (rowsPerPage !== prevState.rowsPerPage) { + this.getDataGroupSales(0, rowsPerPage) + this.setState({ page: 0 }) + } + + if (search !== prevState.search) { + this.getDataGroupSales(0, rowsPerPage) + this.setState({ page: 0 }) + } + }; + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, page: 0 }) + }; + + + getDataGroupSales = async (start, length) => { + const { search, rowsPerPage, searchDetail, searchDetailField } = this.state + let url = BASE_URL + "group-sales/search"; + const obj = { + "paging": { "start": start, "length": length }, + // "columns": [ + // { + // "name": "name", + // "logic_operator": "like", + // "value": search, + // "operator": "and" + // } + // ], + ...searchDetail === "All" && + { + "filter_columns": [ + { "name": "name", "value": search.toString() }, + { "name": "description", "value": search.toString() }, + ], + }, + ...searchDetail !== "All" && + { + "columns": [ + { "name": searchDetailField, "logic_operator": "like", "operator": "and", "value": search.toString() } + ] + }, + "orders": { "columns": ["created_date"], "ascending": false } + } + + const result = await axios + .post(url, obj) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result.data.message == "OK") { + console.log(`result data`, result.data.data) + this.setState({ data: result.data.data, totalPage: result.data.totalRecord }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + + + handleOpenDialog = () => { + this.setState({ openDialog: true, methodAct: "Add" }) + } + + handleCloseDialog = () => { + this.setState({ openDialog: false }) + this.getDataGroupSales() + } + + + handleEditData = (data) => { + this.setState({ dataEdit: data, openDialog: true, methodAct: "Edit" }) + // console.log(`handleEditData`, data) + } + + + onDelete = (id) => { + this.setState({ + alertDelete: true, + idDelete: id + }) + } + + handleDeleted = async (param) => { + const { idDelete } = this.state + + if (param === "delete") { + const result = await axios.delete(`${BASE_URL}/group-sales/${idDelete}/delete`) + .then((response) => response) + .catch((error) => error.response) + this.setState({ + alertDelete: false, + alert: true + }) + await this.getDataGroupSales() + } else { + this.setState({ + alertDelete: false + }) + } + } + + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }) + } + + toggleDropDown = () => { + this.setState(prevState => ({ splitButtonOpen: !prevState.splitButtonOpen })) + } + + toggle = (param) => { + // console.log(`ee`, param) + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } + } + render() { + const { openDialog, data, search, alert, page, totalPage, rowsPerPage, currentPage, searchDetail, searchDetailField, splitButtonOpen, tooltipEdit, tooltipDelete } = this.state + // console.log(`page`, page) + let noSeq = 0; + return ( +
+ + this.setState({ alert: false })} onCancel={() => this.setState({ alert: false })}> + You clicked the button! + + this.handleDeleted("delete")} + onCancel={() => this.handleDeleted("cancel")} + focusCancelBtn + > + Data group sales akan terhapus!! + + + + +

Group Sales

+ +
+ + + + + + + this.setState({ + searchDetail: "All", + searchDetailField: "name" + })}>All + this.setState({ + searchDetail: "Name", + searchDetailField: "name" + })}>Name + this.setState({ + searchDetail: "Description", + searchDetailField: "description" + })}>Description + + + + + + + + {column.map((i) => { + return ( + + ) + })} + + + + {data.map((n) => { + return ( + + + + + + ) + })} + +
Actions{i.name}
+ {/* delete */} + this.onDelete(n.id)}> + this.toggle("delete")}> + Delete + + + {/* edit */} + this.handleEditData(n)}> + this.toggle("edit")}> + Edit + + {/* this.handleDetail(n)}> */} + {n.name}{n.description}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterKaryawan/DialogForm.js b/src/views/Master/MasterKaryawan/DialogForm.js new file mode 100644 index 0000000..d461e77 --- /dev/null +++ b/src/views/Master/MasterKaryawan/DialogForm.js @@ -0,0 +1,590 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input, FormText } from 'reactstrap'; +import Select from 'react-select' +import axios from 'axios'; +import moment from 'moment'; +import { DatePicker } from 'antd'; +import 'antd/dist/antd.css'; +import { BASE_URL_GEOHR_API2 } from '../../../const/ApiConst' +import '../MasterDataStyles.css' +import SweetAlert from 'react-bootstrap-sweetalert'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; + + +const format = 'YYYY-MM-DD'; +// const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; +let countError = 0; +const listGender = [ + { + "value": "L", + "label": "Laki-laki" + }, + { + "value": "P", + "label": "Perempuan" + }] + + const listBlood = [ + { + "value": "A", + "label": "A" + }, + { + "value": "AB", + "label": "AB" + }, + { + "value": "B", + "label": "B" + }, + { + "value": "O", + "label": "O" + }] + + const API = `https://oslog.id/geohr-api` + + + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + idDivision:0, + idRoles:0, + name:"", + email:"", + phoneNumber:"", + username:"", + password:"", + address:"", + birthPlace: "", + birthDate: null, + currentSelectGender: null, + hobby: "", + openDialog: false, + isParentClick: false, + currentSelectVal: null, + dataDivision:[], + listDivisionSelect:[], + selectDisable: false, + dataRoles:[], + listRolesSelect:[], + selectDisableRoles: false, + currentSelectRoles: null, + currentSelectBlood: null, + selectDisableBlood: false, + nik: "", + files: [], + showImg: false, + getUrl: "", + namePhoto: "", + alertDelete: false, + idDelete: 0 , + dataPhoto: null + + + } + } + + fileSelectedHandler = (e) => { + this.setState({ files: [...this.state.files, ...e.target.files] }, () => { + console.log('test data foto', this.state.files) + }) + } + + + async componentDidMount(){ + // this.getDataDivision(); + this.getDataRoles() + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (prevProps, prevState){ + const { dataEdit } = this.props + + if(this.state.isParentClick===true){ + if(this.props.typeDialog==="Edit" || this.props.typeDialog==="View"){ + // console.log('[MasterKaryawan] dataEdit', dataEdit); + // if(dataEdit.employee_division_id!=="" && dataEdit.employee_division_id!==null){ + // this.searchDivision(dataEdit.employee_division_id); + // } + + if(dataEdit.role_id!=="" && dataEdit.role_id!==null && dataEdit.role_id!==undefined){ + this.searchRoles(dataEdit.role_id); + } + + this.getUrlImagery(dataEdit.id) + + this.setState({ + id:dataEdit.id, + idRoles: dataEdit.role_id, + idDivision:dataEdit.employee_division_id, + username:dataEdit.username || "", + name:dataEdit.name || "", + email:dataEdit.email || "", + password:"", + phoneNumber:dataEdit.phone_number || "", + address:dataEdit.address || "", + birthPlace: dataEdit.birth_place || "", + birthDate: dataEdit.birth_date || null, + currentSelectGender: dataEdit.gender ? listGender.find((x) => x.value === dataEdit.gender) : null, + hobby: dataEdit.hobby || "", + nik: dataEdit.ktp_number || "", + currentSelectBlood: dataEdit.blood_type ? listBlood.find((x) => x.value === dataEdit.blood_type) : null, + }) + }else{ + this.setState({ + id:0, + idDivision:0, + idRoles:0, + name:"", + email:"", + password:"", + phoneNumber:"", + address:"", + username:"", + birthPlace: "", + birthDate: null, + currentSelectGender: null, + hobby: "", + currentSelectVal:null, + currentSelectRoles:null, + nik: "", + currentSelectBlood: null, + files: [] + }) + } + this.setState({isParentClick:false}); + } + } + + + + closeImg = () => { + this.setState({ showImg: false }) + } + + getUrlImagery = async (param) => { + const payload = { + "paging": { "start": 0, "length": -1 }, + "columns": [ + { "name": "category", "logic_operator": "=", "value": "employee", "operator": "and" }, + { "name": "ref_id", "logic_operator": "=", "value": param.toString(), "operator": "and" } + ], + "orders": { "columns": ["id"], "ascending": true } + } + const resultUrl = await axios.post(`${API}/image/search`, payload).then(response => response).catch(err => err.response) + this.setState({ files: resultUrl.data.data }) + console.log(`resultUrl`, resultUrl.data.data) + + } + + getDataDivision = async () => { + countError++; + let url = BASE_URL_GEOHR_API2+`/employee_devision.php?act=get_data`; + + const result = await axios + .get(url) + .then(res => res) + .catch((error) => error.response ); + console.log('cek', result) + + if(result && result.data && result.data.code_status === 200){ + this.setState({ dataDivision:result.data.data },()=>{ + this.setDataDivision(); + }) + }else{ + if(countError<6){ + this.getDataDivision(); + } + } + + } + + getDataRoles = async () => { + countError++; + let url = BASE_URL_GEOHR_API2+`/roles.php?act=get_data`; + + const result = await axios + .get(url) + .then(res => res) + .catch((error) => error.response ); + console.log('cek', result) + + if(result && result.data && result.data.code_status === 200){ + this.setState({ dataRoles:result.data.data },()=>{ + this.setDataRoles(); + }) + }else{ + if(countError<6){ + this.getDataRoles(); + } + } + } + + setDataDivision = () => { + const { dataDivision } = this.state + const listDivision = [] + dataDivision.map((val, index)=> { + listDivision.push({ + value:val.id, + label:val.name + }) + }) + this.setState({ listDivisionSelect:listDivision }) + } + + setDataRoles = () => { + const { dataRoles } = this.state + const listRoles = [] + dataRoles.map((val, index)=> { + listRoles.push({ + value:val.id, + label:val.name + }) + }) + this.setState({ listRolesSelect:listRoles }) + } + + + searchDivision = (id) => { + let getIndex = this.getIndexDataDivision(id); + let data = this.state.dataDivision[getIndex] + this.setState({ idDivision:data.id,currentSelectVal:{value:data.id,label:data.name} }) + } + + getIndexDataDivision = (val) => { + let index = this.state.dataDivision.findIndex(obj => obj.id === val); + return index + } + + searchRoles = (id) => { + let getIndex = this.getIndexDataRoles(id); + let data = this.state.dataRoles[getIndex] + this.setState({ idRoles:data.id,currentSelectRoles:{value:data.id,label:data.name} }) + } + + getIndexDataRoles = (val) => { + let index = this.state.dataRoles.findIndex(obj => obj.id === val); + return index + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + handleDeletePhoto = (data) => { + this.setState({ alertDelete: true, dataPhoto: data }, () => { + console.log('data photo', this.state.dataPhoto) + }); + } + + + handleSave = async () => { + const { + id, + idDivision, + username, + name, + email, + password, + address, + phoneNumber, + idRoles, + birthPlace, + birthDate, + currentSelectGender, + hobby, + currentSelectBlood, + nik, + files, + currentSelectRoles + } = this.state + + let data = ''; + if(this.props.typeDialog==="Save"){ + data = { + idDivision, + username, + name, + email, + password, + address, + phoneNumber, + idRoles, + birthPlace, + birthDate, + currentSelectGender, + hobby, + nik, + currentSelectBlood, + files, + currentSelectRoles + } + + + this.props.closeDialog('save', data); + }else{ + data = { + id, + username, + name, + email, + password, + address, + phoneNumber, + idDivision, + idRoles, + birthPlace, + birthDate, + currentSelectGender, + hobby, + nik, + currentSelectBlood, + currentSelectRoles + + } + + console.log('test log data', data) + + //handle edit foto + if (files.length > 0) { + const formData = new FormData() + files.map((res) => { + if (res) + formData.append("ref_id", id); + formData.append("files", res); + }) + await axios.post(`${API}/image/employee/upload`, formData).then(response => response).catch(err => err.response) + } + + this.props.closeDialog('edit', data); + } + + + this.setState({ id:0,idDivision:0,currentSelectVal:null, currentSelectRoles: null, currentSelectGender: null, currentSelectBlood: null }); + + } + + handleCancel = () => { + this.setState({ id:0,currentSelectVal:null, currentSelectRoles: null, currentSelectGender: null, currentSelectBlood: null }); + this.props.closeDialog('cancel', 'none') + } + + handleSelectGs = (inputValue, actionMeta) => { + this.setState({ idDivision:inputValue.value,currentSelectVal:{ value:inputValue.value,label:inputValue.label } }) + } + + handleSelectRoles = (inputValue, actionMeta) => { + this.setState({ idRoles:inputValue.value,currentSelectRoles:{ value:inputValue.value,label:inputValue.label } }, ()=> { + console.log("test select role", this.state.currentSelectRoles) + }) + } + + onConfirmDeletePhoto = async () => { + const { id, dataPhoto } = this.state + let url = `https://oslog.id/geohr-api/image/employee/${id}/delete?files=${dataPhoto.image}` + let result = await axios.delete(url) + .then(res => res) + .catch((error) => error.response); + + if(result.message!==undefined){ + + }else{ + + } + console.log("test data photooo", result) + + if (result) { + this.getUrlImagery(id) + + this.setState({ dataPhoto: null, alertDelete: false }) + NotificationManager.success('Foto berhasil dihapus!!', 'Success!!'); + } else { + this.getUrlImagery(id) + + this.setState({ dataPhoto: null, alertDelete: false }) + NotificationManager.error('Foto gagal dihapus!!', 'Failed!!'); + } + } + + + renderForm = () => { + const {files} = this.state + const {typeDialog} = this.props + let typeRole = localStorage.getItem('role_name') + + // console.log("test file", files) + return( +
+ + + {/* + + this.setState({username:e.target.value})} placeholder="" /> + + + + + + this.setState({password:e.target.value})} placeholder="" /> + {this.props.typeDialog==="Save" ? "" :

*Kosongkan jika tidak ada perubahaan

} + +
+ +
+ + + this.setState({nik:e.target.value})} placeholder="" /> + + + + + this.setState({name:e.target.value})} placeholder="" /> + + + + + this.setState({email:e.target.value})} placeholder="" /> + + + + + this.setState({phoneNumber:e.target.value})} placeholder="" /> + + + + {typeDialog !== "View" && ( + <> + + + Max Size 1Mb + + )} + {files && ( +
+ {files.map((item) => { + // this.setUrlPhotos(item) + let checkImg = null + if (item.image) { + checkImg = `${API}/assets/images/employee/${item.image}` + } else { + checkImg = URL.createObjectURL(item) + } + return ( +
+ {typeDialog === "Edit" && ( +
this.handleDeletePhoto(item)} style={{cursor: "pointer", position: "absolute", width: 30, height: 30, borderRadius: 30 / 2, backgroundColor: "grey", marginTop: 10, marginLeft: 10, justifyContent: "center", alignContent: "center", paddingTop: 4, paddingLeft: 5 }}> + +
+ ) + } + ... +
+ ) + })} +
+ ) + } +
+ + + + {typeRole === "Superadmin" && ( + + + this.setState({currentSelectGender: {value: inputValue.value, label: inputValue.label}})} value={this.state.currentSelectGender} isDisabled={typeDialog === "View"} /> + + + + + this.setState({birthPlace:e.target.value})} placeholder="" /> + + + + + + + + this.setState({birthDate: val})} defaultValue={this.state.birthDate ? moment(this.state.birthDate, format) : null} /> + + + + + + + + + this.setState({address:e.target.value})} placeholder="" /> + + + + + this.setState({hobby:e.target.value})} placeholder="" /> + + + + +
+ ) + } + + render() { + const {typeDialog} = this.props + return ( + + + + this.setState({ alertDelete: false, dataPhoto: null})} + focusCancelBtn + > + Foto Akan Terhapus!! + + this.handleCancel()}>{this.props.typeDialog} Karyawan + + {this.renderForm()} + + {typeDialog !== "View" && ( + + + {' '} + + + )} + + ) + } +} diff --git a/src/views/Master/MasterKaryawan/DialogImport.js b/src/views/Master/MasterKaryawan/DialogImport.js new file mode 100644 index 0000000..d9fbed4 --- /dev/null +++ b/src/views/Master/MasterKaryawan/DialogImport.js @@ -0,0 +1,80 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import moment from 'moment'; +import 'antd/dist/antd.css'; + +export default class DialogImport extends Component { + constructor(props) { + super(props) + this.state = { + dataImport:null, + openDialog: false, + isParentClick: false, + disable: false + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + this.setState({isParentClick:false}); + } + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + const { + dataImport + } = this.state + + + this.props.closeDialog('import', dataImport); + this.setState({ dataImport:null }); + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + importExcel = (e) => { + e.preventDefault(); + let file = e.target.files[0]; + this.setState({dataImport:file}); + // console.log("test file", file); + } + + renderForm = () => { + return( +
+ + + + +
+ ) + } + + render() { + return ( + + Import Data Karyawan + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterKaryawan/index.js b/src/views/Master/MasterKaryawan/index.js new file mode 100644 index 0000000..221ca90 --- /dev/null +++ b/src/views/Master/MasterKaryawan/index.js @@ -0,0 +1,639 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input,InputGroup, InputGroupButtonDropdown, DropdownToggle, DropdownItem, DropdownMenu,ButtonDropdown } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import DialogImport from './DialogImport'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +import { Tooltip } from 'reactstrap'; +import * as XLSX from 'xlsx'; +import { BASE_URL_GEOHR_API2 } from '../../../const/ApiConst' +import { Icon, InlineIcon } from '@iconify/react'; +import eyeFilled from '@iconify/icons-ant-design/eye-filled'; +import { Link } from 'react-router-dom'; + +const id_org = window.localStorage.getItem('id_org'); +const roleName = window.localStorage.getItem('role_name'); + + +// const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; + +// "Authorization": `Bearer ${token}` + +// const config = { +// headers: { +// 'Content-Type': 'application/json', +// 'Origin':'http://localhost:3000/', +// 'Accept':'application/json' +// } +// } + +const momentFormat = 'HH:mm'; + +const column = [ + { name: "Organasi" }, + { name: "Nama" }, + { name: "Email" }, + { name: "Nomor Handphone" }, +] + +const LENGTH_DATA = 10 + +const API = `https://oslog.id/geohr-api` + + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + finalData: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + splitButtonOpen:false, + searchDetail: "Nama", + searchDetailField: "name", + tooltipDetail: false, + tooltipTambah:false, + tooltipImport:false, + tooltipExport:false, + dataImport: null, + aksiDropdown:false, + openDialogImport:false, + dataExport:false, + } + } + + async componentDidMount() { + this.getDataEmployee(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataEmployee() + } + + toggleAksiDropdown = () => { + this.setState({aksiDropdown:!this.state.aksiDropdown}); + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataEmployee = async () => { + let start = 0; + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + let url = BASE_URL_GEOHR_API2 + `/employee.php?act=get_data&start=${start}&length=${this.state.rowsPerPage}&role_name=${roleName}`; + const formData = new FormData(); + formData.append("id_org", id_org); + formData.append('field', this.state.searchDetailField); + formData.append('value', this.state.search); + // console.log("test", url); + const result = await axios + .post(url,formData) + .then(res => res) + .catch((error) => error.response); + // console.log("result", result.data.data); + if (result && result.data && result.statusText == "OK") { + this.setState({ dataTable: result.data.data, totalPage: result.data.total_record }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + setListIdHo = (data) => { + let list = []; + data.map((val, index) => { + list.push(val.sales) + }) + this.setState({ dataIdHo: list }, () => { + // console.log(this.state.dataIdHo) + }) + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = async (type, data) => { + let result = false; + if (type === "save") { + result = await this.saveEmployee(data); + } else if (type === "edit") { + result = await this.editEmployee(data); + } + else if (type === "cancel") { + result = true; + } + + if (result) { + this.setState({ openDialog: false }) + } + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmDelete = async () => { + const { idDelete } = this.state + let url = BASE_URL_GEOHR_API2 + `/employee.php?act=delete&id=${idDelete}`; + let result = await axios.get(url) + .then(res => res) + .catch((error) => error.response); + + if(result.message!==undefined){ + + }else{ + + } + + if (result.data.message === "Data Has Been Deleted") { + this.getDataEmployee() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success('Data employee berhasil dihapus!!', 'Success!!'); + } else { + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error('Data employee gagal dihapus!!', 'Failed!!'); + } + } + + saveEmployee = async (data) => { + let url = BASE_URL_GEOHR_API2 + "/employee.php?act=input"; + const formData = new FormData(); + formData.append('name', data.name); + formData.append('username', data.username); + formData.append('phone_number', data.phoneNumber); + formData.append('email', data.email); + formData.append('address', data.name); + formData.append('password', data.password); + formData.append('birth_place', data.birthPlace); + formData.append('birth_date', data.birthDate); + formData.append('gender', data.currentSelectGender ? data.currentSelectGender.value : ""); + formData.append('hobby', data.hobby); + formData.append('blood_type', data.currentSelectBlood ? data.currentSelectBlood.value: ""); + formData.append('ktp_number', data.nik); + formData.append('role_id', data.currentSelectRoles ? data.currentSelectRoles.value: ""); + + + // if(data.idDivision !==0 && data.idDivision!==""){ + // formData.append('employee_division_id', data.idDivision); + // } + + if(data.idRoles!==undefined && data.idRoles!==0 && data.idRoles!==null){ + formData.append('roles_id', data.idRoles); + } + + let result = await axios.post(url, formData) + .then(res => res) + .catch((error) => error.response); + + if (!result) { + return false; + } + + if(result && result.message!==undefined){ + if (result.message === "Data Has Been Saved") { + this.getDataEmployee(); + NotificationManager.success('Data employee berhasil ditambahkan!!', 'Success!!'); + return true; + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + return false; + } + }else{ + if (result.data.message === "Data Has Been Saved") { + this.getDataEmployee(); + NotificationManager.success('Data employee berhasil ditambahkan!!', 'Success!!'); + return true; + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + return false; + } + } + + let ref_id = parseInt(result.data.data.id) + if (ref_id) { + if (data.files.length > 0) { + const formData = new FormData() + data.files.map((res) => { + console.log('test data upload photo', res) + if (res) + formData.append("ref_id", ref_id); + formData.append("files", res); + }) + await axios.post(`${API}/image/employee/upload`, formData).then(response => response).catch(err => err.response) + } + } + + + + } + + editEmployee = async (data) => { + let url = BASE_URL_GEOHR_API2 + `/employee.php?act=edit&id=${data.id}&role_name=${roleName}`; + const formData = new FormData(); + formData.append('name', data.name); + formData.append('username', data.username); + formData.append('phone_number', data.phoneNumber); + formData.append('email', data.email); + formData.append('address', data.address); + formData.append('birth_place', data.birthPlace); + formData.append('birth_date', data.birthDate); + formData.append('gender', data.currentSelectGender ? data.currentSelectGender.value : ""); + formData.append('hobby', data.hobby); + formData.append('blood_type', data.currentSelectBlood ? data.currentSelectBlood.value: ""); + formData.append('role_id', data.currentSelectRoles ? data.currentSelectRoles.value: ""); + + formData.append('ktp_number', data.nik); + + + if(data.password!==""){ + formData.append('password', data.password); + } + + if(data.idRoles!==undefined && data.idRoles!==0 && data.idRoles!==null){ + formData.append('role_id', data.idRoles); + } + + // if(data.idDivision !==0 && data.idDivision!==""){ + // formData.append('employee_division_id', data.idDivision); + // } + + // console.log('test form data', formData) + + let result = await axios.post(url, formData) + .then(res => res) + .catch((error) => error.response); + + // console.log("test result employee", result) + if (!result) { + return false; + } + + if(result && result.message!==undefined){ + if (result.message === "Data Has Been Edited") { + this.getDataEmployee(); + NotificationManager.success('Data karyawan berhasil diedit!!', 'Success!!'); + return true; + } else { + NotificationManager.error('Data karyawan gagal diedit!!', 'Failed!!'); + return false + } + }else{ + if (result.data.message === "Data Has Been Edited") { + this.getDataEmployee(); + NotificationManager.success('Data karyawan berhasil diedit!!', 'Success!!'); + return true; + } else { + NotificationManager.error('Data karyawan gagal diedit!!', 'Failed!!'); + return false; + } + } + } + + + handleEdit = (data) => { + this.setState({ dataEdit: data }); + this.handleOpenDialog('Edit'); + } + + handleDetail = (data) => { + this.setState({ dataEdit: data }) + this.handleOpenDialog('View'); + + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataEmployee(); + }) + + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataEmployee(); + }) + } + + toggle = (param) => { + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "detail") { + this.setState(prevState => ({ tooltipDetail: !prevState.tooltipDetail })) + } else if (param === "tambah") { + this.setState(prevState => ({ tooltipTambah: !prevState.tooltipTambah })) + } else if (param === "import") { + this.setState(prevState => ({ tooltipImport : !prevState.tooltipImport })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + + } + + toggleDropDown = () => { + this.setState({splitButtonOpen:!this.state.splitButtonOpen}) + } + + handleDialogImport = () => { + this.setState({ openDialogImport: true}) + this.showImportDialog(); + } + + handleCloseImport = (type, file) => { + if(type==="import"){ + if(!file){ + NotificationManager.error('Silahkan masukan file excel!!', 'Failed!!'); + return false + } + + if (/\.(xls?x)$/i.test(file.name) === false ) { + NotificationManager.error('File yang dimasukan bukan format excel (.xsl/.xslx)!!', 'Failed!!'); + return false + } + + this.importExcel(file); + // console.log("cek file import", file); + + }else{ + this.setState({ openDialogImport:false }); + } + } + + toggleImportDialog = () => { + this.setState({ openDialogImport:!this.state.openDialogImport }) + } + + importExcel = async (file) => { + const url = `${BASE_URL_GEOHR_API2}/employee.php?act=import`; + const formData = new FormData(); + formData.append('file', file); + const result = await axios + .post(url, formData) + .then((res) => res) + .catch((error) => error.response); + + // console.log('result import excel sales order', result); + if(result && result.code_status){ + if (result.code_status === 200) { + // NotificationManager.success("Add Import Excel", "Success message"); + NotificationManager.success('File imported successfully', 'Success Message'); + this.getDataEmployee(); + this.setState({openDialogImport: false}); + } else { + NotificationManager.error(result.data.message, "error message"); + } + }else{ + NotificationManager.error("Import data karyawan gagal!!", "error message"); + } + + } + + getTemplateExcel = async (event) => { + event.preventDefault(); + const url = `${BASE_URL_GEOHR_API2}/dok/template-import-karyawan.xlsx`; + // console.log(`url`, url); + window.open(url, "_blank"); + }; + + + handleExportExcel = async () => { + let start = 0; + let end = "ALL"; + let url = BASE_URL_GEOHR_API2 + `/employee.php?act=get_data&start=${start}&length=${end}&role_name=${roleName}`; + const formData = new FormData(); + formData.append('field', this.state.searchDetailField); + formData.append('value', this.state.search); + // console.log("test", url); + const result = await axios + .post(url,formData) + .then(res => res) + .catch((error) => error.response); + // console.log("result", result); + if (result && result.data && result.statusText == "OK") { + const dataRes = result.data.data|| []; + // console.log("data result", dataRes); + const dataExport = []; + // {n.division_name} + // {n.name} + // {n.email} + // {n.phone_number} + dataRes.map((val,index)=> { + let row = { + Nama:val.name, + Organisasi:val.organization_name || "-", + Nik:val.ktp_number, + Username:val.username, + Email:val.email, + "Nomor Handphone":val.phone_number, + "Tanggal Lahir":val.birth_date, + "Tempat Lahir":val.birth_place, + "Hobby":val.hobby, + "Jenis Kelamin":val.gender, + "Golongan Data":val.blood_type, + } + dataExport.push(row); + }) + // console.log("data Export", dataExport); + this.setState({ dataExport:dataExport },()=> { + this.exportExcel(); + }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + exportExcel = () => { + const dataExcel = this.state.dataExport || []; + const fileName = "Data Karyawan.xlsx"; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'Data Karyawan'); + + XLSX.writeFile(wb, fileName); + } + + + render() { + const { tooltipTambah,tooltipImport,tooltipExport,tooltipDetail, dataTable, searchDetail, splitButtonOpen, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete } = this.state + let dataTable2 = dataTable || []; + return ( +
+ + this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data karyawan akan terhapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + this.toggleImportDialog} + showDialog={showDialog => this.showImportDialog = showDialog} + /> + + + + +

Karyawan

+ + + + + + + + this.setState({ + searchDetail: "Nama", + searchDetailField: "name" + })}>Nama + this.setState({ + searchDetail: "Nomor Handphone", + searchDetailField: "phone_number" + })}>Nomor Handphone + this.setState({ + searchDetail: "Email", + searchDetailField: "email" + })}>Email + this.setState({ + searchDetail: "Alamat", + searchDetailField: "address" + })}>Alamat + + + + + + + this.toggleAksiDropdown()}> + + + + + this.handleDialogImport()}>Import Data Karyawan + this.getTemplateExcel(e)}>Download Template + + + + this.toggle("tambah")}> + Tambah + + this.toggle("import")}> + Import Excel + + this.toggle("export")}> + Export Excel + + +
+
+ + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataTable2.map((n) => { + return ( + + + + + + + + ) + })} + +
Aksi{i.name}
+ this.handleDelete(n.id)}> + this.toggle("delete")}> + Hapus + + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + this.handleDetail(n)}> + + + + + this.toggle("detail")}> + Detail + + {n.organization_name===null ? n.chairman_org || "-" : n.organization_name || "-"}{n.name}{n.email || "-"}{n.phone_number || "-"}
+ +
+
+
+ ) + } +} + diff --git a/src/views/Master/MasterLembur/DialogForm.js b/src/views/Master/MasterLembur/DialogForm.js new file mode 100644 index 0000000..affc26f --- /dev/null +++ b/src/views/Master/MasterLembur/DialogForm.js @@ -0,0 +1,268 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Select from 'react-select' +import axios from 'axios'; +import moment from 'moment'; +import { DatePicker } from 'antd'; +import 'antd/dist/antd.css'; + + +const { RangePicker } = DatePicker; +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + name:"", + status:"", + permStatus:"", + description:"", + employee_id:0, + openDialog: false, + isParentClick: false, + listEmployeeSelect:[], + currentSelectVal: null, + supervisor_id:0, + listSupervisorSelect:[], + currentSelectSupervisor:null, + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + console.log("cek employee", this.props.dataEmployee) + if(this.props.typeDialog==="Edit"){ + const { dataEdit } = this.props + if(dataEdit.employee_id){ + this.setSelectParent(dataEdit.employee_id); + this.searchEmployee(dataEdit.employee_id); + } + + if(dataEdit.supervisor_id){ + this.setSelectSupervisor(dataEdit.supervisor_id); + this.searchSupervisor(dataEdit.supervisor_id); + } + + this.setState({ + id:dataEdit.id, + name:dataEdit.name, + status:dataEdit.status, + permStatus:dataEdit.status, + description:dataEdit.description, + startDate:moment(moment(dataEdit.start_date).format("YYYY-M-D")), + endDate:moment(moment(dataEdit.end_date).format("YYYY-M-D")), + employee_id:dataEdit.employee_id, + supervisor_id:dataEdit.supervisor_id + }) + }else{ + this.setSelectParent("None"); + this.setSelectSupervisor("None"); + this.setState({ + id:0, + employee_id:0, + supervisor_id:0, + description:"", + status:"Waiting Approval", + permStatus:"Waiting Approval", + currentSelectSupervisor:null, + currentSelectVal:null, + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + }); + } + this.setState({isParentClick:false}); + } + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + setSelectParent = (id) => { + const { dataEmployee } = this.props + const listEmployee = [] + if(id==="None"){ + console.log("cek the set select") + dataEmployee.map((val, index)=> { + listEmployee.push({ + value:val.id, + label:val.name + }) + }) + }else{ + console.log("cek the set select edit") + dataEmployee.map((val, index)=> { + if(val.id!==id){ + listEmployee.push({ + value:val.id, + label:val.name + }) + } + }) + } + this.setState({ listEmployeeSelect:listEmployee }) + } + + searchEmployee = (id) => { + let getIndex = this.getIndexDataEmployee(id); + let data = this.props.dataEmployee[getIndex] + this.setState({ employee_id:data.id,currentSelectVal:{value:data.id,label:data.name} }) + } + + setSelectSupervisor = (id) => { + const { dataEmployee } = this.props + const listSupervisor = [] + if(id==="None"){ + // console.log("cek the set select") + dataEmployee.map((val, index)=> { + listSupervisor.push({ + value:val.id, + label:val.name + }) + }) + }else{ + // console.log("cek the set select edit") + dataEmployee.map((val, index)=> { + if(val.id!==id){ + listSupervisor.push({ + value:val.id, + label:val.name + }) + } + }) + } + this.setState({ listSupervisorSelect:listSupervisor }) + } + + searchSupervisor = (id) => { + let getIndex = this.getIndexDataEmployee(id); + let data = this.props.dataEmployee[getIndex] + this.setState({ supervisor_id:data.id,currentSelectSupervisor:{value:data.id,label:data.name} }) + } + + getIndexDataEmployee = (val) => { + let index = this.props.dataEmployee.findIndex(obj => obj.id === val); + return index + } + + handleSave = () => { + const { + id, + employee_id, + supervisor_id, + startDate, + endDate, + status, + description + } = this.state + + let data = ''; + if(this.props.typeDialog==="Save"){ + data = { + employee_id, + supervisor_id, + start_date:startDate, + end_date:endDate, + status, + description + } + this.props.closeDialog('save', data); + }else{ + data = { + id, + employee_id, + supervisor_id, + start_date:startDate, + end_date:endDate, + status, + description + } + this.props.closeDialog('edit', data); + } + + this.setState({ id:0 }); + + } + + handleCancel = () => { + this.setState({ + id:0, + employee_id:0, + supervisor_id:0, + status:"Waiting Approval", + permStatus:"Waiting Approval", + currentSelectSupervisor:null, + currentSelectVal:null, + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + description:"", + }); + this.props.closeDialog('cancel', 'none') + } + + handleSelectEmployee = (inputValue, actionMeta) => { + this.setState({ employee_id:inputValue.value,currentSelectVal:{ value:inputValue.value,label:inputValue.label } }) + } + + handleSelectSupervisor = (inputValue, actionMeta) => { + this.setState({ supervisor_id:inputValue.value,currentSelectSupervisor:{ value:inputValue.value,label:inputValue.label } }) + } + + handleDatePicker = (date, dateString) => { + this.setState({ startDate:date[0],endDate:date[1] }) + } + + renderForm = () => { + return( +
+ + + this.setState({status:e.target.value})}> + + + + + + + + this.setState({description:e.target.value})} /> + +
+ ) + } + + render() { + return ( + + {this.props.typeDialog=="Save" ? "Tambah" : "Edit"} Lembur + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterLembur/index.js b/src/views/Master/MasterLembur/index.js new file mode 100644 index 0000000..ed77556 --- /dev/null +++ b/src/views/Master/MasterLembur/index.js @@ -0,0 +1,433 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import * as XLSX from 'xlsx'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +import { Tooltip } from 'reactstrap'; +import { DatePicker } from 'antd'; +import moment from 'moment'; + +const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; +const { RangePicker } = DatePicker; + +const column = [ + { name: "Nama" }, + { name: "Divisi" }, + { name: "Status" }, + { name: "Atasan Langsung" }, + { name: "Tanggal Mulai" }, + { name: "Tanggal Selesai" }, + { name: "Deskripsi" }, +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + dataMenu:[], + tooltipTambah:false, + tooltipImport:false, + tooltipExport:false, + dataExport:[], + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + dataEmployee:[] + } + } + + async componentDidMount() { + this.getDataOvertime(); + this.getDataEmployee(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataOvertime() + } + + getDataEmployee = async () => { + let url = BASE_URL + `employee.php?act=get_data&role_name=superadmin`; + const result = await axios + .post(url) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.statusText == "OK") { + // console.log("result", result); + this.setState({ dataEmployee: result.data.data }); + } else { + // NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataOvertime = async () => { + let url = BASE_URL + "overtime.php?act=get_data"; + + let start = 0; + + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + const formData = new FormData(); + let dateStart = this.state.startDate; + let dateEnd = this.state.endDate; + formData.append("startDate", dateStart.format("YYYY-M-D")+" 00:00:00"); + formData.append("endDate", dateEnd.format("YYYY-M-D")+" 23:59:59"); + formData.append('start', start); + formData.append('length', this.state.rowsPerPage); + + formData.append('field', 'name'); + if(this.state.search !== ""){ + formData.append('value', this.state.search); + } + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if(result){ + if(result.data){ + if (result.data.code_status == 200) { + // console.log("menu", result.data.data) + this.setState({ dataTable: result.data.data, totalPage: result.data.total_record }); + } else { + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + } + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = (type, data) => { + if (type === "save") { + this.saveMenu(data); + } else if (type === "edit") { + this.editDataOvertime(data); + } + + this.setState({ openDialog: false }) + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmDelete = async () => { + const { idDelete } = this.state + let url = BASE_URL + `overtime.php?act=delete&id=${idDelete}`; + const result = await axios.post(url) + .then(res => res) + .catch((error) => error.response); + + if (result.data.message === "Data Has Been Deleted") { + this.getDataOvertime() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success('Data menu berhasil dihapus!!', 'Success!!'); + } else { + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error('Data menu gagal dihapus!!', 'Failed!!'); + } + } + + saveMenu = async (data) => { + let url = BASE_URL + "overtime.php?act=input"; + let startDate = moment(data.start_date).format("YYYY-M-D"); + let endDate = moment(data.end_date).format("YYYY-M-D"); + const formData = new FormData() + formData.append('employee_id', data.employee_id); + formData.append('supervisor_id', data.supervisor_id); + formData.append('description', data.description); + formData.append('status', data.status); + formData.append('start_date', startDate); + formData.append('end_date', endDate); + + const result = await axios.post(url, formData) + .then(res => res) + .catch((error) => error.response); + + console.log("cek res add ", result); + if(result && result.data && result.data.code_status===200){ + this.getDataOvertime(); + NotificationManager.success('Data lembur berhasil ditambahkan!!', 'Success!!'); + }else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + // id, + // employee_id, + // supervisor_id, + // start_date:startDate, + // end_date:endDate, + // status + + editDataOvertime = async (data) => { + let url = BASE_URL + `overtime.php?act=edit&id=${data.id}`; + let startDate = moment(data.start_date).format("YYYY-M-D"); + let endDate = moment(data.end_date).format("YYYY-M-D"); + const formData = new FormData() + formData.append('employee_id', data.employee_id); + formData.append('supervisor_id', data.supervisor_id); + formData.append('description', data.description); + formData.append('status', data.status); + formData.append('start_date', startDate); + formData.append('end_date', endDate); + + const result = await axios.post(url, formData) + .then(res => res) + .catch((error) => error.response); + + console.log("cek res", result); + if(result && result.data && result.data.code_status===200){ + this.getDataOvertime(); + NotificationManager.success('Data lembur berhasil diedit!!', 'Success!!'); + } else { + NotificationManager.error('Data lembur gagal diedit!!', 'Failed!!'); + } + } + + + handleEdit = (data) => { + this.setState({ dataEdit: data }); + this.handleOpenDialog('Edit'); + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataOvertime(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataOvertime(); + }) + } + + toggle = (param) => { + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "tambah") { + this.setState(prevState => ({ tooltipTambah: !prevState.tooltipTambah })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + } + + handleExportExcel = async () => { + let start = 0; + let end = "ALL"; + let url = BASE_URL + "overtime.php?act=get_data"; + let dateStart = this.state.startDate; + let dateEnd = this.state.endDate; + const formData = new FormData(); + formData.append("startDate", dateStart.format("YYYY-M-D")+" 00:00:00"); + formData.append("endDate", dateEnd.format("YYYY-M-D")+" 23:59:59"); + formData.append('start', start); + formData.append('length', end); + formData.append('field', 'name'); + if(this.state.search !== ""){ + formData.append('value', this.state.search); + } + // console.log("test", url); + const result = await axios + .post(url,formData) + .then(res => res) + .catch((error) => error.response); + // console.log("result", result); + if (result && result.data && result.statusText == "OK") { + const dataRes = result.data.data|| []; + console.log("data result", dataRes); + const dataExport = []; + // {n.employee_name} + // {n.division_name} + // {n.status} + // {n.supervisor_name} + // {n.start_date} + // {n.end_date} + // {n.description} + dataRes.map((val,index)=> { + let row = { + "Nama":val.employee_name, + "Divisi":val.division_name, + "Status":val.status, + "Atasan Langsung":val.supervisor_name, + "Tanggal Mulai": moment(val.start_date).format("YYYY-M-D"), + "Tanggal Selesai": moment(val.end_date).format("YYYY-M-D"), + "Deskripsi":val.description + } + dataExport.push(row); + }) + console.log("data Export", dataExport); + this.setState({ dataExport:dataExport },()=> { + this.exportExcel(); + }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + exportExcel = () => { + const dataExcel = this.state.dataExport || []; + const fileName = "Data Lembur.xlsx"; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'Data Lembur'); + + XLSX.writeFile(wb, fileName); + } + + handleDatePicker = (date, dateString) => { + this.setState({ startDate:date[0],endDate:date[1] },()=>{ + this.getDataOvertime(); + }) + } + + render() { + const { tooltipTambah,tooltipExport,dataTable, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data lembur akan terhapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataEmployee={this.state.dataEmployee} + /> + + +

Lembur

+ + + + + {/* */} + this.toggle("tambah")}> + Tambah Lembur + + this.toggle("export")}> + Export Excel + + + +
+ +
+
+ {' '} + +
+ +
+ + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataTable.map((n) => { + return ( + + + + + + + + + + + ) + })} + {/* + + */} + +
Aksi{i.name}
+ {n.status==="Waiting Approval" ? + <> + this.handleDelete(n.id)}> + this.toggle("delete")}> + Hapus + + + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + : "-"} + {n.employee_name}{n.division_name}{n.status}{n.supervisor_name}{n.start_date}{n.end_date}{n.description}
No Data Available
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterMenu/DialogForm.js b/src/views/Master/MasterMenu/DialogForm.js new file mode 100644 index 0000000..f3e4839 --- /dev/null +++ b/src/views/Master/MasterMenu/DialogForm.js @@ -0,0 +1,165 @@ +import React, { useEffect, useState } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input, Col, Row } from 'reactstrap'; +import { Select } from 'antd'; +import moment from 'moment'; +import 'antd/dist/antd.css'; + +const { Option } = Select + +const DialogForm = ({openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, dataMenu}) => { + const [id, setId] = useState(0) + const [name, setName] = useState('') + const [url, setUrl] = useState('') + const [aliasName, setAliasName] = useState('') + const [icon, setIcon] = useState('') + const [sequence, setSequence] = useState(0) + const [parentId, setParentId] = useState(null) + + + useEffect(()=> { + if(typeDialog==="Edit"){ + console.log("data edit", dataEdit) + setId(dataEdit.id) + setName(dataEdit.name) + setUrl(dataEdit.url) + setIcon(dataEdit.icon) + setParentId(dataEdit.parent_id) + setSequence(dataEdit.sequence) + setAliasName(dataEdit.alias_name) + }else{ + setId(0) + setName('') + setUrl('') + setIcon('') + setParentId(null) + setSequence(0) + setAliasName('') + } + },[dataEdit,openDialog]) + + const handleSave = () => { + let data = ''; + if(typeDialog==="Save"){ + data = { + name, + url, + sequence:parseInt(sequence), + icon, + alias_name:aliasName + } + + if(parentId && parentId > 0){ + data['parent_id'] = parentId + } + + closeDialog('save', data); + }else{ + data = { + id, + name, + url, + sequence: parseInt(sequence), + icon, + alias_name:aliasName + } + + if(parentId && parentId > 0){ + data['parent_id'] = parentId + } + + closeDialog('edit', data); + } + setId(0) + setName('') + setUrl('') + setIcon('') + setParentId(null) + setSequence(0) + setAliasName('') + } + + const handleCancel = () => { + closeDialog('cancel', 'none') + setId(0) + setName('') + setUrl('') + setIcon('') + setParentId(null) + setSequence(0) + setAliasName('') + } + + const onChangeParent = (val) => { + setParentId(val) + } + + const setupSelectParent = () => { + return( + <> + {dataMenu.map((val, index)=> { + return( + + ) + })} + + ) + } + + const renderForm = () => { + return( +
+ + + + + setName(e.target.value)} placeholder={`Menu..`}/> + + + + setUrl(e.target.value)} placeholder={`Url..`} /> + + + + setIcon(e.target.value)} placeholder={`Ikon..`} /> + + + + + + setSequence(e.target.value)} placeholder={`urutan..`} /> + + + + + + + + setAliasName(e.target.value)} placeholder={`Alias..`} /> + + + + +
+ ) + } + + + return ( + + {typeDialog=="Save" ? `Tambah` : "Edit"} Menu + + {renderForm()} + + + {' '} + + + + ) + +} + +export default DialogForm; \ No newline at end of file diff --git a/src/views/Master/MasterMenu/index.js b/src/views/Master/MasterMenu/index.js new file mode 100644 index 0000000..96c96d2 --- /dev/null +++ b/src/views/Master/MasterMenu/index.js @@ -0,0 +1,448 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import * as XLSX from 'xlsx'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination, Tooltip, Table } from 'antd'; +// import moment from 'moment'; +import { MENU_ADD,MENU_SEARCH,MENU_EDIT,MENU_DELETE } from '../../../const/ApiConst.js'; + +const token = window.localStorage.getItem('token'); + +const column = [ + { name: "Nama" }, + { name: "Url" }, + { name: "Ikon" }, + { name: "Alias" }, + { name: "Urutan" }, + { name: "Parent" }, +] + +const Index = ({params}) => { + const [dataTable, setDatatable] = useState([]) + const [search, setSearch] = useState('') + const [currentPage, setCurrentPage] = useState(1) + const [totalPage, setTotalPage] = useState(0) + const [openDialog, setOpenDialog] = useState(false) + const [typeDialog, setTypeDialog] = useState('Save') + const [idDelete, setIdDelete] = useState(0) + const [alertDelete, setAlertDelete] = useState(false) + const [dataEdit, setDataEdit] = useState([]) + const [rowsPerPage, setRowsPerPage] = useState(10) + const [tooltipDelete, setTooltipDelete] = useState(false) + const [tooltipEdit, setTooltipEdit] = useState(false) + const [tooltipTambah, setTooltipTambah] = useState(false) + const [tooltipExport, setTooltipExport] = useState(false) + const [clickOpenModal, setClickOpenModal] = useState(false) + const [dataExport, setDataExport] = useState([]) + const [allDataMenu, setAllDataMenu] = useState([]) + + const pageName = params.name; + + const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } + }; + + + useEffect(()=> { + // for select in form add or edit + getDataMenu(); + getDataAllMenu(); + console.log("cek config", config) + },[]) + + 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 payload = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name": "name", "logic_operator": "like", "value": "", "operator": "AND"} + ], + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + + + + const result = await axios + .post(MENU_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + setAllDataMenu(result.data.data); + }else{ + // NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + const getDataMenu = async () => { + + + let start = 0; + + if (currentPage !== 1 && currentPage > 1) { + start = (currentPage * rowsPerPage) - rowsPerPage + } + + const payload = { + "paging": {"start": start, "length": rowsPerPage}, + "columns": [ + {"name": "name", "logic_operator": "like", "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); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + setDatatable(result.data.data); + console.log("resdata", result.data.data) + setTotalPage(result.data.totalRecord); + }else{ + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + const handleOpenDialog = async (type) => { + await setTypeDialog(type) + 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 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 ditambah`, 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + } + + const editMenu = async (data) => { + console.log("cek data edit", 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 diedit`, 'Success!!'); + } else { + NotificationManager.error(`Data menu gagal di edit`, `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": "like", "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); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + let resData = result.data.data; + console.log("resdata", resData) + 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) + }) + // console.log("cek excel data", excelData) + await setDataExport(excelData); + // exportExcel(); + }else{ + NotificationManager.error('Gagal Export Data!!', 'Failed'); + } + } + + const exportExcel = () => { + const dataExcel = dataExport || []; + const fileName = `Data ${pageName}.xlsx`; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`); + + XLSX.writeFile(wb, fileName); + setDataExport([]) + } + + const cancelDelete = () => { + setAlertDelete(false) + setIdDelete(0) + } + + const renderTable = useMemo(() => { + const columns = [ + { + title: 'Action', + dataIndex: '', + key: 'x', + render: (text, record) => <> + + handleDelete(text.id)}> + + + handleEdit(text)}> + + , + }, + { title: 'Nama Menu', dataIndex: 'name', key: 'name' }, + { title: 'Url', dataIndex: 'url', key: 'url' }, + { title: 'Ikon', dataIndex: 'icon', key: 'icon' }, + { title: 'Alias', dataIndex: 'alias_name', key: 'alias_name' }, + { title: 'Urutan', dataIndex: 'sequence', key: 'sequence' }, + { title: 'Parent Menu', dataIndex: 'join_first_name', key: 'join_first_name', render: (text, record) => (text ? text : "-") } + ]; + return ( + + ) + }, [dataTable]) + + return ( +
+ + cancelDelete()} + focusCancelBtn + > + Data akan terhapus + + toggleAddDialog} + typeDialog={typeDialog} + dataEdit={dataEdit} + clickOpenModal={clickOpenModal} + dataMenu={allDataMenu} + /> + + +

{pageName}

+ +
+ + + + + + + + + + + + + + {/*
+ + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataTable.length<=0 ? + + + : null} + {dataTable.map((n, index) => { + return ( + + + + + + + + + + ) + })} + +
{`Aksi`}{i.name}
No Data Available
+ + handleDelete(n.id)}> + + + handleEdit(n)}> + + {n.name}{n.url}{n.icon}{n.alias_name}{n.sequence}{n.join_first_name ? n.join_first_name : "-"}
*/} + {renderTable} + + + +
+ ) +} + +export default Index; \ No newline at end of file diff --git a/src/views/Master/MasterOffice/DialogForm.js b/src/views/Master/MasterOffice/DialogForm.js new file mode 100644 index 0000000..b10e70f --- /dev/null +++ b/src/views/Master/MasterOffice/DialogForm.js @@ -0,0 +1,400 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input, FormText, CardImg, FormFeedback } from 'reactstrap'; +import { ListGroup, ListGroupItem } from 'reactstrap'; +import Map from './Map' +import axios from 'axios' +import { NotificationContainer, NotificationManager } from 'react-notifications'; +// import ShowImage from './ShowImage'; + + +const API = `https://oslog.id/geohr-api` + + +const formState = { + files: [], + company: "", + name: "", + employes: "", + address: "", + description: "", + geom: "", + openMap: false, + showImg: false, + getUrl: "", + namePhoto: "", + lat: "", + lon: "", + latlon: "", + buffer_radius: null, + idData: 0, + ref_id: "", + nameMessage: "", + latlonMessage: "", + companyMessage: "", + buffer_radiusMessage: "", +} + +const ERROR_LATLON = "Geom cannot be empty" +const ERROR_COMPANY = "Company cannot be empty" +const ERROR_NAME = "name cannot be empty" +const ERROR_RADIUS = "Buffer Radius cannot be empty" +export default class DialogForm extends Component { + state = { + ...formState + } + + + fileSelectedHandler = (e) => { + this.setState({ files: [...this.state.files, ...e.target.files] }, () => { + console.log('test data foto', this.state.files) + }) + } + + handleCancel = () => { + this.props.closeDialog() + this.setState({ + ...formState + }) + } + + cancelMap = () => { + this.setState({ openMap: false }) + } + handleOpenMap = () => { + this.setState({ openMap: true }) + } + + getLocation = (param) => { + console.log(`getLocation`, param) + this.setState({ + latlonMessage: param != "" ? "" : ERROR_LATLON, + lat: param.lat, + lon: param.lng, + latlon: `lat:${param.lat} lng:${param.lng}` + }) + } + + handleSave = async () => { + const err = this.handleValidation(); + const { files, company, name, employes, address, description, geom, lat, lon, idData, buffer_radius, } = this.state + const { methodAct } = this.props + + // console.log(`idData`, idData) + + if (methodAct === 'Save') { + if (!err) { + const payload = { + "company": parseInt(company), + name, + employes, + address, + description, + lat: lat === "" ? null : lat, + lon: lon === "" ? null : lon, + buffer_radius: parseInt(buffer_radius) + // geom, + // files + } + // console.log(`payload`, payload) + + const result = await axios.post(`${API}/office/add`, payload).then(response => response).catch(err => err.response) + + console.log(`result add`, result.data.data) + if (result.data.code === 200) { + this.setState({ + ...formState, + // ref_id: result.data.data.id + }) + this.props.closeDialog() + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + // handlesave foto + let ref_id = parseInt(result.data.data.id) + if (ref_id) { + if (files.length > 0) { + const formData = new FormData() + files.map((res) => { + console.log('test data upload photo', res) + if (res) + formData.append("ref_id", ref_id); + formData.append("files", res); + }) + await axios.post(`${API}/image/office/upload`, formData).then(response => response).catch(err => err.response) + } + } + } + + } else if (methodAct === 'Edit') { + if (!err) { + const payload = { + "company": parseInt(company), + name, + employes, + address, + description, + lat: lat === "" ? null : lat, + lon: lon === "" ? null : lon, + buffer_radius: parseInt(buffer_radius) + // geom, + // files + } + console.log(`payload edit`, payload) + const result = await axios.put(`${API}/office/${idData}/edit`, payload).then(response => response).catch(err => err.response) + console.log(`result edit`, result.data) + if (result.data.code === 200) { + this.setState({ + ...formState + }) + this.props.closeDialog() + } + } + } + + // handlesave edit foto + if (files.length > 0) { + const formData = new FormData() + files.map((res) => { + if (res) + formData.append("ref_id", idData); + formData.append("files", res); + }) + await axios.post(`${API}/image/office/upload`, formData).then(response => response).catch(err => err.response) + } + } + + handleChange = (e) => { + console.log(`targeeetttt`, e.target.name) + if (e.target.name === "company") { + this.setState({ companyMessage: e.target.value !== "" ? "" : ERROR_COMPANY }) + } + if (e.target.name === "name") { + this.setState({ nameMessage: e.target.value !== "" ? "" : ERROR_NAME }) + } + if (e.target.name === "buffer_radius") { + this.setState({ buffer_radiusMessage: e.target.value !== "" ? "" : ERROR_RADIUS }) + } + e.preventDefault() + this.setState({ + [e.target.name]: e.target.value, + }) + } + + setUrlPhotos = async (param) => { + let checkImg = null + if (param.image) { + checkImg = `${API}/assets/images/office/${param.image}` + } else { + checkImg = URL.createObjectURL(param) + } + this.setState({ + showImg: true, getUrl: checkImg, + // namePhoto: param.name ? param.name : param.image + }) + // console.log(`param photos`, setUrlImg) + } + + closeImg = () => { + this.setState({ showImg: false }) + } + + getUrlImagery = async (param) => { + const payload = { + "paging": { "start": 0, "length": -1 }, + "columns": [ + { "name": "category", "logic_operator": "=", "value": "office", "operator": "and" }, + { "name": "ref_id", "logic_operator": "=", "value": param.toString(), "operator": "and" } + ], + "orders": { "columns": ["id"], "ascending": true } + } + const resultUrl = await axios.post(`${API}/image/search`, payload).then(response => response).catch(err => err.response) + this.setState({ files: resultUrl.data.data }) + console.log(`resultUrl`, resultUrl.data.data) + + } + componentDidUpdate(prevProps, prevState) { + const { editData, methodAct } = this.props + // console.log(`editData`, editData) + if ((prevProps.editData !== editData) && (methodAct == "Edit" || methodAct == "View")) { + this.getUrlImagery(editData.id) + this.setState({ + idData: editData.id, + company: editData.company, + name: editData.name, + employes: editData.employes, + address: editData.address, + description: editData.description, + namePhoto: editData, + lat: editData.lat, + lon: editData.lon, + latlon: `${editData.lat},${editData.lon}`, + buffer_radius: editData.buffer_radius, + // geom: editData.geom + }) + } + } + + handleValidation = () => { + const { files, openMap, company, name, employes, address, description, geom, showImg, getUrl, namePhoto, buffer_radius, latlon, lat, lon } = this.state + let isError = false; + const errors = { + latlonMessage: "", + companyMessage: "", + nameMessage: "", + }; + if (lat === "" && lon === "") { + isError = true; + errors.latlonMessage = ERROR_LATLON; + } + if (company === "") { + isError = true + errors.companyMessage = ERROR_COMPANY + } + if (name === "") { + isError = true + errors.nameMessage = ERROR_NAME + } + if (buffer_radius === null) { + isError = true + errors.buffer_radiusMessage = ERROR_RADIUS + } + this.setState({ + latlonMessage: "", + companyMessage: "", + nameMessage: "", + buffer_radiusMessage: "", + ...errors, + }); + + return isError; + } + render() { + const { files, openMap, company, name, employes, address, description, geom, showImg, getUrl, namePhoto, buffer_radius, latlon, lat, lon, latlonMessage, companyMessage, nameMessage, buffer_radiusMessage } = this.state + const { methodAct } = this.props + console.log(`geom`, geom) + + + return ( +
+ + {/* */} + + ADD OFFICE + + + +
+ + + + + + {companyMessage && ( + {companyMessage} + )} + + + + + + + {nameMessage && ( + {nameMessage} + )} + + + + + + + + + + + {/* + + + + + */} + + + + + + + {buffer_radiusMessage && ( + {buffer_radiusMessage} + )} + + + + + + + {latlonMessage && ( + {latlonMessage} + )} + + + + + + + + + + + + + + + + + {methodAct !== "View" && ( + <> + + + Max Size 1Mb + + )} +
+ {files.map((item) => { + // this.setUrlPhotos(item) + let checkImg = null + if (item.image) { + checkImg = `${API}/assets/images/office/${item.image}` + } else { + checkImg = URL.createObjectURL(item) + } + return ( + ... + ) + })} +
+
+
+ + +
+ +
+ +
+
+ {methodAct !== "View" && ( + + {' '} + + + )} +
+
+ ) + } +} diff --git a/src/views/Master/MasterOffice/Map.js b/src/views/Master/MasterOffice/Map.js new file mode 100644 index 0000000..ba7bd92 --- /dev/null +++ b/src/views/Master/MasterOffice/Map.js @@ -0,0 +1,90 @@ +import React, { useState, useRef, useMemo, useCallback, useEffect } from 'react' +import { MapContainer, TileLayer, Marker, Popup, Polygon } from 'react-leaflet' + +const center = { + lat: -6.200000, + lng: 106.816666 +} + +const DraggableMarker = (props) => { + const { method } = props + // const center = [-6.200000, 106.816666] + let lat = props.lat + let lng = props.lng + const currentPos = [lat, lng] + console.log(`currentPos`, currentPos) + + + + // console.log(`DraggableMarker`, currentPosition) + const [draggable, setDraggable] = useState(true) + const [position, setPosition] = useState(center) + + // console.log(`position`, position) + + const markerRef = useRef(null) + const eventHandlers = useMemo( + () => ({ + dragend() { + const marker = markerRef.current + // console.log(`marker.getLatLng()`, marker.getLatLng()) + if (marker != null) { + setPosition(marker.getLatLng()) + props.getLocation(marker.getLatLng()) + } + }, + }), + [], + ) + return ( + <> + + {method === "View" && ( + + + )} + + {method === "Edit" && ( + + + )} + {method === "Save" && ( + + + )} + + ) +} + + +const purpleOptions = { color: 'purple' } +const RenderMap = (props) => { + let lat = props.lat + let lng = props.lng + // console.log(`latlon`, { lat, lng }) + const currentPos = (lat !== "" && lng !== "") ? [lat, lng] : center + // console.log(`currentPos`, currentPos) + return ( + + {/* */} + + + + ) +} +export default RenderMap; \ No newline at end of file diff --git a/src/views/Master/MasterOffice/SettingOffice.js b/src/views/Master/MasterOffice/SettingOffice.js new file mode 100644 index 0000000..c15d91b --- /dev/null +++ b/src/views/Master/MasterOffice/SettingOffice.js @@ -0,0 +1,356 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input, Modal, ModalHeader, ModalBody, ModalFooter, FormGroup, Label } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager} from 'react-notifications'; +import { Pagination } from 'antd'; +// npm install --save-dev @iconify/react @iconify-icons/ant-design +import { Icon, InlineIcon } from '@iconify/react'; +import settingOutlined from '@iconify/icons-ant-design/setting-outlined'; +import {Link} from 'react-router-dom' +import Select from 'react-select' + +import { SketchPicker } from 'react-color'; + + +const BASE_URL = "https://oslog.id/geohr-api/"; + + +const column = [ + { name: "Group Sales" }, + { name: "Name" }, + { name: "Phone Number" }, + { name: "Email" }, + { name: "Address" }, +] + +const layerOffice = [ + { + "id": 1, + "rule_name": "office yang incomenya lebih dari 20000", + "style": { + "fill_color": "#22194D", + }, + "rule": { + "column": "income", + "operator": ">", + "value": 20000, + } + }, + { + "id": 2, + "rule_name": "office kebayoran lama", + "style": { + "fill_color": "#22194D", + }, + "rule": { + "column": "name", + "operator": "like", + "value": "mohammad" + } + }, + +] + +const options = [ + { value: 'name', label: 'Name' }, + { value: 'employes', label: 'Employes' }, + { value: 'description', label: 'Description' }, + + { value: 'address', label: 'Address' } + + ] + + const logicOperator = [ + { value: '>', label: '>' }, + { value: '>=', label: '>=' }, + { value: '<', label: '<' }, + { value: '<=', label: '<=' }, + { value: '==', label: '==' }, + { value: '<>', label: '<>' }, + ] + + +const LENGTH_DATA = 10 +export default class SettingOffice extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + layersOffice: [], + modalEdit: false, + modalAdd: false, + rule_name: "", + dataEdit: null, + + id: 0, + fill_color: "", + column: "", + operator: "", + value: "" + } + } + + + + async componentDidMount (){ + this.getRuleOffice() + + } + + + async componentDidUpdate (prevProps, prevState){ + const { search } = this.state + if (search !== prevState.search) this.getDataSales() + } + + handleOpenDialog = (type) => { + this.setState({modalEdit: true, typeDialog: type }) + this.handleSetData() + console.log('test open dialog', this.state.typeDialog) +} + + handleSetData = () => { + const { dataEdit, typeDialog} = this.state + if(typeDialog === "Edit") { + this.setState({ + id: dataEdit.id, + rule_name: dataEdit.rule_name, + fill_color: dataEdit.style.fill_color, + column: dataEdit.rule.column, + value: dataEdit.rule.value, + operator: dataEdit.rule.operator + + }) + } else if (typeDialog === "Save") { + this.setState({ + id: 0, + rule_name: "", + fill_color: "#000", + column: null, + value:"", + operator: null + + }) + + } + + } + +handleEdit = (data) => { + this.setState({dataEdit: data}) + this.handleOpenDialog('Edit') + console.log('test data edit', data) + +} + +handleCancel = () => { + this.setState({ + + rule_name: "", + fill_color: "#000", + column: null, + value:"", + operator: null, + modalEdit: false + }) +} + +handleSave = () => { + this.setState({ + + rule_name: "", + fill_color: "#000", + column: null, + value:"", + operator: null, + modalEdit: false }) + +} + +handleChangeComplete = (color, event) => { + + console.log('handleChangeComplete',color, event); + this.setState({fill_color: color.hex}); +} + +toggleAdd = () => { + this.setState({modalAdd: !this.state.modalAdd}) +} + + getRuleOffice = () => { + this.setState({layersOffice: layerOffice}) + } + + render() { + const {layersOffice, modalEdit, modalAdd, search, openDialog, currentPage, rowsPerPage,totalPage, dataTable } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete:false })} + onCancel={()=> this.setState({ alertDelete:false })} + focusCancelBtn + > + Rule sales akan terhapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog=> this.showChildDialog = showDialog} + dataGs={this.state.dataGs} + /> + + +

Rules Offices

+
+ + +
+
+ + + + + {layersOffice.map((n) => { + return( + + + + + + + +
+ + + + {n.rule_name} + + +
+ + + +
+ + + + + + + + + + {this.state.typeDialog} Rule Name + +
+ + + this.setState({ rule_name :e.target.value })} /> + + + + + + + + + + + + + + + this.setState({ value :e.target.value })} /> + + + +
+
+ + {' '} + + +
+ + + + ) + }) + + } + + + + + + {/* + Add Rule Name + +
+ + + + + + + + + + + + + + + + + + + + +
+
+ + {' '} + + +
*/} + +
+ ) + } +} diff --git a/src/views/Master/MasterOffice/ShowImage.js b/src/views/Master/MasterOffice/ShowImage.js new file mode 100644 index 0000000..f87b36f --- /dev/null +++ b/src/views/Master/MasterOffice/ShowImage.js @@ -0,0 +1,29 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { + Card, CardImg, CardText, CardBody, + CardTitle, CardSubtitle, Button +} from 'reactstrap'; + +export default class ShowImage extends Component { + render() { + const { isOpen, isClose, dataImg, photoName } = this.props + return ( +
+ + {photoName} + + + + + + +
+ ) + } +} diff --git a/src/views/Master/MasterOffice/index.js b/src/views/Master/MasterOffice/index.js new file mode 100644 index 0000000..89b6bbc --- /dev/null +++ b/src/views/Master/MasterOffice/index.js @@ -0,0 +1,311 @@ +import React, { Component } from 'react' +import { Card, CardBody, CardHeader, Col, Row, Table, Input, InputGroupButtonDropdown, DropdownToggle, DropdownItem, DropdownMenu, InputGroup } from 'reactstrap'; +import { Button } from 'reactstrap'; +import DialogForm from './DialogForm' +import axios from 'axios' +import SweetAlert from 'react-bootstrap-sweetalert'; +// import { Pagination, PaginationItem, PaginationLink } from 'reactstrap'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +import { Icon, InlineIcon } from '@iconify/react'; +import settingOutlined from '@iconify/icons-ant-design/setting-outlined'; +import { Link } from 'react-router-dom'; +import { Tooltip } from 'reactstrap'; + + +const API = `https://oslog.id/geohr-api` +const column = [ + // { name: "Company" }, + { name: "Name" }, + { name: "Employes" }, + { name: "Address" }, + { name: "Description" }, +] + +const LENGTH_DATA = 10 +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + editData: "", + methodAct: "Save", + alertWarning: false, + alert: false, + idDelete: 0, + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + searchDetail: "All", + searchDetailField: "name", + splitButtonOpen: false, + tooltipEdit: false, + tooltipDelete: false, + tooltipDetail: false, + } + } + + handleOpenDialog = () => { + this.setState({ openDialog: true, methodAct: "Save" }) + } + + handleCloseDialog = () => { + this.setState({ openDialog: false }) + this.getDataOffices() + } + + getDataOffices = async (start, length) => { + const { search, searchDetail, searchDetailField } = this.state + const payload = { + "paging": { "start": start, "length": length }, + ...searchDetail === "All" && + { + "filter_columns": [ + { "name": "name", "value": search.toString() }, + { "name": "employes", "value": search.toString() }, + { "name": "address", "value": search.toString() }, + { "name": "description", "value": search.toString() }, + ], + }, + ...searchDetail !== "All" && + { + "columns": [ + { "name": searchDetailField, "logic_operator": "like", "operator": "and", "value": search.toString() } + ] + }, + // "columns": [ + // { "name": "name", "logic_operator": "like", "value": search, "operator": "and" } + // ], + "orders": { "columns": ["created_date"], "ascending": false } + } + + const result = await axios.post(`${API}/office/search`, payload) + .then((response) => response) + .catch((error) => error.response) + + if (result.data.code === 200) { + this.setState({ dataTable: result.data.data, totalPage: result.data.totalRecord }) + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + componentDidMount = async () => { + const { rowsPerPage } = this.state + await this.getDataOffices(0, rowsPerPage) + } + + async componentDidUpdate(prevProps, prevState) { + const { search, rowsPerPage } = this.state + if (search !== prevState.search) this.getDataOffices(0, rowsPerPage) + }; + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, page: 0 }) + }; + + handleEditData = (data) => { + this.setState({ editData: data, openDialog: true, methodAct: "Edit" }) + // console.log(`handleEditData`, data) + } + handleDetail = (data) => { + this.setState({ editData: data, openDialog: true, methodAct: "View" }) + // console.log(`handleEditData`, data) + } + + handleDeleted = async (param) => { + const { idDelete } = this.state + // console.log(`idDelete`, idDelete) + // console.log(`param`, param) + + if (param === "delete") { + + const result = await axios.delete(`${API}/office/${idDelete}/delete`) + .then((response) => response) + .catch((error) => error.response) + // console.log(`result delete`, result.data) + this.setState({ + alertWarning: false, + alert: true + }) + await this.getDataOffices() + } else { + this.setState({ + alertWarning: false + }) + } + } + + onDelete = (id) => { + this.setState({ + alertWarning: true, + idDelete: id + }) + } + + handleChange = (e) => { + this.setState({ + [e.target.name]: e.target.value, + }) + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }) + } + + toggleDropDown = () => { + this.setState(prevState => ({ splitButtonOpen: !prevState.splitButtonOpen })) + } + + toggle = (param) => { + // console.log(`ee`, param) + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "detail") { + this.setState(prevState => ({ tooltipDetail: !prevState.tooltipDetail })) + } + } + render() { + const { openDialog, dataTable, editData, methodAct, alertWarning, alert, search, page, currentPage, rowsPerPage, totalPage, searchDetail, splitButtonOpen, tooltipEdit, tooltipDelete, tooltipDetail } = this.state + + return ( +
+ this.setState({ alert: false })} onCancel={() => this.setState({ alert: false })}> + You clicked the button! + + this.handleDeleted("delete")} + onCancel={() => this.handleDeleted("cancel")} + focusCancelBtn + > + This will be deleted + + this.handleCloseDialog() + } + editData={editData} + methodAct={methodAct} + /> + + +

Offices

+
+ + + + + +
+
+ + + + + + + this.setState({ + searchDetail: "All", + searchDetailField: "name" + })}>All + this.setState({ + searchDetail: "Name", + searchDetailField: "name" + })}>Name + this.setState({ + searchDetail: "Employes", + searchDetailField: "employes" + })}>Employes + this.setState({ + searchDetail: "Address", + searchDetailField: "address" + })}>Address + this.setState({ + searchDetail: "Description", + searchDetailField: "description" + })}>Description + + + + + + + + {column.map((i) => { + return ( + + ) + })} + + + + {dataTable.map((n) => { + return ( + + + {/* */} + + + + + + ) + })} + +
Actions{i.name}
+ {/* delete */} + this.onDelete(n.id)}> + this.toggle("delete")}> + Delete + + {/* ediit */} + this.handleEditData(n)} > + this.toggle("edit")}> + Edit + + + {/* detail */} + this.handleDetail(n)}> + this.toggle("detail")}> + Detail + + {n.company}{n.name}{n.employes}{n.address}{n.description}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterOfficeHours/DialogForm.js b/src/views/Master/MasterOfficeHours/DialogForm.js new file mode 100644 index 0000000..0b80613 --- /dev/null +++ b/src/views/Master/MasterOfficeHours/DialogForm.js @@ -0,0 +1,727 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Select from 'react-select' +import axios from 'axios'; +import moment from 'moment'; +import { TimePicker, Checkbox, Transfer, Radio } from 'antd'; +import { API_GEOHR_EMPLOYEE_DIVISION_LIST } from '../../../const/ApiConst.js'; +import 'antd/dist/antd.css'; +import '../MasterDataStyles.css' +import '../../Map/CustomScroll.css' + +const format = 'HH:mm'; +const default_start = `${moment().format('YYYY-MM-DD')} 08:00`; +const default_end = `${moment().format('YYYY-MM-DD')} 17:00` +const BASE_URL = "https://oslog.id/geohr-api/"; + +const mockData = []; +for (let i = 0; i < 20; i++) { + mockData.push({ + key: i.toString(), + title: `content${i + 1}`, + description: `description of content${i + 1}`, + // disabled: i % 3 < 1, + }); +} + +const oriTargetKeys = mockData.filter(item => +item.key % 3 > 1).map(item => item.key); + + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + idSales:0, + openDialog: false, + isParentClick: false, + currentSelectVal: null, + dataSales:[], + dataDivision:[], + listSalesSelect:[], + selectDisable: false, + senStart:moment().format(format), + senEnd:moment().format(format), + selStart:moment().format(format), + selEnd:moment().format(format), + rabStart:moment().format(format), + rabEnd:moment().format(format), + kamStart:moment().format(format), + kamEnd:moment().format(format), + jumStart:moment().format(format), + jumEnd:moment().format(format), + sabStart:moment().format(format), + sabEnd:moment().format(format), + minStart:moment().format(format), + minEnd:moment().format(format), + allStart:moment().format(format), + allEnd: moment().format(format), + selectedGroupCheck: 'all', + dataSourceEmployee: [], + targetKeysEmployee: [], + selectedKeysEmployee: [], + selectedEmployee: [], + dataSourceDivision: [], + targetKeysDivision: [], + selectedKeysDivision: [], + selectedDivision: [] + } + } + + async componentDidMount(){ + this.getDataEmployee(); + this.getDataDivision(); + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + // console.log('isParentClick', this.state.isParentClick); + this.setState({isParentClick:false}); + this.getDataEmployee(); + this.getDataDivision(); + if(this.props.typeDialog==="Edit"){ + const { dataEdit } = this.props + this.searchSales(parseInt(dataEdit.employee_id)); + this.setState({ + id:dataEdit.id, + selectDisable:true, + allStart:moment(default_start).format(format), + allEnd:moment(default_end).format(format), + senStart:moment(dataEdit.monday_start, "hh:mm").format(format), + senEnd:moment(dataEdit.monday_end, "hh:mm").format(format), + selStart:moment(dataEdit.tuesday_start, "hh:mm").format(format), + selEnd:moment(dataEdit.tuesday_end, "hh:mm").format(format), + rabStart:moment(dataEdit.wednesday_start, "hh:mm").format(format), + rabEnd:moment(dataEdit.wednesday_end, "hh:mm").format(format), + kamStart:moment(dataEdit.thursday_start, "hh:mm").format(format), + kamEnd:moment(dataEdit.thursday_end, "hh:mm").format(format), + jumStart:moment(dataEdit.friday_start, "hh:mm").format(format), + jumEnd:moment(dataEdit.friday_end, "hh:mm").format(format), + sabStart:dataEdit.saturday_start ? moment(dataEdit.saturday_start, "hh:mm").format(format) : moment(default_start).format(format), + sabEnd: dataEdit.saturday_end ? moment(dataEdit.saturday_end, "hh:mm").format(format) : moment(default_end).format(format), + minStart: dataEdit.sunday_start ? moment(dataEdit.sunday_start, "hh:mm"). format(format) :moment(default_start).format(format), + minEnd: dataEdit.sunday_end ? moment(dataEdit.sunday_end, "hh:mm").format(format) : moment(default_end).format(format), + }) + }else{ + this.setState({ + id:0, + idSales:0, + selectDisable:false, + allStart:moment(default_start).format(format), + allEnd:moment(default_end).format(format), + senStart:moment(default_start).format(format), + senEnd:moment(default_end).format(format), + selStart:moment(default_start).format(format), + selEnd:moment(default_end).format(format), + rabStart:moment(default_start).format(format), + rabEnd:moment(default_end).format(format), + kamStart:moment(default_start).format(format), + kamEnd:moment(default_end).format(format), + jumStart:moment(default_start).format(format), + jumEnd:moment(default_end).format(format), + sabStart:moment(default_start).format(format), + sabEnd:moment(default_end).format(format), + minStart:moment(default_start).format(format), + minEnd:moment(default_end).format(format), + }) + } + } + } + + getDataEmployee = async () => { + let url = BASE_URL+"employee/search"; + const obj = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name":"name", "logic_operator": "like", "value": "", "operator": "and"} + ], + // "joins": [ + // {"name": "group_sales", "column_results": ["name", "description"]} + // ], + "orders": {"columns": ["name"], "ascending": true} + } + + const result = await axios + .post(url, obj) + .then(res => res) + .catch((error) => error.response ); + // console.log("res res", result) + if(result && result.data && result.data.message == "OK"){ + // this.setDataSales(result.data.data || []); + this.setState({ dataSales:result.data.data }, () => this.setDataSales()) + }else{ + // this.getDataEmployee(); + } + } + + getDataDivision = async () => { + const formData = new FormData(); + formData.append('start', 0); + formData.append('length', 'all'); + + // const result = await axios + // .post(API_GEOHR_EMPLOYEE_DIVISION_LIST, formData) + // .then(res => res) + // .catch((error) => error.response ); + // if(result && result.data && result.data.message == "OK"){ + // this.setState({ dataSales:result.data.data }, () => this.setDataDivision()) + // }else{ + // this.getDataEmployee(); + // } + + const result = await axios + .post(API_GEOHR_EMPLOYEE_DIVISION_LIST, formData) + .then(res => res) + .catch((error) => error.response); + // console.log('result getDataDivision', result) + + if (result && result.data && result.data.code_status === 200) { + this.setState({ dataDivision: result.data.data }, () => this.setDataDivision()); + } + else { + + } + // if(result){ + // if(result.data){ + // if (result.data.code_status == 200) { + // console.log("sales", result.data.data) + // this.setState({ dataTable: result.data.data, totalPage: result.data.total_record }); + // } else { + // NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + // } + // } + // } + } + + setDataSales = () => { + const { dataSales } = this.state + const listSales = [] + const listEmployee = [] + dataSales.map((val, index)=> { + let check = this.checkListHo(val.id); + if(check===false){ + listSales.push({ + value:val.id, + label:val.name + }) + } + listEmployee.push({ + key: index, + id: val.id, + title: val.name + }) + }) + this.setState({ listSalesSelect:listSales, dataSourceEmployee: listEmployee }) + } + + setDataDivision = () => { + const { dataDivision } = this.state + const listDivision = [] + dataDivision.map((val, index)=> { + listDivision.push({ + key: index, + id: val.id, + title: val.name + }) + }) + this.setState({ dataSourceDivision: listDivision }) + } + + checkListHo = (val) => { + return this.props.dataHs.includes(val); + } + + searchSales = (id) => { + let getIndex = this.getIndexDataSales(id); + let data = this.state.dataSales[getIndex] + this.setState({ idSales:data.id,currentSelectVal:{value:data.id,label:data.name} }) + } + + getIndexDataSales = (val) => { + let index = this.state.dataSales.findIndex(obj => obj.id === val); + return index + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + const { + id, + idSales, + senStart, + senEnd, + selStart, + selEnd, + rabStart, + rabEnd, + kamStart, + kamEnd, + jumStart, + jumEnd, + sabStart, + sabEnd, + minStart, + minEnd, + selectedGroupCheck, + selectedDivision, + selectedEmployee + } = this.state + + let data = ''; + + if(this.props.typeDialog==="Save"){ + // data = { + // employee:idSales, + // monday_start:senStart, + // monday_end:senEnd, + // tuesday_start:selStart, + // tuesday_end:selEnd, + // wednesday_start:rabStart, + // wednesday_end:rabEnd, + // thursday_start:kamStart, + // thursday_end:kamEnd, + // friday_start:jumStart, + // friday_end:jumEnd, + // saturday_start:sabStart, + // saturday_end:sabEnd, + // sunday_start:minStart, + // sunday_end:minEnd + // } + data = { + "type": selectedGroupCheck, + "schedule": { + "monday_start":senStart, + "monday_end":senEnd, + "tuesday_start":selStart, + "tuesday_end":selEnd, + "wednesday_start":rabStart, + "wednesday_end":rabEnd, + "thursday_start":kamStart, + "thursday_end":kamEnd, + "friday_start":jumStart, + "friday_end":jumEnd, + "saturday_start":sabStart, + "saturday_end":sabEnd, + "sunday_start":minStart, + "sunday_end":minEnd + } + } + + if (selectedGroupCheck === 'division') { + data.division = selectedDivision + } + else if (selectedGroupCheck === 'employee') { + data.employee = selectedEmployee + } + + this.props.closeDialog('save', data); + } + else { + data = { + id, + employee:idSales, + monday_start:senStart, + monday_end:senEnd, + tuesday_start:selStart, + tuesday_end:selEnd, + wednesday_start:rabStart, + wednesday_end:rabEnd, + thursday_start:kamStart, + thursday_end:kamEnd, + friday_start:jumStart, + friday_end:jumEnd, + saturday_start:sabStart, + saturday_end:sabEnd, + sunday_start:minStart, + sunday_end:minEnd + } + this.props.closeDialog('edit', data); + } + + this.setState({ + id:0, + idSales:0, + currentSelectVal:null, + selectedGroupCheck: 'all', + dataSourceEmployee: [], + targetKeysEmployee: [], + selectedKeysEmployee: [], + selectedEmployee: [], + dataSourceDivision: [], + targetKeysDivision: [], + selectedKeysDivision: [], + selectedDivision: [] + }); + + } + + handleCancel = () => { + this.setState({ id:0,currentSelectVal:null }); + this.props.closeDialog('cancel', 'none') + } + + handleSelectGs = (inputValue, actionMeta) => { + this.setState({ idSales:inputValue.value,currentSelectVal:{ value:inputValue.value,label:inputValue.label } }) + } + + + handleHourChangeStart = (e, val, type) => { + switch(type){ + case 'senin': + this.setState({ senStart:val }) + break; + case 'selasa': + this.setState({ selStart:val }) + break; + case 'rabu': + this.setState({ rabStart:val }) + break; + case 'kamis': + this.setState({ kamStart:val }) + break; + case 'jumat': + this.setState({ jumStart:val }) + break; + case 'sabtu': + this.setState({ sabStart:val }) + break; + default: + this.setState({ minStart:val }) + break; + } + } + + handleHourChangeEnd = (e, val, type) => { + switch(type){ + case 'senin': + this.setState({ senEnd:val }) + break; + case 'selasa': + this.setState({ selEnd:val }) + break; + case 'rabu': + this.setState({ rabEnd:val }) + break; + case 'kamis': + this.setState({ kamEnd:val }) + break; + case 'jumat': + this.setState({ jumEnd:val }) + break; + case 'sabtu': + this.setState({ sabEnd:val }) + break; + default: + this.setState({ minEnd:val }) + break; + } + } + + handleHourChangeAllStart = (e, val) => { + this.setState({ + senStart:val, + selStart:val, + rabStart:val, + kamStart:val, + jumStart:val, + sabStart:val, + minStart:val, + allStart:val, + }) + } + + handleHourChangeAllEnd = (e, val) => { + this.setState({ + senEnd:val, + selEnd:val, + rabEnd:val, + kamEnd:val, + jumEnd:val, + sabEnd:val, + minEnd:val, + allEnd:val, + }) + } + + handleTransferChangeEmployee = (nextTargetKeys, direction, moveKeys) => { + const { dataSourceEmployee } = this.state; + // select the dataSourceEmployee by nextTargetKeys array + + let selected = []; + + dataSourceEmployee.map((item, idx) => { + if (nextTargetKeys.includes(item.key)) { + selected.push(item.id) + } + }); + + // console.log('selected employee', selected); + + this.setState({ targetKeysEmployee: nextTargetKeys, selectedEmployee: selected }, () => { + // console.log('handleTransferChangeEmployee targetKeysEmployee', this.state.targetKeysEmployee); + }); + } + + handleSelectChangeEmployee = (sourceSelectedKeys, targetSelectedKeys) => { + // console.log('handleSelectChangeEmployee sourceSelectedKeys', sourceSelectedKeys); + // console.log('handleSelectChangeEmployee targetSelectedKeys', targetSelectedKeys); + this.setState({ selectedKeysEmployee: [...sourceSelectedKeys, ...targetSelectedKeys] }, () => { + // console.log('handleSelectChangeEmployee selectedKeysEmployee', this.state.selectedKeysEmployee); + }); + } + + handleTransferChangeDivision = (nextTargetKeys, direction, moveKeys) => { + const { dataSourceDivision } = this.state; + // select the dataSourceDivision by nextTargetKeys array + + let selected = []; + + dataSourceDivision.map((item, idx) => { + if (nextTargetKeys.includes(item.key)) { + selected.push(item.id) + } + }); + + // console.log('selected division', selected); + this.setState({ targetKeysDivision: nextTargetKeys, selectedDivision: selected }); + } + + handleSelectChangeDivision = (sourceSelectedKeys, targetSelectedKeys) => { + this.setState({ selectedKeysDivision: [...sourceSelectedKeys, ...targetSelectedKeys] }); + } + + renderFormSelectedGroup = () => { + const { selectedGroupCheck, + dataSourceEmployee, targetKeysEmployee, selectedKeysEmployee, + dataSourceDivision, targetKeysDivision, selectedKeysDivision } = this.state; + switch (selectedGroupCheck) { + case 'employee' : + // return ( + // + // + // +
+ + + + + + this.handleHourChangeAllStart(e, val)} value={moment(this.state.allStart, format)} allowClear={false} /> + + + {"-"} + + + this.handleHourChangeAllEnd(e, val)} value={moment(this.state.allEnd, format)} allowClear={false}/> + + + + + + + + this.handleHourChangeStart(e, val, 'senin')} value={moment(this.state.senStart, format)} allowClear={false}/> + + + {"-"} + + + this.handleHourChangeEnd(e, val, 'senin')} value={moment(this.state.senEnd, format)} allowClear={false}/> + + + + + + + + this.handleHourChangeStart(e, val, 'selasa')} value={moment(this.state.selStart, format)} allowClear={false}/> + + + {"-"} + + + this.handleHourChangeEnd(e, val, 'selasa')} value={moment(this.state.selEnd, format)} allowClear={false}/> + + + + + + + + this.handleHourChangeStart(e, val, 'rabu')} value={moment(this.state.rabStart, format)} allowClear={false}/> + + + {"-"} + + + this.handleHourChangeEnd(e, val, 'rabu')} value={moment(this.state.rabEnd, format)} allowClear={false}/> + + + + + + + + this.handleHourChangeStart(e, val, 'kamis')} value={moment(this.state.kamStart, format)} allowClear={false}/> + + + {"-"} + + + this.handleHourChangeEnd(e, val, 'kamis')} value={moment(this.state.kamEnd, format)} allowClear={false}/> + + + + + + + + this.handleHourChangeStart(e, val, 'jumat')} value={moment(this.state.jumStart, format)} allowClear={false}/> + + + {"-"} + + + this.handleHourChangeEnd(e, val, 'jumat')} value={moment(this.state.jumEnd, format)} allowClear={false}/> + + + + + + + + this.handleHourChangeStart(e, val, 'sabtu')} value={moment(this.state.sabStart, format)} allowClear={false} /> + + + {"-"} + + + this.handleHourChangeEnd(e, val, 'sabtu')} value={moment(this.state.sabEnd, format)} allowClear={false}/> + + + + + + + + this.handleHourChangeStart(e, val, 'minggu')} value={moment(this.state.minStart, format)} allowClear={false}/> + + + {"-"} + + + this.handleHourChangeEnd(e, val, 'minggu')} value={moment(this.state.minEnd, format)} allowClear={false}/> + + + + + ) + } + + render() { + return ( + + Add Office Hours Sales + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterOfficeHours/index.js b/src/views/Master/MasterOfficeHours/index.js new file mode 100644 index 0000000..028ba2f --- /dev/null +++ b/src/views/Master/MasterOfficeHours/index.js @@ -0,0 +1,471 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +import { Tooltip } from 'reactstrap'; +import { API_GET_DATA_OFFICE_HOURS,BASE_URL_GEOHR_API, API_GEOHR_SETTING_OFFICE_HOURS, API_GEOHR_EDIT_OFFICE_HOURS} from '../../../const/ApiConst' +import * as XLSX from 'xlsx'; + +// const BASE_URL = "https://oslog.id/geohr-api/"; +const id_org = window.localStorage.getItem('id_org'); +const roleName = window.localStorage.getItem('role_name'); + +const momentFormat = 'HH:mm'; + +const column = [ + { name: "Aksi" }, + { name: "Nama Karyawan" }, + { name: "Senin" }, + { name: "Selasa" }, + { name: "Rabu" }, + { name: "Kamis" }, + { name: "Jumat" }, + { name: "Sabtu" }, + { name: "Minggu" }, +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + isReady: false, + dataTable: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + tooltipTambah:false, + tooltipExport:false, + dataExport: [] + } + } + + async componentDidMount() { + this.getDataHoursOffice(); + this.getAllDataOfficeHours(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataHoursOffice() + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + + + handleExportExcel = async () => { + let url = API_GET_DATA_OFFICE_HOURS + `act=get_data&role_name=superadmin`; + + const formData = new FormData(); + formData.append("start", 0); + formData.append("length", 'All'); + formData.append('field', this.state.searchDetailField); + if(this.state.search!=="" && this.state.search!==null){ + formData.append('value', this.state.search); + } + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => { + NotificationManager.error(error.response, 'Failed'); + return false; + }); + + if (result && result.data && result.data.code_status == 200) { + this.setState({ dataExport: result.data.data },()=> { + this.exportTable(); + }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + exportTable = () => { + const dataExcel = this.state.dataExport || []; + const dataExport = []; + dataExcel.map((val) => { + let row = { + "Nama Karyawan": val.employee_name, + "Senin": moment(val.monday_start, "hh:mm").format(momentFormat) + ' - ' + moment(val.monday_end, "hh:mm").format(momentFormat), + "Selesa": moment(val.tuesday_start, "hh:mm").format(momentFormat) + ' - ' + moment(val.tuesday_end, "hh:mm").format(momentFormat), + "Rabu": moment(val.wednesday_start, "hh:mm").format(momentFormat) + ' - ' + moment(val.wednesday_end, "hh:mm").format(momentFormat), + "Kamis": moment(val.thursday_start, "hh:mm").format(momentFormat) + ' - ' + moment(val.thursday_end, "hh:mm").format(momentFormat), + "Jumat": moment(val.friday_start, "hh:mm").format(momentFormat) + ' - ' + moment(val.friday_end, "hh:mm").format(momentFormat), + "Sabtu" : "-", + "Minggu" : "-" + } + dataExport.push(row); + }); + const fileName = "Jam Kerja Karyawan.xlsx"; + const ws = XLSX.utils.json_to_sheet(dataExport); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'Jam Kerja'); + + XLSX.writeFile(wb, fileName); + } + + getDataHoursOffice = async () => { + let url = API_GET_DATA_OFFICE_HOURS + `act=get_data&role_name=${roleName}`; + + let start = 0; + + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + const formData = new FormData(); + formData.append("start", start); + formData.append("id_org", id_org); + formData.append("length", this.state.rowsPerPage); + formData.append('field', this.state.searchDetailField); + if(this.state.search!=="" && this.state.search!==null){ + formData.append('value', this.state.search); + } + + // const obj = { + // "paging": { "start": start, "length": this.state.rowsPerPage }, + // "columns": [ + // { + // "name": "name", + // "logic_operator": "like", + // "value": this.state.search.toString(), + // "operator": "and", + // "table_name": "m_employee" + // } + // ], + // "joins": [ + // { "name": "employee", "column_results": ["name", "username", "phone_number", "email", "address", "lat", "lon"] } + // ], + // "orders": { "columns": ["created_date"], "ascending": true } + // } + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => { + NotificationManager.error(error.response, 'Failed'); + return false; + }); + + // console.log('test data office hour', result) + + if (!result) { + this.setState({isReady: true}); + return; + } + + if (result && result.data && result.data.code_status == 200) { + this.setState({ dataTable: result.data.data, totalPage: result.data.total_record, isReady: true }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + this.setState({isReady: true}); + } + } + + getAllDataOfficeHours = async () => { + let url = API_GET_DATA_OFFICE_HOURS + `act=get_data&role_name=superadmin`; + + const formData = new FormData(); + formData.append("start", 0); + formData.append("length", "all"); + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => { console.log(error) }); + + if (result && result.data && result.data.code_status == 200) { + this.setListIdHo(result.data.data || []); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + setListIdHo = (data) => { + let list = []; + data.map((val, index) => { + list.push(parseInt(val.employee_id)) + }) + this.setState({ dataIdHo: list }, () => { + // console.log(this.state.dataIdHo) + }) + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = (type, data) => { + // let obj = JSON.stringify(data); + // console.log(data) + if (type === "save") { + // console.log('save', data) + this.saveOfficeHours(data); + // this.uploadImage(files); + } else if (type === "edit") { + this.editOfficeHours(data); + } + else if (type === "cancel") { + this.setState({ openDialog: false }) + } + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmDelete = async () => { + const { idDelete } = this.state + let url = BASE_URL_GEOHR_API + `/office-hours-employee/${idDelete}/delete`; + const result = await axios.delete(url) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.message === "OK") { + this.getDataHoursOffice() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success('Data office hours berhasil dihapus!!', 'Success!!'); + } else { + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error('Data office hours gagal dihapus!!', 'Failed!!'); + } + } + + // saveOfficeHours = async (obj, files) => { + // let url = BASE_URL_GEOHR_API + "/office-hours-employee/add"; + // let data = JSON.stringify(obj); + // const result = await axios.post(url, data) + // .then(res => res) + // .catch((error) => error.response); + // // console.log(result) + // if (result && result.data && result.data.message === "OK") { + // this.getDataHoursOffice(); + // NotificationManager.success('Data office hours berhasil ditambahkan!!', 'Success!!'); + // } else { + // NotificationManager.error(`${result.data.message}`, 'Failed!!'); + // } + // } + + saveOfficeHours = async (obj) => { + console.log('saveOfficeHours', obj); + const type = obj.type; + let url = API_GEOHR_SETTING_OFFICE_HOURS(type); + const result = await axios.post(url, obj) + .then(res => res) + .catch((error) => { + // console.log('error saveOfficeHours', error); + NotificationManager.error(error.toString(), 'Error Message'); + return false; + }); + // console.log('result', result); + if (!result) { + return; + } + if (result && result.data && result.data.code_status === 200) { + this.getDataHoursOffice(); + NotificationManager.success('Data office hours berhasil diperbaharui!!', 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + await this.setState({ openDialog: false }) + } + + editOfficeHours = async (data) => { + let url = API_GEOHR_EDIT_OFFICE_HOURS(data.employee); + const obj = JSON.stringify(data); + const result = await axios.put(url, obj) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result && result.data && result.data.code_status === 200) { + this.getDataHoursOffice(); + NotificationManager.success('Data office hours berhasil diedit!!', 'Success!!'); + } else { + NotificationManager.error('Data office hours gagal diedit!!', 'Failed!!'); + } + + await this.setState({ openDialog: false }) + } + + + handleEdit = (data) => { + // console.log('handleEdit', data); + this.setState({ dataEdit: data },()=>{ + this.handleOpenDialog('Edit'); + }); + + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + // console.log("show size", pageSize); + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataHoursOffice(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataHoursOffice(); + }) + } + + toggle = (param) => { + // console.log(`ee`, param) + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "tambah") { + this.setState(prevState => ({ tooltipTambah: !prevState.tooltipTambah })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + } + + render() { + const { tooltipTambah,tooltipExport, isReady, dataTable, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data group sales akan terhapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + + +

Jam Kerja

+ + + + + + + + + + + {/* */} + this.toggle("tambah")}> + Atur Jam Kerja + + this.toggle("export")}> + Export Excel + + + + +
+ + {/**/} + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + { + !isReady ? + + + + : + isReady && dataTable && dataTable.length > 0 ? dataTable.map((n, idx) => { + return ( + + + + + + + + + + + + ) + }) + : + + + + } + +
{i.name}
Sedang memuat data...
+ {/* delete */} + this.handleDelete(n.id)}> + this.toggle("delete")}> + Hapus + + + {/* edit */} + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + {n.employee_name}{moment(n.monday_start, "hh:mm").format(momentFormat) + ' - ' + moment(n.monday_end, "hh:mm").format(momentFormat)}{moment(n.tuesday_start, "hh:mm").format(momentFormat) + ' - ' + moment(n.tuesday_end, "hh:mm").format(momentFormat)}{moment(n.wednesday_start, "hh:mm").format(momentFormat) + ' - ' + moment(n.wednesday_end, "hh:mm").format(momentFormat)}{moment(n.thursday_start, "hh:mm").format(momentFormat) + ' - ' + moment(n.thursday_end, "hh:mm").format(momentFormat)}{moment(n.friday_start, "hh:mm").format(momentFormat) + ' - ' + moment(n.friday_end, "hh:mm").format(momentFormat)}{n.saturday_start || n.saturday_end ? moment(n.saturday_start, "hh:mm").format(momentFormat) + ' - ' + moment(n.saturday_end, "hh:mm").format(momentFormat) : '-'}{n.sunday_start || n.sunday_end ? moment(n.sunday_start, "hh:mm").format(momentFormat) + ' - ' + moment(n.sunday_end, "hh:mm").format(momentFormat) : '-'}
Data tidak tersedia
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterOfficeHours/index_sales.js b/src/views/Master/MasterOfficeHours/index_sales.js new file mode 100644 index 0000000..74f10eb --- /dev/null +++ b/src/views/Master/MasterOfficeHours/index_sales.js @@ -0,0 +1,304 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +import { Tooltip } from 'reactstrap'; + +const BASE_URL = "https://oslog.id/geohr-api/"; + +const momentFormat = 'HH:mm'; + +const column = [ + { name: "Sales Name" }, + { name: "Senin" }, + { name: "Selasa" }, + { name: "Rabu" }, + { name: "Kamis" }, + { name: "Jumat" }, + { name: "Sabtu" }, + { name: "Minggu" }, +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + } + } + + async componentDidMount() { + this.getDataHoursOffice(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataHoursOffice() + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataHoursOffice = async () => { + let url = BASE_URL + "office-hours-sales/search"; + let start = 0; + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + const obj = { + "paging": { "start": start, "length": this.state.rowsPerPage }, + "columns": [ + { + "name": "name", + "logic_operator": "like", + "value": this.state.search.toString(), + "operator": "and", + "table_name": "m_sales" + } + ], + "joins": [ + { "name": "sales", "column_results": ["name", "username", "phone_number", "email", "address", "type_sales", "lat", "lon", "session_login"] } + ], + "orders": { "columns": ["created_date"], "ascending": true } + } + + const result = await axios + .post(url, obj) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result.data.message == "OK") { + console.log("sales", result.data.data) + this.setState({ dataTable: result.data.data, totalPage: result.data.totalRecord }); + this.setListIdHo(result.data.data || []); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + setListIdHo = (data) => { + let list = []; + data.map((val, index) => { + list.push(val.sales) + }) + this.setState({ dataIdHo: list }, () => { + console.log(this.state.dataIdHo) + }) + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = (type, data) => { + // let obj = JSON.stringify(data); + // console.log(data) + if (type === "save") { + console.log('save', data) + this.saveOfficeHours(data); + // this.uploadImage(files); + } else if (type === "edit") { + this.editOfficeHours(data); + } + + this.setState({ openDialog: false }) + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmDelete = async () => { + const { idDelete } = this.state + let url = BASE_URL + `office-hours-sales/${idDelete}/delete`; + const result = await axios.delete(url) + .then(res => res) + .catch((error) => error.response); + if (result.data.message === "OK") { + this.getDataHoursOffice() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success('Data office hours berhasil dihapus!!', 'Success!!'); + } else { + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error('Data office hours gagal dihapus!!', 'Failed!!'); + } + } + + saveOfficeHours = async (obj, files) => { + let url = BASE_URL + "office-hours-sales/add"; + let data = JSON.stringify(obj); + const result = await axios.post(url, data) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result.data.message === "OK") { + this.getDataHoursOffice(); + NotificationManager.success('Data office hours berhasil ditambahkan!!', 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + editOfficeHours = async (data) => { + let url = BASE_URL + `office-hours-sales/${data.id}/edit`; + const obj = JSON.stringify(data); + // console.log(obj); + const result = await axios.put(url, obj) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result.data.message === "OK") { + this.getDataHoursOffice(); + NotificationManager.success('Data office hours berhasil diedit!!', 'Success!!'); + } else { + NotificationManager.error('Data office hours gagal diedit!!', 'Failed!!'); + } + } + + + handleEdit = (data) => { + this.setState({ dataEdit: data }); + this.handleOpenDialog('Edit'); + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + // console.log("show size", pageSize); + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataHoursOffice(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataHoursOffice(); + }) + } + + toggle = (param) => { + // console.log(`ee`, param) + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } + } + + render() { + const { dataTable, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data group sales akan terhapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + + +

Office Hours

+ +
+ + + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataTable.map((n) => { + return ( + + + + + + + + + + + + ) + })} + +
Actions{i.name}
+ {/* delete */} + this.handleDelete(n.id)}> + this.toggle("delete")}> + Delete + + + {/* edit */} + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + {n.join.sales_name}{moment(n.monday_start, "hh:mm").subtract(7 ,"hour").format(momentFormat) + ' - ' + moment(n.monday_end, "hh:mm").subtract(7 ,"hour").format(momentFormat)}{moment(n.tuesday_start, "hh:mm").subtract(7 ,"hour").format(momentFormat) + ' - ' + moment(n.tuesday_end, "hh:mm").subtract(7 ,"hour").format(momentFormat)}{moment(n.wednesday_start, "hh:mm").subtract(7 ,"hour").format(momentFormat) + ' - ' + moment(n.wednesday_end, "hh:mm").subtract(7 ,"hour").format(momentFormat)}{moment(n.thursday_start, "hh:mm").subtract(7 ,"hour").format(momentFormat) + ' - ' + moment(n.thursday_end, "hh:mm").subtract(7 ,"hour").format(momentFormat)}{moment(n.friday_start, "hh:mm").subtract(7 ,"hour").format(momentFormat) + ' - ' + moment(n.friday_end, "hh:mm").subtract(7 ,"hour").format(momentFormat)}{moment(n.saturday_start, "hh:mm").subtract(7 ,"hour").format(momentFormat) + ' - ' + moment(n.saturday_end, "hh:mm").subtract(7 ,"hour").format(momentFormat)}{moment(n.sunday_start, "hh:mm").subtract(7 ,"hour").format(momentFormat) + ' - ' + moment(n.sunday_end, "hh:mm").subtract(7 ,"hour").format(momentFormat)}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterOrganization/DialogBagan.js b/src/views/Master/MasterOrganization/DialogBagan.js new file mode 100644 index 0000000..24fede7 --- /dev/null +++ b/src/views/Master/MasterOrganization/DialogBagan.js @@ -0,0 +1,81 @@ +import React, { useRef, useState } from 'react' +import { + Modal, + ModalHeader, + ModalBody, + ModalFooter, + DropdownToggle, + DropdownItem, + DropdownMenu, + ButtonDropdown, + Tooltip +} from 'reactstrap'; +import { Button } from 'reactstrap'; +import ChartContainer from '@dabeng/react-orgchart'; +import 'antd/dist/antd.css'; + + + +const DialogBagan = ({openDialog, closeDialog, toggleDialog, dataBagan}) => { + const [resizeDropdown, setResize] = useState(false) + const [sizeModal, setSize] = useState("lg") + const [tooltipPrint, setTooltipPrint] = useState(false) + const [tooltipResize, setTooltipResize] = useState(false) + const orgchart = useRef(); + + const handleCancel = () => { + closeDialog('cancel', 'none') + } + + const exportTo = () => { + orgchart.current.exportTo("Bagan Organisasi", "png"); + } + + const handleTooltip = (type) => { + if(type==="resize"){ + setTooltipResize(!tooltipResize) + }else if(type==="print"){ + setTooltipPrint(!tooltipPrint) + } + } + + const handleResize = (size) => { + setSize(size); + } + + const toggleResize = () => { + setResize(!resizeDropdown) + } + + return ( + + + Bagan Organisasi + + toggleResize()}> + + + + + handleResize('lg')}>Medium + handleResize('xl')}>Large + + + handleTooltip('print')}> + Print Bagan + + handleTooltip('resize')}> + Ubah Ukuran Modal + + + + + + + + + + ) +} + +export default DialogBagan; \ No newline at end of file diff --git a/src/views/Master/MasterOrganization/DialogEmployee.js b/src/views/Master/MasterOrganization/DialogEmployee.js new file mode 100644 index 0000000..61b38b3 --- /dev/null +++ b/src/views/Master/MasterOrganization/DialogEmployee.js @@ -0,0 +1,206 @@ +import 'antd/dist/antd.css'; +import React, { Component } from 'react'; +import { Button, Form, FormFeedback, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import Select from 'react-select'; +import axios from 'axios'; +import { BASE_URL_GEOHR_API2 } from '../../../const/ApiConst'; +import { Transfer } from 'antd'; + +const ERROR_TITLE = "judul is required!" +const ERROR_MESSAGE = "message is required!" +// const BASE_URL = "https://oslog.id/geohr-api/"; +const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; +const roleName = window.localStorage.getItem('role_name'); +let countError = 0; +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + openDialog: false, + isParentClick: false, + dataEmployee: [], + dataSourceEmployee: [], + targetKeys:[], + selectedKeys: [], + allEmployeeId:[], + idEmployeeDivision: [], + disableTransfer:false + } + } + + async componentDidMount() { + this.props.showDialog(this.showDialog); + this.getDataEmployee() + } + + async componentDidUpdate() { + if (this.state.isParentClick === true) { + + this.setDataEmployee(); + this.setMemberEmor(); + this.setState({ isParentClick: false }); + } + } + + + showDialog = () => { + this.setState({ isParentClick: true }); + } + + handleSave = () => { + const { + idCurrentEmor + } = this.props + + const idEmployee = this.state.targetKeys || []; + + const data = { + id:idCurrentEmor, + idEmployee + } + // console.log("test 1 2 3 ", idEmployee); + this.props.closeDialog("save", data) + this.setState({ id: 0,targetKeys:[] }); + + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + getDataEmployee = async () => { + countError++; + let url = BASE_URL+`employee.php?act=get_data&role_name=${roleName}`; + + const result = await axios + .post(url) + .then(res => res) + .catch((error) => error.response ); + if(result && result.data && result.data.code_status == 200){ + // this.setDataSales(result.data.data || []); + // console.log(result.data.data) + this.setState({ dataEmployee:result.data.data }, () => this.setDataEmployee()) + }else{ + // console.log(result.data.data)s + if(countError<6){ + this.getDataEmployee(); + } + } + } + + setDataEmployee = async () => { + const listEmployee = []; + this.state.dataEmployee.map((val, index)=> { + const getIndex = this.getIndexDataEmployee(parseInt(val.id)); + // console.log(getIndex); + if(getIndex<0){ + listEmployee.push({ + key: parseInt(val.id), + id: parseInt(val.id), + title: val.name + }); + } + + }) + this.setState({ dataSourceEmployee: listEmployee}) + } + + setMemberEmor = () => { + this.setState({targetKeys:this.props.allEmorMemberId},() => { + // console.log('test 21', this.state.targetKeys) + }); + } + + getIndexDataEmployee = (val) => { + const { allIdEmor } = this.props + const memberId = this.props.allEmorMemberId || [] + const chairmanId = this.props.allIdChairman || [] + let index = allIdEmor.indexOf(val); + if(chairmanId.length>0){ + let cek = chairmanId.indexOf(parseInt(val)) + if(cek>=0){ + return cek + } + } + + if(memberId.length>0){ + if(index >= 0){ + let valRes = allIdEmor[index] + let cek = memberId.indexOf(valRes) + // console.log("cek cek", cek) + if(cek>=0){ + return -1 + } + } + } + + return index + } + + handleChangeTransfer = (nextTargetKeys, direction, moveKeys) => { + this.setState({ targetKeys: nextTargetKeys,errorEmployee:"" },()=>{ + // console.log('targetKeys: ', this.state.targetKeys); + }); + + + // console.log('direction: ', direction); + // console.log('moveKeys: ', moveKeys); + }; + + handleSelectChangeTransfer = (sourceSelectedKeys, targetSelectedKeys) => { + this.setState({ selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys],errorEmployee:"" }); + + // console.log('sourceSelectedKeys: ', sourceSelectedKeys); + // console.log('targetSelectedKeys: ', targetSelectedKeys); + }; + + renderForm = () => { + return ( +
+ + + { !this.state.disableTransfer && + item.title} + disabled={this.state.disableTransfer} + /> + } + {this.state.errorEmployee && {this.state.errorEmployee}} + +
+ ) + } + + handleCloseDialog = () => { + this.props.closeDialog("cancel", "none") + this.setState({ + errorTitle: "", + errorMessage: "", + errorDivision: "", + errorEmployee: "" + }) + } + + render() { + return ( + + Employee Organisasi + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterOrganization/DialogForm.js b/src/views/Master/MasterOrganization/DialogForm.js new file mode 100644 index 0000000..f69cf13 --- /dev/null +++ b/src/views/Master/MasterOrganization/DialogForm.js @@ -0,0 +1,249 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Select from 'react-select' +import axios from 'axios'; +import moment from 'moment'; +import { TimePicker } from 'antd'; +import 'antd/dist/antd.css'; +const BASE_URL = "https://oslog.id/geohr-api/"; +let countError = 0; + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + name:"", + description:"", + id_ketua:0, + parent_id:0, + openDialog: false, + isParentClick: false, + listParentSelect:[], + selectDisable: false, + currentSelectVal: null, + dataEmployee:[], + dataSourceEmployee:[], + currentSelectEmployee:null + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + this.setDataEmployee(); + this.setSelectParent("None"); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + if(this.props.typeDialog==="Edit"){ + const { dataEdit } = this.props + this.setDataEmployee(); + this.setSelectParent(dataEdit.id); + if(dataEdit.parent_org!=="" && dataEdit.parent_org!==undefined && dataEdit.parent_org!==null){ + this.searchParent(dataEdit.parent_org); + }else{ + this.searchParent("none"); + } + if(dataEdit.employee_id!=="" && dataEdit.employee_id!==undefined && dataEdit.employee_id!==null){ + this.searchEmployee(dataEdit.employee_id); + }else{ + this.searchEmployee("none"); + } + this.setState({ + id:dataEdit.id, + name:dataEdit.name_org, + description:dataEdit.description, + id_ketua:dataEdit.employee_id, + parent_org:dataEdit.parent_org + }) + }else{ + this.setSelectParent("None"); + this.setDataEmployee(); + this.setState({ + id:0, + name:"", + description:"", + id_ketua:0, + parent_id:0, + currentSelectEmployee:null, + currentSelectVal:null + }) + } + this.setState({isParentClick:false}); + } + } + + setDataEmployee = () => { + const listEmployee = []; + this.props.dataEmployee.map((val, index)=> { + let cekChairman = this.cekChairman(val.id); + if(cekChairman<0){ + listEmployee.push({ + label: val.name, + value: val.id + }); + } + }) + this.setState({ dataSourceEmployee: listEmployee}) + } + + cekChairman = (id) => { + const { allIdChairman } = this.props + let index = allIdChairman.indexOf(parseInt(id)); + return index + } + + searchEmployee = (id) => { + // console.log("cek id", id); + if(id==="none"){ + this.setState({ id_ketua:0,currentSelectEmployee:null }) + }else{ + let getIndex = this.getIndexDataEmployee(id); + if(getIndex>=0){ + let data = this.props.dataEmployee[getIndex] + this.setState({ id_ketua:data.id,currentSelectEmployee:{value:data.id,label:data.name} }) + } + } + } + + getIndexDataEmployee = (val) => { + const { dataEmployee } = this.props + let index = dataEmployee.findIndex(obj => obj.id === val); + return index + } + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + setSelectParent = (id) => { + const { dataOrganization } = this.props + const listParent = [] + if(id==="None"){ + dataOrganization.map((val, index)=> { + listParent.push({ + value:val.id, + label:val.name_org + }) + }) + }else{ + dataOrganization.map((val, index)=> { + if(val.id!==id){ + listParent.push({ + value:val.id, + label:val.name_org + }) + } + }) + } + this.setState({ listParentSelect:listParent }) + } + + searchParent = (id) => { + if(id==="none"){ + this.setState({ parent_id:0,currentSelectVal:null }) + }else{ + let getIndex = this.getIndexDataParent(id); + if(getIndex>=0){ + let data = this.props.dataOrganization[getIndex] + this.setState({ parent_id:data.id,currentSelectVal:{value:data.id,label:data.name_org} }) + } + } + + } + + getIndexDataParent = (val) => { + let index = this.props.dataOrganization.findIndex(obj => obj.id === val); + return index + } + + handleSave = () => { + const { + id, + name, + description, + id_ketua, + parent_id + } = this.state + + let data = ''; + if(this.props.typeDialog==="Save"){ + data = { + name_org:name, + description, + employee_id:id_ketua, + parent_org:parent_id + } + this.setState({ id:0,parent_id:0,id_ketua:0,currentSelectVal:null,currentSelectEmployee:null }); + this.props.closeDialog('save', data); + }else{ + data = { + id, + name_org:name, + description, + employee_id:id_ketua, + parent_org:parent_id + } + this.setState({ id:0,parent_id:0,id_ketua:0,currentSelectVal:null,currentSelectEmployee:null }); + this.props.closeDialog('edit', data); + } + + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + handleSelectParent = (inputValue, actionMeta) => { + if(inputValue===null){ + this.setState({ parent_id:0,currentSelectVal:null }) + }else{ + this.setState({ parent_id:inputValue.value,currentSelectVal:{ value:inputValue.value,label:inputValue.label } }) + } + + } + + handleSelectChairman = (inputValue, actionMeta) => { + this.setState({ id_ketua:inputValue.value,currentSelectEmployee:{ value:inputValue.value,label:inputValue.label } }) + } + + renderForm = () => { + return( +
+ + + this.setState({ name:e.target.value })} placeholder="name organisasi.." /> + + + + this.setState({ description:e.target.value })} placeholder="deskripsi.." /> + + + + + + + + this.setState({ + searchDetail: "Nama Organisasi", + searchDetailField: "name_org" + })}>Nama Organisasi + this.setState({ + searchDetail: "Ketua Divisi", + searchDetailField: "employee_name" + })}>Ketua Divisi + this.setState({ + searchDetail: "Deskripsi", + searchDetailField: "description" + })}>Deskripsi + this.setState({ + searchDetail: "Atasan Organisasi", + searchDetailField: "parent_name" + })}>Atasan Organisasi + + + + + + + + + {/* */} + this.toggle("bagan")}> + Bagan Organisasi + + this.toggle("tambah")}> + Tambah Organisasi + + this.toggle("export")}> + Export Excel + + + + + + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {this.checkdata()} + {dataTable.map((n) => { + return ( + + + + + + + + ) + })} + +
Aksi{i.name}
+ this.handleEmor(n.id)}> + this.toggle("emor")}> + Employee Organisasi + + + this.handleDelete(n.id)}> + this.toggle("delete")}> + Hapus + + + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + {n.name_org}{n.employee_name}{n.description}{n.parent_name || "-"}
+ +
+ +
+ ) + } +} diff --git a/src/views/Master/MasterRoles/DialogForm.js b/src/views/Master/MasterRoles/DialogForm.js new file mode 100644 index 0000000..e1fb6a3 --- /dev/null +++ b/src/views/Master/MasterRoles/DialogForm.js @@ -0,0 +1,109 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import 'antd/dist/antd.css'; + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + name:"", + description:"", + openDialog: false, + isParentClick: false, + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + if(this.props.typeDialog==="Edit"){ + const { dataEdit } = this.props + this.setState({ + id:dataEdit.id, + name:dataEdit.name, + description:dataEdit.description + }) + }else{ + this.setState({ + id:0, + name:"", + description:"" + }) + } + this.setState({isParentClick:false}); + } + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + const { + id, + name, + description + } = this.state + + let data = ''; + if(this.props.typeDialog==="Save"){ + data = { + id, + name, + description + } + this.props.closeDialog('save', data); + }else{ + data = { + id, + name, + description + } + this.props.closeDialog('edit', data); + } + + this.setState({ id:0 }); + + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + renderForm = () => { + return( + + + + this.setState({ name:e.target.value })} placeholder="name roles.." /> + + + + this.setState({ description:e.target.value })} placeholder="deskripsi.." /> + + + ) + } + + render() { + return ( + + {this.props.typeDialog=="Save" ? "Tambah" : "Edit"} Roles + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterRoles/DialogMenuRoles.js b/src/views/Master/MasterRoles/DialogMenuRoles.js new file mode 100644 index 0000000..5e2b732 --- /dev/null +++ b/src/views/Master/MasterRoles/DialogMenuRoles.js @@ -0,0 +1,240 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter, Row, Col, Table } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import 'antd/dist/antd.css'; +import axios from 'axios'; +import { MENU_SEARCH } from '../../../const/ApiConst.js'; +const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; + +const token = window.localStorage.getItem('token'); + + +const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } +}; + +export default class DialogMenuRoles extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + name:"", + description:"", + openDialog: false, + isParentClick: false, + menu:[], + stateMenu:[], + allChecked:true, + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + this.getAllMenu(); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + const { idRoles } = this.props + this.setStateMenu(true); + // this.checkMenuRoles(); + this.setState({isParentClick:false, id:idRoles}); + } + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + getAllMenu = async () => { + const payload = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name": "name", "logic_operator": "like", "value": "", "operator": "AND"} + ], + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + + + + const result = await axios + .post(MENU_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + this.setState({ menu:result.data.data },() => { + this.setStateMenu(false); + }); + }else{ + // NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + + } + + setStateMenu = edit => { + const stateMenu = []; + this.state.menu.map((val)=>{ + stateMenu.push(false); + }) + // console.log("test reset", stateMenu) + this.setState({ stateMenu:[] },()=> { + this.setState({stateMenu:stateMenu},()=> { + if(edit){ + this.checkMenuRoles(); + } + }) + }) + } + + checkMenuRoles = () => { + // console.log("menu roles", menuRoles); + // console.log("allchecked", this.state.allChecked); + let copyStateMenu = [...this.state.stateMenu]; + // console.log("first copy", copyStateMenu) + this.props.menuRoles.map((val, indexMenu)=> { + let index = this.getIndexDataMenu(val.menu_id); + console.log("index true", val.menu_id); + if(index >= 0){ + copyStateMenu[index] = true; + } + }) + this.setState({ stateMenu:[] },()=>{ + let check = copyStateMenu.some(this.checkArray); + if(check===false){ + this.setState({ allChecked:true,stateMenu:copyStateMenu }) + }else{ + this.setState({ allChecked:false,stateMenu:copyStateMenu }) + } + }) + } + + + getIndexDataMenu = (id) => { + let index = this.state.menu.findIndex(obj => obj.id === id); + return index + } + + handleSave = () => { + const { + stateMenu, + menu, + id + } = this.state + + const arrayData = []; + + menu.map((val, index) => { + let data = { + roles_id:id, + menu_id:val.id, + checked:stateMenu[index] + } + arrayData.push(data); + }) + // console.log("cek again", arrayData); + this.props.closeDialog('save', arrayData); + this.setState({ id:0 }); + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + handleChangeCheckbox = (checked, index) => { + let copyStateMenu = [...this.state.stateMenu]; + copyStateMenu[index] = checked; + console.log("cek uncek", copyStateMenu); + this.setState({ stateMenu:copyStateMenu }) + } + + renderForm = () => { + const { menu, stateMenu } = this.state + return( + menu.map((val, index)=>{ + return( + + {val.name} + this.handleChangeCheckbox(e.target.checked, index)} defaultChecked={stateMenu[index]} /> + + ) + }) + ) + } + + checkArray = (val) => { + return val === false; + } + + handleAllChecked = (checked) => { + this.setState({ allChecked:!this.state.allChecked }); + if(checked===true){ + console.log("test 1") + let check = this.state.stateMenu.some(this.checkArray); + + if(check){ + const stateMenu = []; + this.state.menu.map((val)=>{ + stateMenu.push(true); + }) + + this.setState({ stateMenu:[] },()=> { + this.setState({stateMenu:stateMenu}); + }) + } + + + }else{ + console.log("test 1 2 3 ") + const stateMenu = []; + this.state.menu.map((val)=>{ + stateMenu.push(false); + }) + + this.setState({ stateMenu:[] },()=> { + this.setState({stateMenu:stateMenu}); + }) + } + } + + render() { + return ( + + Roles Menu + +
+ + + + + + + + + + + + + {this.renderForm()} + +
Menu + T/F +
All this.handleAllChecked(e.target.checked)} checked={this.state.allChecked} />
+
+
+ + {' '} + + +
+ ) + } +} diff --git a/src/views/Master/MasterRoles/index.js b/src/views/Master/MasterRoles/index.js new file mode 100644 index 0000000..8c8ccc8 --- /dev/null +++ b/src/views/Master/MasterRoles/index.js @@ -0,0 +1,519 @@ +import React, { Component, useMemo } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import * as XLSX from 'xlsx'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import DialogMenuRoles from './DialogMenuRoles'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination, Tooltip, Table } from 'antd'; +// import { Tooltip } from 'reactstrap'; + +import { ROLE_ADD, ROLE_SEARCH, ROLE_EDIT, ROLE_DELETE, ROLEMENU_ADD, ROLEMENU_SEARCH, ROLEMENU_DELETE_ROLE } from '../../../const/ApiConst.js'; + +const token = window.localStorage.getItem('token'); + +const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; + +const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } +}; + +const momentFormat = 'HH:mm'; + + + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + dialogMenuForm: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + alertNotDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + tooltipMenu: false, + idRoles:0, + menuRoles:[], + tooltipTambah:false, + tooltipImport:false, + tooltipExport:false, + dataExport:[] + } + + this.columns = [ + { + title: 'Action', + dataIndex: '', + key: 'x', + className:'nowrap', + render: (text, record) => <> + + this.handleMenuRoles(text.id)}> + + + + this.handleDelete(text.id)}> + + + + this.handleEdit(text)}> + + , + }, + { title: 'Nama Role', dataIndex: 'name', key: 'name', className:"nowrap" }, + { title: 'Description', dataIndex: 'description', key: 'description' }, + ] + } + + async componentDidMount() { + this.getDataRoles(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataRoles() + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataRoles = async () => { + + let start = 0; + + if (this.state.currentPage !== 1 && this.state.currentPage > 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + const formData = { + "paging": {"start": start, "length": this.state.rowsPerPage}, + "columns": [ + {"name": "name", "logic_operator": "like", "value": this.state.search, "operator": "AND"} + ], + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + + const result = await axios + .post(ROLE_SEARCH, formData, config) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + + if(result && result.data && result.data.code == 200){ + this.setState({ dataTable: result.data.data, totalPage: result.data.totalRecord }); + }else{ + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + + + handleCloseDialog = (type, data) => { + if (type === "save") { + this.saveRole(data); + } else if (type === "edit") { + this.editRole(data); + } + + this.setState({ openDialog: false }) + } + + + handleOpenDialogMr = () => { + this.setState({ dialogMenuForm: true }) + this.showMenuRolesDialog(); + } + handleCloseDialogMr = (type, data) => { + if(type==="save"){ + this.saveMenuRoles(data) + } + this.setState({ dialogMenuForm: false }) + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmDelete = async () => { + const { idDelete } = this.state + const url = ROLE_DELETE(idDelete) + + const result = await axios.delete(url, config) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + this.deleteCurrentRoleMenu(idDelete) + this.getDataRoles() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success(`Data role berhasil dihapus`, 'Success!!'); + } else { + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error(`Data role gagal dihapus`, 'Failed!!'); + } + } + + saveRole = async (data) => { + + const formData = { + name:data.name, + description:data.description + } + + const result = await axios.post(ROLE_ADD, formData, config) + .then(res => res) + .catch((error) => error.response); + + if(result && result.data && result.data.code===200){ + this.getDataRoles(); + NotificationManager.success(`Data role berhasil ditambah`, 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + + } + + editRole = async (data) => { + + const formData = { + name:data.name, + description:data.description + } + const url = ROLE_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){ + this.getDataRoles(); + NotificationManager.success(`Data role berhasil diedit`, 'Success!!'); + } else { + NotificationManager.error(`Data role gagal di edit`, `Failed!!`); + } + + } + + + handleEdit = (data) => { + this.setState({ dataEdit: data }); + this.handleOpenDialog('Edit'); + } + + handleDelete = (id) => { + id == '1' ? this.setState({ alertNotDelete: true }) : + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataRoles(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataRoles(); + }) + } + + toggle = (param) => { + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if(param === "menu"){ + this.setState(prevState => ({ tooltipMenu: !prevState.tooltipMenu })) + } else if (param === "tambah") { + this.setState(prevState => ({ tooltipTambah: !prevState.tooltipTambah })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + } + + dataNotAvailable = () => { + if(this.state.dataTable.length===0){ + return ( + + Not Data Available + + ) + } + } + + handleMenuRoles = async (id) => { + + const formData = { + "paging": {"start": 0, "length": -1}, + "columns": [ + {"name": "role_id", "logic_operator": "=", "value": `${id}`, "operator": "AND"} + ], + "joins": [], + "orders": {"columns": ["id"], "ascending": false} + } + + const result = await axios + .post(ROLEMENU_SEARCH, formData, config) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result && result.data && result.data.code == 200) { + this.setState({ menuRoles:result.data.data,idRoles:id },() => { + this.handleOpenDialogMr(); + }); + } else { + // NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + + saveMenuRoles = async (dataArray) => { + let payloadArray = dataArray || [] + // console.log("cek", payloadArray[0]) + if(payloadArray.length > 0){ + await this.deleteCurrentRoleMenu(payloadArray[0].roles_id) + } + let promises = [] + let result = [] + console.log("test test", dataArray) + dataArray.map((val, index)=> { + if(val.checked===true){ + const formData = { + menu_id:val.menu_id, + role_id:val.roles_id + } + promises.push(axios.post(ROLEMENU_ADD, formData, config) + .then(res => result.push(res))) + } + }) + + await Promise.all(promises); + if(result){ + if(result.length > 0){ + if (result[0].data.code === 200) { + this.getDataRoles(); + NotificationManager.success('Data roles berhasil ditambahkan!!', 'Success!!'); + } else { + NotificationManager.error(`${result[0].data.message}`, 'Failed!!'); + } + } + } + + } + + deleteCurrentRoleMenu = async (id) => { + let urlDel = ROLEMENU_DELETE_ROLE(id) + const result = await axios.delete(urlDel, config) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + return true + } else { + return false + } + } + + handleExportExcel = async () => { + let start = 0; + let end = "ALL"; + let url = BASE_URL + "roles.php?act=get_data"; + const formData = new FormData(); + formData.append('field', 'name'); + if(this.state.search !== ""){ + formData.append('value', this.state.search); + } + // console.log("test", url); + const result = await axios + .post(url,formData) + .then(res => res) + .catch((error) => error.response); + // console.log("result", result); + if (result && result.data && result.statusText == "OK") { + const dataRes = result.data.data|| []; + console.log("data result", dataRes); + const dataExport = []; + // {n.name} + // {n.url} + // {n.alias_name} + // {n.icon} + // {n.parent_name || "-"} + dataRes.map((val,index)=> { + let row = { + Nama:val.name, + Deskripsi:val.description + } + dataExport.push(row); + }) + console.log("data Export", dataExport); + this.setState({ dataExport:dataExport },()=> { + this.exportExcel(); + }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + exportExcel = () => { + const dataExcel = this.state.dataExport || []; + const fileName = "Data Roles.xlsx"; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'Data Roles'); + + XLSX.writeFile(wb, fileName); + } + + render() { + const { tooltipTambah,tooltipExport,dialogMenuForm, dataTable, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete,tooltipMenu } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data tipe roles akan terhapus!! + + this.setState({ alertNotDelete: false})} + // focusCancelBtn + > + Data tipe roles tidak dapat di hapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + this.showMenuRolesDialog = showDialog} + menuRoles={this.state.menuRoles} + + /> + + +

{this.props.params.name}

+ + + + + + + + + + + + + +
+ + + {/*
+ + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {this.dataNotAvailable()} + {dataTable.map((n, index) => { + return ( + + + + + + + ) + })} + +
Aksi{i.name}
+ + this.handleMenuRoles(n.id)}> + + + + this.handleDelete(n.id)}> + + + + this.handleEdit(n)}> + + {n.name}{n.description}
*/} + +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterSales/DialogForm.js b/src/views/Master/MasterSales/DialogForm.js new file mode 100644 index 0000000..6180a9d --- /dev/null +++ b/src/views/Master/MasterSales/DialogForm.js @@ -0,0 +1,269 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Select from 'react-select' + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + openDialog: false, + type_sales:'B2B', + name: '', + email: '', + address: '', + username: '', + phoneNumber: '', + password:'', + id: 0, + idGs:0, + isParentClick: false, + file: [], + fileObj: [], + fileArray: [], + dataGs: [], + currentSelectVal: null, + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + this.cekDataGs() + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + await this.cekDataGs() + if(this.props.typeDialog==="Edit"){ + let dataGs = this.searchGs(this.props.dataEdit.group_sales); + // console.log(dataGs); + this.setState({ + idGs:this.props.dataEdit.group_sales, + id:this.props.dataEdit.id, + name:this.props.dataEdit.name, + type_sales:this.props.dataEdit.type_sales, + email: this.props.dataEdit.email, + address: this.props.dataEdit.address, + username: this.props.dataEdit.username, + phoneNumber: this.props.dataEdit.phone_number, + password:this.props.dataEdit.password, + currentSelectVal:dataGs + }) + }else{ + this.setState({ + id:0, + name:'', + type_sales:'B2B', + email: '', + address:'', + username: '', + phoneNumber: '', + password: '', + currentSelectVal:null + }) + } + this.setState({isParentClick:false}); + } + } + + searchGs = (idGs) => { + let getIndex = this.getIndexDataGs(idGs); + return this.state.dataGs[getIndex]; + } + + getIndexDataGs = (val) => { + let index = this.state.dataGs.findIndex(obj => obj.value === val); + // console.log('index', this.state.dataGs); + // console.log('index', index); + return index + } + + cekDataGs = () => { + if(this.state.dataGs.length===0){ + let data = []; + this.props.dataGs.map((val,index) => { + data.push( + { + value: val.id, + label: val.name + } + ) + }); + this.setState({ dataGs:data }) + } + } + + showDialog = () => { + // console.log("test") + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + const { file, name, email, id, address, phoneNumber, password, username, idGs,type_sales } = this.state + let data = ''; + if(this.props.typeDialog==="Save"){ + data = { + username, + password, + name, + email, + address, + phone_number:phoneNumber, + group_sales:idGs, + type_sales + } + this.props.closeDialog('save', data, file); + }else{ + data = { + id, + username, + password, + name, + email, + address, + phone_number:phoneNumber, + group_sales:idGs, + type_sales + } + this.props.closeDialog('edit', data, file); + } + + this.setState({ id:'',username:'',password:'',name:'',email:'',address:'',phoneNumber:'',file:[],idGs:0,type_sales:'B2B',currentSelectVal:null }); + + } + + handleCancel = () => { + this.setState({ id:'',username:'',password:'',name:'',email:'',address:'',phoneNumber:'',file:[],idGs:0,type_sales:'B2B' }); + this.props.closeDialog('cancel', 'none', 'none') + } + + uploadMultipleImage = (event) => { + // this.state.fileObj.push(e.target.files) + // console.log(event.target.files); + this.setState({ fileObj: [...this.state.fileObj, event.target.files],file: [...this.state.file, event.target.files] }, ()=> { + this.setUrl() + }) + + } + + setUrl = () => { + for (let i = 0; i < this.state.fileObj[0].length; i++) { + // this.fileArray.push(URL.createObjectURL(this.fileObj[0][i])) + this.setState({ fileArray: [...this.state.fileArray, URL.createObjectURL(this.state.fileObj[0][i])],fileObj:[] }) + } + } + + handleSelectGs = (inputValue, actionMeta) => { + this.setState({ idGs:inputValue.value,currentSelectVal:{ value:inputValue.value,label:inputValue.label } }) + } + + handleTypeSales = (e) => { + this.setState({ type_sales:e.target.value }) + } + + renderForm = () => { + return( +
+ + + + + this.setState({ username:e.target.value })} placeholder="username " /> + + + + + + + + this.setState({ password:e.target.value })} placeholder="password" /> + + + + + + this.setState({ name:e.target.value })} placeholder="name " /> + + + + + + + + this.setState({ email:e.target.value })} placeholder="email" /> + + + + + + this.setState({ address:e.target.value })} placeholder="address " /> + + + + + + + + this.setState({ phoneNumber:e.target.value })} placeholder="phone number" /> + + + + + + + + + + + + + + + + + + + +
+ {(this.state.fileArray || []).map((url,index) => ( + ... + ))} +
+
+ + + + + +
+ ) + } + + render() { + return ( + + Add Sales + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterSales/SettingSales.js b/src/views/Master/MasterSales/SettingSales.js new file mode 100644 index 0000000..8f0c49b --- /dev/null +++ b/src/views/Master/MasterSales/SettingSales.js @@ -0,0 +1,348 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input, Modal, ModalHeader, ModalBody, ModalFooter, FormGroup, Label } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager} from 'react-notifications'; +import { Pagination } from 'antd'; +// npm install --save-dev @iconify/react @iconify-icons/ant-design +import { Icon, InlineIcon } from '@iconify/react'; +import settingOutlined from '@iconify/icons-ant-design/setting-outlined'; +import {Link} from 'react-router-dom' +import Select from 'react-select' + +import { SketchPicker } from 'react-color'; + + +const BASE_URL = "https://oslog.id/geohr-api/"; + + +const column = [ + { name: "Group Sales" }, + { name: "Name" }, + { name: "Phone Number" }, + { name: "Email" }, + { name: "Address" }, +] + +const layerSales = [ + { + "id": 1, + "rule_name": "sales yang incomenya lebih dari 1000000", + "style": { + "fill_color": "#22194D", + }, + "rule": { + "column": "income", + "operator": ">", + "value": 1000000, + } +}, +{ + "id": 2, + "rule_name": "sales yang punya nama mohammad", + "style": { + "fill_color": "#22194D", + }, + "rule": { + "column": "name", + "operator": "like", + "value": "mohammad" + } +}, + + +] + +const options = [ + { value: 'group-sales', label: 'Group Sales' }, + { value: 'name', label: 'Name' }, + { value: 'phone-number', label: 'Phone Number' }, + { value: 'email', label: 'Email' }, + { value: 'address', label: 'Address' } + + ] + + const logicOperator = [ + { value: '>', label: '>' }, + { value: '>=', label: '>=' }, + { value: '<', label: '<' }, + { value: '<=', label: '<=' }, + { value: '==', label: '==' }, + { value: '<>', label: '<>' }, + ] + + +const LENGTH_DATA = 10 +export default class SettingSales extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + layersSales: [], + modalEdit: false, + modalAdd: false, + id: 0, + fill_color: "#000", + column: "", + operator: "", + value: "", + dataEdit: null, + + } + } + + + async componentDidMount (){ + this.getRuleSales() + } + + async componentDidUpdate (prevProps, prevState){ + const { search } = this.state + if (search !== prevState.search) this.getRuleSales() + } + + handleOpenDialog = (type) => { + this.setState({modalEdit: true, typeDialog: type }) + this.handleSetData() +} + + handleSetData = () => { + const { dataEdit, typeDialog} = this.state + if(typeDialog === "Edit") { + this.setState({ + id: dataEdit.id, + rule_name: dataEdit.rule_name, + fill_color: dataEdit.style.fill_color, + column: dataEdit.rule.column, + value: dataEdit.rule.value, + operator: dataEdit.rule.operator + + }) + } else { + this.setState({ + id: 0, + rule_name: "", + fill_color: "#000", + column: null, + value:"", + operator: null + + }) + + } + + } + + handleEdit = (data) => { + this.setState({dataEdit: data}) + this.handleOpenDialog('Edit') + console.log('test data edit', data) + + } + + handleCancel = () => { + this.setState({ + + rule_name: "", + fill_color: "#000", + column: null, + value:"", + operator: null, + modalEdit: false + }) + + + + + } + + handleSave = () => { + this.setState({ + + rule_name: "", + fill_color: "#000", + column: null, + value:"", + operator: null, + modalEdit: false }) + + + } + + toggleAdd = () => { + this.setState({modalAdd: !this.state.modalAdd}) + } + + getRuleSales = () => { + this.setState({layersSales: layerSales}) + } + + handleChangeComplete = (color, event) => { + + console.log('handleChangeComplete',color, event); + this.setState({fill_color: color.hex}); + } + + + render() { + const {layersSales, modalEdit, modalAdd, search, openDialog, currentPage, rowsPerPage,totalPage, dataTable } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete:false })} + onCancel={()=> this.setState({ alertDelete:false })} + focusCancelBtn + > + Rule sales akan terhapus!! + + + + +

Rules Sales

+
+ +
+
+ + + + + {layersSales.map((n) => { + return( + + + + + + + +
+ + + + {n.rule_name} + + +
+ + + +
+ + + + + + + + + + + + + ) + }) + + } + + + + + + Edit Rule Name + +
+ + + this.setState({ rule_name :e.target.value })} /> + + + + + + + + + + + + + + + this.setState({ value :e.target.value })} /> + + + +
+
+ + {' '} + + +
+ {/* + Add Rule Name + +
+ + + + + + + + + + + + + + + + + + + + +
+
+ + {' '} + + +
*/} + +
+ ) + } +} diff --git a/src/views/Master/MasterSales/index.js b/src/views/Master/MasterSales/index.js new file mode 100644 index 0000000..71b4651 --- /dev/null +++ b/src/views/Master/MasterSales/index.js @@ -0,0 +1,418 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input, InputGroup, InputGroupButtonDropdown, DropdownToggle, DropdownItem, DropdownMenu } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +// npm install --save-dev @iconify/react @iconify-icons/ant-design +import { Icon, InlineIcon } from '@iconify/react'; +import settingOutlined from '@iconify/icons-ant-design/setting-outlined'; +import { Link } from 'react-router-dom'; +import { Tooltip } from 'reactstrap'; + + + +const BASE_URL = "https://oslog.id/geohr-api/"; + + +const column = [ + { name: "Group Sales" }, + { name: "Name" }, + { name: "Phone Number" }, + { name: "Email" }, + { name: "Address" }, +] + +const LENGTH_DATA = 10 +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + splitButtonOpen: false, + dropdownOpen: false, + searchDetail: "All", + searchDetailField: "name", + tooltipDelete: false, + tooltipEdit: false + } + } + + async componentDidMount() { + this.getDataSales(); + this.getDataGroupSales(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataSales() + } + + getDataGroupSales = async () => { + let url = BASE_URL + "group-sales/search"; + const obj = { + "paging": { "start": 0, "length": -1 }, + "columns": [ + { + "name": "name", + "logic_operator": "like", + "value": "", + "operator": "and" + } + ], + "orders": { "columns": ["name"], "ascending": true } + } + + const result = await axios + .post(url, obj) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result && result.data && result.data.message == "OK") { + // console.log(result.data.data) + this.setState({ dataGs: result.data.data }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + + getDataSales = async () => { + const { searchDetail, searchDetailField } = this.state + let url = BASE_URL + "sales/search"; + let start = 0 + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + const obj = { + "paging": { "start": start, "length": this.state.rowsPerPage }, + // "filter_columns": [ + // { "name": "name", "value": this.state.search.toString() }, + // { "name": "name", "value": this.state.search.toString(), "table_name": "m_group_sales" }, + // // { "name": "description", "value": this.state.search.toString(), "table_name": "m_group_sales" } + // ], + ...searchDetail === "All" && + { + "filter_columns": [ + { "name": "name", "value": this.state.search.toString() }, + { "name": "phone_number", "value": this.state.search.toString() }, + { "name": "email", "value": this.state.search.toString() }, + { "name": "address", "value": this.state.search.toString() }, + { "name": "name", "value": this.state.search.toString(), "table_name": "m_group_sales" }, + ], + }, + ...searchDetail !== "All" && searchDetail !== "Group Sales" && + { + "columns": [ + { "name": searchDetailField, "logic_operator": "like", "operator": "and", "value": this.state.search.toString() } + ] + }, + ...searchDetail === "Group Sales" && + { + "columns": [ + { "name": searchDetailField, "logic_operator": "like", "operator": "and", "value": this.state.search.toString(), "table_name": "m_group_sales" } + ] + }, + + "joins": [ + { "name": "group_sales", "column_results": ["name", "description"] } + ], + "orders": { "columns": ["name"], "ascending": true } + } + + const result = await axios + .post(url, obj) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result && result.data && result.data.message == "OK") { + // console.log("sales",result.data.data) + this.setState({ dataTable: result.data.data, totalPage: result.data.totalRecord }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = (type, data, files) => { + // let obj = JSON.stringify(data); + // console.log(data) + if (type === "save") { + this.saveSales(data, files); + // this.uploadImage(files); + } else if (type === "edit") { + this.editSales(data, files); + this.uploadImage(files, data.id); + } + + this.setState({ openDialog: false }) + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmDelete = async () => { + const { idDelete } = this.state + let url = BASE_URL + `sales/${idDelete}/delete`; + const result = await axios.delete(url) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.message === "OK") { + this.getDataSales() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success('Data group sales berhasil dihapus!!', 'Success!!'); + } else { + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error('Data group sales galal dihapus!!', 'Failed!!'); + } + } + + + saveSales = async (obj, files) => { + let url = BASE_URL + "sales/add"; + let data = JSON.stringify(obj); + const result = await axios.post(url, data) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result && result.data && result.data.message === "OK") { + let ref_id = parseInt(result.data.data.id); + this.uploadImage(files, ref_id); + NotificationManager.success('Data sales berhasil ditambahkan!!', 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + editSales = async (data) => { + let url = BASE_URL + `sales/${data.id}/edit`; + const obj = JSON.stringify(data); + // console.log(obj); + const result = await axios.put(url, obj) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result && result.data && result.data.message === "OK") { + this.getDataSales(); + NotificationManager.success('Data sales berhasil diedit!!', 'Success!!'); + } else { + NotificationManager.error('Data sales galal diedit!!', 'Failed!!'); + } + } + + uploadImage = async (files, idRef) => { + if (files.length > 0) { + let promises = []; + let response = []; + let url = BASE_URL + `image/m_sales/upload`; + files.map((val, index) => { + let formData = new FormData(); + // console.log(val[0]) + formData.append("ref_id", idRef); + formData.append("files", val[0]); + promises.push(axios.post(url, formData) + .then(res => { response.push(res) })) + }) + await Promise.all(promises); + } + + this.getDataSales() + } + + handleEdit = (data) => { + this.setState({ dataEdit: data }); + this.handleOpenDialog('Edit'); + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + // console.log("show size", pageSize); + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataSales(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataSales(); + }) + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + toggleDropDown = () => { + this.setState(prevState => ({ splitButtonOpen: !prevState.splitButtonOpen })) + } + + toggle = (param) => { + // console.log(`ee`, param) + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } + } + + + + render() { + const { search, openDialog, currentPage, rowsPerPage, totalPage, dataTable, splitButtonOpen, searchDetail, tooltipDelete, tooltipEdit } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data group sales akan terhapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataGs={this.state.dataGs} + /> + + +

Sales

+
+ + + + + +
+
+ + + + + + + + + + this.setState({ + searchDetail: "All", + searchDetailField: "name" + })}>All + this.setState({ + searchDetail: "Name", + searchDetailField: "name" + })}>Name + this.setState({ + searchDetail: "Group Sales", + searchDetailField: "name" + })}>Group Sales + this.setState({ + searchDetail: "Phone Number", + searchDetailField: "phone_number" + })}>Phone Number + this.setState({ + searchDetail: "Email", + searchDetailField: "email" + })}>Email + this.setState({ + searchDetail: "Address", + searchDetailField: "address" + })}>Address + + + + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataTable.map((n) => { + return ( + + + + + + + + + ) + })} + +
Actions{i.name}
+ {/* delete */} + this.handleDelete(n.id)}> + this.toggle("delete")}> + Delete + + + {/* edit */} + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + {n.join.group_sales_name}{n.name}{n.phone_number}{n.email}{n.address}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterSubdistrict/MasterSubdistrict.css b/src/views/Master/MasterSubdistrict/MasterSubdistrict.css new file mode 100644 index 0000000..e69de29 diff --git a/src/views/Master/MasterSubdistrict/MasterSubdistrict.js b/src/views/Master/MasterSubdistrict/MasterSubdistrict.js new file mode 100644 index 0000000..f3a185c --- /dev/null +++ b/src/views/Master/MasterSubdistrict/MasterSubdistrict.js @@ -0,0 +1,52 @@ +import React, { Component } from 'react' +// import './MasterSubdistrict.css' +import DataTable from '../../../components/DataTable' +import { API_LIST_DATA_SUBDISTRICT, API_INSERT_DATA_SUBDISTRICT, API_UPDATE_DATA_SUBDISTRICT, API_DELETE_DATA_SUBDISTRICT } from '../../../const/ApiConst.js' + +// important! +const columns = [{ + dataField: 'id', + alias: "Id", + showInput: false, + type: "number", + state: 0 + }, { + dataField: 'subdistrict_name', + alias: "Subdistrict Name", + showInput: true, + type: "text", + state: "" + }, { + dataField: 'last_updated', + alias: "Last Updated", + showInput: false, + type: "text", + state: "" + }]; + +class MasterSubdistrict extends Component { + constructor(props) { + super(props) + this.state = {} + } + + componentDidMount() {} + + render() { + + return ( +
+ +
+ ) + } +} + +export default MasterSubdistrict; \ No newline at end of file diff --git a/src/views/Master/MasterSubdistrict/package.json b/src/views/Master/MasterSubdistrict/package.json new file mode 100644 index 0000000..46f7fea --- /dev/null +++ b/src/views/Master/MasterSubdistrict/package.json @@ -0,0 +1,6 @@ +{ + "name": "MasterSubdistrict", + "version": "0.0.0", + "private": true, + "main": "./MasterSubdistrict.js" +} diff --git a/src/views/Master/MasterTask/DialogForm.js b/src/views/Master/MasterTask/DialogForm.js new file mode 100644 index 0000000..5f7a96d --- /dev/null +++ b/src/views/Master/MasterTask/DialogForm.js @@ -0,0 +1,188 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import 'antd/dist/antd.css'; +import '../MasterDataStyles.css' +import DialogFullScreen from './DialogFullScreen'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import ImageImage from "../../../assets/img/image.png" +const API = `https://oslog.id/geohr-api` + + + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + name:'', + status:'', + deskripsi:'', + created_date:'', + isParentClick:false, + imageName:'', + files:'', + fullScreen:false + } + } + + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (prevProps, prevState){ + const { dataEdit } = this.props + + if(this.state.isParentClick===true){ + // console.log("cek parent click") + this.setState({ + id:dataEdit.id, + name:dataEdit.employee_name, + status:dataEdit.status, + deskripsi:dataEdit.description, + created_date:dataEdit.send_time + },()=>{ + this.getUrlImagery(); + }) + this.setState({isParentClick:false}); + } + } + + + + closeImg = () => { + this.setState({ showImg: false }) + } + + getUrlImagery = async () => { + const payload = { + "paging": { "start": 0, "length": -1 }, + "columns": [ + { "name": "category", "logic_operator": "=", "value": "task", "operator": "and" }, + { "name": "ref_id", "logic_operator": "=", "value": this.state.id, "operator": "and" } + ], + "orders": { "columns": ["id"], "ascending": true } + } + const result = await axios.post(`${API}/image/search`, payload).then(response => response).catch(err => err.response) + // console.log("cek result", result.data) + if(result && result.data && result.data.code===200){ + const resData = result.data.data[0]; + // console.log("cek result", resData) + this.setState({ imageName: resData.image },()=> { + console.log("cek", this.state.imageName) + }) + } + // console.log("cek", result.data.data) + + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + handleCancel = () => { + this.setState({ id:0 }); + this.props.closeDialog('cancel', 'none') + } + + + renderForm = () => { + return( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + {this.state.imageName==="" + ? <> +
+ ... +
+ *Laporan ini tidak mempunyai foto + + : <> +
+ ... +
+ } + +
+ +
+
+ ) + } + + toggleFullScreen = () => { + this.setState({fullScreen:!this.state.fullScreen}) + } + + openFullScreen = () => { + this.setState({fullScreen:true}) + } + + render() { + const {typeDialog} = this.props + return ( + + + + {/* this.setState({ alertDelete: false, dataPhoto: null})} + focusCancelBtn + > + Foto Akan Terhapus!! + */} + this.handleCancel()}>Detail Laporan + + {this.renderForm()} + + {typeDialog !== "View" && ( + + + + + )} + + ) + } +} diff --git a/src/views/Master/MasterTask/DialogFullScreen.js b/src/views/Master/MasterTask/DialogFullScreen.js new file mode 100644 index 0000000..244d26d --- /dev/null +++ b/src/views/Master/MasterTask/DialogFullScreen.js @@ -0,0 +1,32 @@ +import React, { Component } from 'react' +import { Modal, ModalBody } from 'reactstrap'; +const API = `https://oslog.id/geohr-api` + + + +export default class DialogFullScreen extends Component { + constructor(props) { + super(props) + this.state = { + id: 0 + } + } + + renderForm = () => { + return( +
+ ... +
+ ) + } + + render() { + return ( + + + {this.renderForm()} + + + ) + } +} diff --git a/src/views/Master/MasterTask/index.js b/src/views/Master/MasterTask/index.js new file mode 100644 index 0000000..a3167d2 --- /dev/null +++ b/src/views/Master/MasterTask/index.js @@ -0,0 +1,414 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Table, Input } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import { Tooltip } from 'reactstrap'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +// import { Tooltip } from 'reactstrap'; +import { DatePicker } from 'antd'; +import * as XLSX from 'xlsx'; +import eyeFilled from '@iconify/icons-ant-design/eye-filled'; +import { Icon } from '@iconify/react'; + +const id_org = window.localStorage.getItem('id_org'); +const roleName = window.localStorage.getItem('role_name'); + +const { RangePicker } = DatePicker; + +const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; + + +const column = [ + { name: "Lihat Gambar"}, + { name: "Nama" }, + { name: "Status" }, + { name: "Deskripsi" }, + { name: "Waktu dibuat" }, + // { name: "Supervisor" }, +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + dataExport: [], + openDialog: false, + typeDialog: 'Save', + dataKlik: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipViewImage: false, + typeClock: "All", + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + tooltipExport:false, + } + } + + async componentDidMount() { + this.getDataTask(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataTask() + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataTask = async () => { + let start = 0; + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + let url = BASE_URL + `task_report.php?act=get_data&start=${start}&length=${this.state.rowsPerPage}&role_name=${roleName}`; + + let dateStart = this.state.startDate; + let dateEnd = this.state.endDate; + + const formData = new FormData(); + formData.append("startDate", dateStart.format("YYYY-M-D")+" 00:00:00"); + formData.append("endDate", dateEnd.format("YYYY-M-D")+" 23:59:59"); + formData.append("id_org", id_org); + // formData.append("start", start); + // formData.append("length", this.state.rowsPerPage); + formData.append('field', 'name'); + // formData.append('type', this.state.typeClock); + if(this.state.search !== ""){ + formData.append('value', this.state.search); + } + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => error.response); + console.log(result) + if (result.data.code_status == 200) { + this.setState({ dataTable: result.data.data, totalPage: result.data.total_record,dataExport:result.data.data_excel }); + } else { + NotificationManager.error('Gagal Menerima Data!!', 'Failed'); + } + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + // this.showChildDialog(); + } + + handleCloseDialog = (type, data) => { + // if (type === "save") { + // this.saveDivision(data); + // } else if (type === "edit") { + // this.editDataDivision(data); + // } + + this.setState({ openDialog: false }) + } + + // toggleAddDialog = () => { + // this.setState({ openDialog: !this.state.openDialog }) + // } + + // onConfirmDelete = async () => { + // const { idDelete } = this.state + // let url = BASE_URL + `employee_devision.php?act=delete&id=${idDelete}`; + // const result = await axios.post(url) + // .then(res => res) + // .catch((error) => error.response); + + // if(result.message!==undefined){ + // if (result.message === "Data Has Been Deleted") { + // this.getDataTask() + // this.setState({ idDelete: 0, alertDelete: false }) + // NotificationManager.success('Data division berhasil dihapus!!', 'Success!!'); + // } else { + // this.setState({ idDelete: 0, alertDelete: false }) + // NotificationManager.error('Data division gagal dihapus!!', 'Failed!!'); + // } + // }else{ + // if (result.data.message === "Data Has Been Deleted") { + // this.getDataTask() + // this.setState({ idDelete: 0, alertDelete: false }) + // NotificationManager.success('Data division berhasil dihapus!!', 'Success!!'); + // } else { + // this.setState({ idDelete: 0, alertDelete: false }) + // NotificationManager.error('Data division gagal dihapus!!', 'Failed!!'); + // } + // } + // } + + // saveDivision = async (data) => { + // let url = BASE_URL + "employee_devision.php?act=input"; + + // const formData = new FormData() + // formData.append('name', data.name); + // formData.append('description', data.description); + // const result = await axios.post(url, formData) + // .then(res => res) + // .catch((error) => error.response); + + // if(result.message!==undefined){ + // if (result.message === "Data Has Been Saved") { + // this.getDataTask(); + // NotificationManager.success('Data division berhasil ditambahkan!!', 'Success!!'); + // } else { + // NotificationManager.error(`${result.data.message}`, 'Failed!!'); + // } + // }else{ + // if (result.data.message === "Data Has Been Saved") { + // this.getDataTask(); + // NotificationManager.success('Data division berhasil ditambahkan!!', 'Success!!'); + // } else { + // NotificationManager.error(`${result.data.message}`, 'Failed!!'); + // } + // } + + // } + + // editDataDivision = async (data) => { + // let url = BASE_URL + `employee_devision.php?act=edit&id=${data.id}`; + + // const formData = new FormData(); + // formData.append('name', data.name); + // formData.append('description', data.description); + + // const result = await axios.post(url, formData) + // .then(res => res) + // .catch((error) => error.response); + + // if(result.message!==undefined){ + // if (result.message === "Data Has Been Edited") { + // this.getDataTask(); + // NotificationManager.success('Data division berhasil diedit!!', 'Success!!'); + // } else { + // NotificationManager.error('Data division gagal diedit!!', 'Failed!!'); + // } + // }else{ + // if (result.data.message === "Data Has Been Edited") { + // this.getDataTask(); + // NotificationManager.success('Data division berhasil diedit!!', 'Success!!'); + // } else { + // NotificationManager.error('Data division gagal diedit!!', 'Failed!!'); + // } + // } + + // } + + + handleViewImage = (data) => { + this.setState({ dataKlik: data }); + this.handleOpenDialog('View'); + this.showChildDialog(); + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataTask(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataTask(); + }) + } + + toggle = (param) => { + switch (param) { + case "export": + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + break; + case "view-image": + this.setState(prevState => ({ tooltipViewImage: !prevState.tooltipViewImage })) + break; + + // default: + // break; + } + } + + handleDatePicker = (date, dateString) => { + this.setState({ startDate:date[0],endDate:date[1] },()=>{ + this.getDataTask(); + }) + } + + handleExportExcel = async () => { + const dataExcel = []; + const start = 0; + const length = "All"; + let url = BASE_URL + `task_report.php?act=get_data&start=${start}&length=${length}&role_name=${roleName}`; + + let dateStart = this.state.startDate; + let dateEnd = this.state.endDate; + + const formData = new FormData(); + formData.append("startDate", dateStart.format("YYYY-M-D")+" 00:00:00"); + formData.append("endDate", dateEnd.format("YYYY-M-D")+" 23:59:59"); + formData.append("id_org", id_org); + // formData.append("start", start); + // formData.append("length", this.state.rowsPerPage); + formData.append('field', 'name'); + // formData.append('type', this.state.typeClock); + if(this.state.search !== ""){ + formData.append('value', this.state.search); + } + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if (result && result.data && result.data.code_status == 200) { + const resData = result.data.data; + // {n.employee_name} + // { n.status!==null ? n.status : "-" } + // { n.description!==null ? n.description : "-" } + // { n.send_time!==null ? n.send_time : "-" } + resData.map((val, index)=> { + dataExcel.push( + { + "Nama Karyawan":val.employee_name, + "Status":val.status, + "Laporan":val.description, + "Tanggal Dibuat":val.send_time + } + ) + }) + } else { + NotificationManager.error('Gagal Menerima Data!!', 'Failed'); + } + this.downloadExcel(dataExcel) + } + + downloadExcel = (dataExcel) => { + const fileName = "Laporan Karyawan"+this.state.startDate.format("D-M-YYYY")+" - "+this.state.endDate.format("D-M-YYYY")+".xlsx"; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'Laporan Karyawan'); + + XLSX.writeFile(wb, fileName); + } + + renderTable = () => { + const dataTable2 = this.state.dataTable || []; + return ( + + {dataTable2.length!==0 ? dataTable2.map((n) => { + return ( + + + + this.handleViewImage(n)}> + + + this.toggle("view-image")}> + Lihat Gambar + + + {n.employee_name} + { n.status!==null ? n.status : "-" } + { n.description!==null ? n.description : "-" } + { n.send_time!==null ? n.send_time : "-" } + {/* { n.supervisor!==null ? n.supervisor : "-" } */} + + ) + }) : + No Data Available + + } + + ) + } + + render() { + const { tooltipExport ,dataTable, openDialog, currentPage, rowsPerPage, totalPage, search } = this.state + return ( +
+ + {/* this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data tipe karyawan akan terhapus!! + */} + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataKlik} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + + +

Laporan Karyawan

+
+ this.toggle("export")}> + Export Excel + + +
+
+ +
+
+ {' '} + +
+ +
+ + + + {/* */} + {column.map((i, index) => { + return ( + + ) + })} + + + { this.renderTable() } +
Actions{i.name}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterTipeKaryawan/DialogForm.js b/src/views/Master/MasterTipeKaryawan/DialogForm.js new file mode 100644 index 0000000..ff07f51 --- /dev/null +++ b/src/views/Master/MasterTipeKaryawan/DialogForm.js @@ -0,0 +1,113 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Select from 'react-select' +import axios from 'axios'; +import moment from 'moment'; +import { TimePicker } from 'antd'; +import 'antd/dist/antd.css'; + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + name:"", + description:"", + openDialog: false, + isParentClick: false, + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + if(this.props.typeDialog==="Edit"){ + const { dataEdit } = this.props + this.setState({ + id:dataEdit.id, + name:dataEdit.name, + description:dataEdit.description + }) + }else{ + this.setState({ + id:0, + name:"", + description:"" + }) + } + this.setState({isParentClick:false}); + } + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + const { + id, + name, + description + } = this.state + + let data = ''; + if(this.props.typeDialog==="Save"){ + data = { + id, + name, + description + } + this.props.closeDialog('save', data); + }else{ + data = { + id, + name, + description + } + this.props.closeDialog('edit', data); + } + + this.setState({ id:0 }); + + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + renderForm = () => { + return( +
+ + + this.setState({ name:e.target.value })} placeholder="name division.." /> + + + + this.setState({ description:e.target.value })} placeholder="deskripsi.." /> + +
+ ) + } + + render() { + return ( + + {this.props.typeDialog=="Save" ? "Tambah" : "Edit"} Divisi + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/MasterTipeKaryawan/index.js b/src/views/Master/MasterTipeKaryawan/index.js new file mode 100644 index 0000000..3704673 --- /dev/null +++ b/src/views/Master/MasterTipeKaryawan/index.js @@ -0,0 +1,380 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination } from 'antd'; +import { Tooltip } from 'reactstrap'; +import * as XLSX from 'xlsx'; + +const BASE_URL = "http://siopas.co.id/custom-php/api/geohr/"; + +const momentFormat = 'HH:mm'; + +const column = [ + { name: "Nama" }, + { name: "Deskripsi" }, +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + openDialog: false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipEdit: false, + tooltipDelete: false, + tooltipTambah:false, + tooltipImport:false, + tooltipExport:false, + dataExport:[] + } + } + + async componentDidMount() { + this.getDataDivision(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search } = this.state + if (search !== prevState.search) this.getDataDivision() + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataDivision = async () => { + let url = BASE_URL + "employee_devision.php?act=get_data"; + + let start = 0; + + if (this.state.currentPage !== 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + const formData = new FormData(); + + formData.append('start', start); + formData.append('length', this.state.rowsPerPage); + + formData.append('field', 'name'); + if(this.state.search !== ""){ + formData.append('value', this.state.search); + } + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + + if(result){ + if(result.data){ + if (result.data.code_status == 200) { + console.log("sales", result.data.data) + this.setState({ dataTable: result.data.data, totalPage: result.data.total_record }); + } else { + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + } + } + + } + + handleOpenDialog = (type) => { + this.setState({ openDialog: true, typeDialog: type }) + this.showChildDialog(); + } + + handleCloseDialog = (type, data) => { + if (type === "save") { + this.saveDivision(data); + } else if (type === "edit") { + this.editDataDivision(data); + } + + this.setState({ openDialog: false }) + } + + toggleAddDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + onConfirmDelete = async () => { + const { idDelete } = this.state + let url = BASE_URL + `employee_devision.php?act=delete&id=${idDelete}`; + const result = await axios.post(url) + .then(res => res) + .catch((error) => error.response); + + if(result.message!==undefined){ + if (result.message === "Data Has Been Deleted") { + this.getDataDivision() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success('Data divisi berhasil dihapus!!', 'Success!!'); + } else { + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error('Data divisi gagal dihapus!!', 'Failed!!'); + } + }else{ + if (result.data.message === "Data Has Been Deleted") { + this.getDataDivision() + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.success('Data divisi berhasil dihapus!!', 'Success!!'); + } else { + this.setState({ idDelete: 0, alertDelete: false }) + NotificationManager.error('Data divisi gagal dihapus!!', 'Failed!!'); + } + } + } + + saveDivision = async (data) => { + let url = BASE_URL + "employee_devision.php?act=input"; + + const formData = new FormData() + formData.append('name', data.name); + formData.append('description', data.description); + const result = await axios.post(url, formData) + .then(res => res) + .catch((error) => error.response); + + if(result.message!==undefined){ + if (result.message === "Data Has Been Saved") { + this.getDataDivision(); + NotificationManager.success('Data divisi berhasil ditambahkan!!', 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + }else{ + if (result.data.message === "Data Has Been Saved") { + this.getDataDivision(); + NotificationManager.success('Data divisi berhasil ditambahkan!!', 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + } + + editDataDivision = async (data) => { + let url = BASE_URL + `employee_devision.php?act=edit&id=${data.id}`; + + const formData = new FormData(); + formData.append('name', data.name); + formData.append('description', data.description); + + const result = await axios.post(url, formData) + .then(res => res) + .catch((error) => error.response); + + if(result.message!==undefined){ + if (result.message === "Data Has Been Edited") { + this.getDataDivision(); + NotificationManager.success('Data divisi berhasil diedit!!', 'Success!!'); + } else { + NotificationManager.error('Data divisi gagal diedit!!', 'Failed!!'); + } + }else{ + if (result.data.message === "Data Has Been Edited") { + this.getDataDivision(); + NotificationManager.success('Data divisi berhasil diedit!!', 'Success!!'); + } else { + NotificationManager.error('Data divisi gagal diedit!!', 'Failed!!'); + } + } + + } + + + handleEdit = (data) => { + this.setState({ dataEdit: data }); + this.handleOpenDialog('Edit'); + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataDivision(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataDivision(); + }) + } + + toggle = (param) => { + if (param === "edit") { + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "tambah") { + this.setState(prevState => ({ tooltipTambah: !prevState.tooltipTambah })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + } + + handleExportExcel = async () => { + let start = 0; + let end = "ALL"; + let url = BASE_URL + "employee_devision.php?act=get_data"; + const formData = new FormData(); + formData.append('field', this.state.searchDetailField); + formData.append('value', this.state.search); + // console.log("test", url); + const result = await axios + .post(url,formData) + .then(res => res) + .catch((error) => error.response); + // console.log("result", result); + if (result && result.data && result.statusText == "OK") { + const dataRes = result.data.data|| []; + console.log("data result", dataRes); + const dataExport = []; + dataRes.map((val,index)=> { + let row = { + Nama:val.name, + Deskripsi:val.description, + } + dataExport.push(row); + }) + console.log("data Export", dataExport); + this.setState({ dataExport:dataExport },()=> { + this.exportExcel(); + }); + } else { + NotificationManager.error('Failed retreiving data!!', 'Failed'); + } + } + + exportExcel = () => { + const dataExcel = this.state.dataExport || []; + const fileName = "Data Divisi Karyawan.xlsx"; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'Data Divisi Karyawan'); + + XLSX.writeFile(wb, fileName); + } + + render() { + const { tooltipTambah,tooltipExport,dataTable, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipEdit, tooltipDelete } = this.state + let noSeq = 0; + return ( +
+ + this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data tipe karyawan akan terhapus!! + + this.toggleAddDialog} + typeDialog={this.state.typeDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showChildDialog = showDialog} + dataHs={this.state.dataIdHo} + /> + + +

Divisi Karyawan

+ + + + + + + + {/* */} + this.toggle("tambah")}> + Tambah + + this.toggle("export")}> + Export Excel + + + +
+ + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataTable.map((n) => { + return ( + + + + + + + ) + })} + +
Aksi{i.name}
+ this.handleDelete(n.id)}> + this.toggle("delete")}> + Hapus + + + this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + {n.name}{n.description}
+ +
+
+
+ ) + } +} diff --git a/src/views/Master/MasterVillage/MasterVillage.css b/src/views/Master/MasterVillage/MasterVillage.css new file mode 100644 index 0000000..e69de29 diff --git a/src/views/Master/MasterVillage/MasterVillage.js b/src/views/Master/MasterVillage/MasterVillage.js new file mode 100644 index 0000000..5be3fdf --- /dev/null +++ b/src/views/Master/MasterVillage/MasterVillage.js @@ -0,0 +1,52 @@ +import React, { Component } from 'react' +// import './MasterVillage.css' +import DataTable from '../../../components/DataTable' +import { API_LIST_DATA_VILLAGE, API_INSERT_DATA_VILLAGE, API_UPDATE_DATA_VILLAGE, API_DELETE_DATA_VILLAGE } from '../../../const/ApiConst.js' + +// important! +const columns = [{ + dataField: 'id', + alias: "Id", + showInput: false, + type: "number", + state: 0 + }, { + dataField: 'village_name', + alias: "Village Name", + showInput: true, + type: "text", + state: "" + }, { + dataField: 'last_updated', + alias: "Last Updated", + showInput: false, + type: "text", + state: "" + }]; + +class MasterVillage extends Component { + constructor(props) { + super(props) + this.state = {} + } + + componentDidMount() {} + + render() { + + return ( +
+ +
+ ) + } +} + +export default MasterVillage; \ No newline at end of file diff --git a/src/views/Master/MasterVillage/package.json b/src/views/Master/MasterVillage/package.json new file mode 100644 index 0000000..536cadc --- /dev/null +++ b/src/views/Master/MasterVillage/package.json @@ -0,0 +1,6 @@ +{ + "name": "MasterVillage", + "version": "0.0.0", + "private": true, + "main": "./MasterVillage.js" +} diff --git a/src/views/Master/PlanningVsRealisasi/DialogEdit.js b/src/views/Master/PlanningVsRealisasi/DialogEdit.js new file mode 100644 index 0000000..921bc71 --- /dev/null +++ b/src/views/Master/PlanningVsRealisasi/DialogEdit.js @@ -0,0 +1,106 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Select from 'react-select' +import axios from 'axios'; +import moment from 'moment'; +import { TimePicker } from 'antd'; +import 'antd/dist/antd.css'; + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + lat:"", + lon:"", + status:"", + name:"", + openDialog: false, + isParentClick: false, + disable: false + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + const { dataEdit } = this.props + console.log("cek", dataEdit); + this.setState({ + id:dataEdit.id, + lat:dataEdit.lat, + lon:dataEdit.lon, + status:dataEdit.status_response, + name:dataEdit.employee_name + }) + + this.setState({isParentClick:false}); + } + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + const { + lat, + lon, + status, + id + } = this.state + + let data = { + lat, + lon, + status_response:status, + id + } + this.props.closeDialog('save', data); + this.setState({ id:0,lat:"",lon:"",status:'active' }); + + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + renderForm = () => { + return( +
+ + + + + + + this.setState({ status:e.target.value })}> + + + + +
+ ) + } + + render() { + return ( + + Edit Status Response + + {this.renderForm()} + + + {' '} + + + + ) + } +} diff --git a/src/views/Master/PlanningVsRealisasi/DialogForm.js b/src/views/Master/PlanningVsRealisasi/DialogForm.js new file mode 100644 index 0000000..75ba380 --- /dev/null +++ b/src/views/Master/PlanningVsRealisasi/DialogForm.js @@ -0,0 +1,86 @@ +import React, { Component } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Col, Row, Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import Map from './Map' +import 'antd/dist/antd.css'; + +const ERROR_LATLON = "Geom cannot be empty" +const ERROR_COMPANY = "Company cannot be empty" +const ERROR_NAME = "name cannot be empty" +const ERROR_RADIUS = "Buffer Radius cannot be empty" + +export default class DialogForm extends Component { + constructor(props) { + super(props) + this.state = { + id: 0, + lat:0, + lon:0, + openDialog: false, + isParentClick: false, + } + } + + async componentDidMount(){ + this.props.showDialog(this.showDialog); + } + + async componentDidUpdate (){ + if(this.state.isParentClick===true){ + const { dataMap } = this.props + // console.log(dataMap) + this.setState({ + lat:dataMap.lat, + lon:dataMap.lon + }) + this.setState({isParentClick:false}); + } + } + + + showDialog = () => { + this.setState({ isParentClick : true }); + } + + + handleSave = () => { + + } + + handleCancel = () => { + this.props.closeDialog('cancel', 'none') + } + + getLocation = (param) => { + console.log(`getLocation`, param) + this.setState({ + latlonMessage: param != "" ? "" : ERROR_LATLON, + lat: param.lat, + lon: param.lng, + latlon: `lat:${param.lat} lng:${param.lng}` + }) + } + + renderForm = () => { + return( +
+ +
+ ) + } + + render() { + return ( + + Map + + {this.renderForm()} + + + {/* {' '} */} + + + + ) + } +} diff --git a/src/views/Master/PlanningVsRealisasi/DialogView.js b/src/views/Master/PlanningVsRealisasi/DialogView.js new file mode 100644 index 0000000..9269cc6 --- /dev/null +++ b/src/views/Master/PlanningVsRealisasi/DialogView.js @@ -0,0 +1,120 @@ +import React, { useEffect, useMemo, useState } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input, Col, Row } from 'reactstrap'; +import { Select, Table } from 'antd' +import { BASE_SIMPRO } from '../../../const/ApiConst'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +// import { Language } from 'src/const/LanguageConst'; +// import moment from 'moment'; +import 'antd/dist/antd.css'; +import axios from 'axios'; +import moment from 'moment'; + +const { Option } = Select + +const DialogView = ({ openDialog, closeDialog, toggleDialog, dataPlanning, nameProyekParent }) => { + const token = localStorage.getItem("token") + const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + } + } + const [dataPlan, setDataPlan] = useState([]); + + useEffect(() => { + setDataPlan(dataPlanning) + }, [dataPlanning]); + + // const handleGetDataProyek = async () => { + // const URL = `${BASE_SIMPRO}/proyek/list` + // const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + // console.log("proyek", result) + // if (result.data.code == 200) { + // setDataProyek(result.data.data) + // } else { + // NotificationManager.error('Gaga Mengambil Data!!', 'Failed'); + // } + // } + + const RenderTableLaporanPlan = (tableData) => { + const columns = [ + { + title: 'Tanggal', + dataIndex: 'tanggal', + key: 'tanggal', + render: (text, record) => <>{moment(text.tanggal).format("YYYY-MM-DD")}, + }, + { title: 'Jumlah Pekerja', dataIndex: 'jumlah_pekerjaan', key: 'jumlah_pekerjaan' }, + { title: 'Status', dataIndex: 'status', key: 'status' }, + { title: 'deskripsi', dataIndex: 'deskripsi', key: 'deskripsi' }, + ]; + + return ( + RenderTable(record.subproyeks), + // rowExpandable: record => record.subproyeks, + // }} + dataSource={tableData} + /> + ) + } + + const RenderTablePlan = useMemo(() => { + let idx = 0 + const columns = [ + { + title: 'No', + dataIndex: 'no', + key: 'id', + render: (text, record) => <>{idx+1}, + }, + + { + title: 'Waspang', + dataIndex: 'created_by', + key: 'created_by', + render: (text, record) => <>{record.created_by}, + }, + { title: 'Jumlah Pekerjaan', dataIndex: 'jumlah_pekerjaan', key: 'jumlah_pekerjaan' }, + { + title: 'Deskripsi', + dataIndex: 'deskripsi', + key: 'deskripsi', + render: (text, record) => <>{record.deskripsi}, + }, + ]; + + return ( +
RenderTableLaporanPlan(record.realisasi), + rowExpandable: record => record.realisasi, + }} + dataSource={dataPlan} + /> + ) + }, [dataPlan]) + + + return ( + + View Realisasi + + {RenderTablePlan} + + + {/* {' '} */} + + + + ) + +} + +export default DialogView; \ No newline at end of file diff --git a/src/views/Master/PlanningVsRealisasi/Map.js b/src/views/Master/PlanningVsRealisasi/Map.js new file mode 100644 index 0000000..ba7bd92 --- /dev/null +++ b/src/views/Master/PlanningVsRealisasi/Map.js @@ -0,0 +1,90 @@ +import React, { useState, useRef, useMemo, useCallback, useEffect } from 'react' +import { MapContainer, TileLayer, Marker, Popup, Polygon } from 'react-leaflet' + +const center = { + lat: -6.200000, + lng: 106.816666 +} + +const DraggableMarker = (props) => { + const { method } = props + // const center = [-6.200000, 106.816666] + let lat = props.lat + let lng = props.lng + const currentPos = [lat, lng] + console.log(`currentPos`, currentPos) + + + + // console.log(`DraggableMarker`, currentPosition) + const [draggable, setDraggable] = useState(true) + const [position, setPosition] = useState(center) + + // console.log(`position`, position) + + const markerRef = useRef(null) + const eventHandlers = useMemo( + () => ({ + dragend() { + const marker = markerRef.current + // console.log(`marker.getLatLng()`, marker.getLatLng()) + if (marker != null) { + setPosition(marker.getLatLng()) + props.getLocation(marker.getLatLng()) + } + }, + }), + [], + ) + return ( + <> + + {method === "View" && ( + + + )} + + {method === "Edit" && ( + + + )} + {method === "Save" && ( + + + )} + + ) +} + + +const purpleOptions = { color: 'purple' } +const RenderMap = (props) => { + let lat = props.lat + let lng = props.lng + // console.log(`latlon`, { lat, lng }) + const currentPos = (lat !== "" && lng !== "") ? [lat, lng] : center + // console.log(`currentPos`, currentPos) + return ( + + {/* */} + + + + ) +} +export default RenderMap; \ No newline at end of file diff --git a/src/views/Master/PlanningVsRealisasi/index.js b/src/views/Master/PlanningVsRealisasi/index.js new file mode 100644 index 0000000..fa36ee3 --- /dev/null +++ b/src/views/Master/PlanningVsRealisasi/index.js @@ -0,0 +1,625 @@ +import React, { Component } from 'react'; +import { Card, CardBody, CardHeader, Col, Row, Table, Input, InputGroup } from 'reactstrap'; +import { Button } from 'reactstrap'; +import axios from 'axios'; +import moment from 'moment'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import DialogForm from './DialogForm'; +import DialogEdit from './DialogEdit'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Pagination, Tooltip } from 'antd'; +import { DatePicker } from 'antd'; +import * as XLSX from 'xlsx'; +import { PLANNING_REALISASI_SEARCH, PLANNING_SEARCH } from '../../../const/ApiConst.js'; +import DialogView from './DialogView'; + + +const { RangePicker } = DatePicker; + +const token = window.localStorage.getItem('token'); +const config = { + headers: + { + Authorization : `Bearer ${token}`, + "Content-type" : `application/json` + } +}; + +const proyek_id = localStorage.getItem('proyek_id'); +const role_id = localStorage.getItem('role_id'); + +const column = [ + { name: "No" }, + { name: "Proyek" }, + { name: "Pekerjaan" }, + { name: "Target" }, + { name: "Actual" }, + { name: "Team Leader" }, + { name: "Waspang" }, + { name: "Tanggal" }, + { name: "Lihat" }, +] + +const LENGTH_DATA = 10 + +export default class index extends Component { + constructor(props) { + super(props) + this.state = { + dataTable: [], + dataExport: [], + openDialog: false, + openDialogEdit:false, + typeDialog: 'Save', + dataEdit: null, + alertDelete: false, + idDelete: 0, + dataGs: [], + dataIdHo: [], + search: "", + page: 0, + rowsPerPage: LENGTH_DATA, + currentPage: 1, + totalPage: 0, + tooltipMap: false, + tooltipDelete: false, + typeClock: "All", + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentDay: 'today', + dataMap:"", + tooltipExport:false, + openDialogPlan: false, + dataRealisasi: [] + } + } + + async componentDidMount() { + this.getDataReportPlanning(); + } + + async componentDidUpdate(prevProps, prevState) { + const { search,startDate,dataExport } = this.state + if (search !== prevState.search) this.getDataReportPlanning() + if (startDate !== prevState.startDate) this.getDataReportPlanning() + if (dataExport !== prevState.dataExport){ + if(dataExport.length > 0){ + this.exportExcel() + } + } + } + + handleSearch = e => { + const value = e.target.value + this.setState({ search: value, currentPage: 1 }) + }; + + getDataReportPlanning = async () => { + let start = 0; + if (this.state.currentPage !== 1 && this.state.currentPage > 1) { + start = (this.state.currentPage * this.state.rowsPerPage) - this.state.rowsPerPage + } + + let dateStart = moment(this.state.startDate).format("YYYY-MM-DD 00:00:00"); + let dateEnd = moment(this.state.endDate).format("YYYY-MM-DD 23:59:59"); + + + + const formData = { + "columns": [ + { + "name": "created_at", + "logic_operator": "range", + "value": dateStart, + "value1": dateEnd, + "operator": "AND" + }, + { + "name": "nama", + "logic_operator": "like", + "value": this.state.search, + "operator": "AND", + } + ], + "joins": [ + { + "name": "m_proyek", + "column_join": "proyek_id", + "column_results": [ + "nama", + "biaya", + "color_progress", + "jumlah_pekerja", + "pic", + "mulai_proyek", + "akhir_proyek" + ] + }, + { + "name": "m_subproyek", + "column_join": "subproyek_id", + "column_results": [ + "nama", + "biaya", + "color_progress", + "jumlah_pekerja", + "pic", + "mulai_proyek", + "akhir_proyek" + ] + }, + { + "name": "m_users", + "column_join": "user_id", + "column_results": [ + "name", + "username", + "email", + "phone_number", + "gender" + ] + } + ], + "orders": { + "columns": [ + "planning_id" + ], + "ascending": true + }, + "paging": { + "start": start, + "length": this.state.rowsPerPage + } + } + + if(parseInt(role_id)!==1){ + formData.columns.push( + { + "name": "id", + "logic_operator": "=", + "value": proyek_id, + "operator": "AND", + "table_name": "m_proyek" + } + ) + } + + const result = await axios + .post(PLANNING_REALISASI_SEARCH, formData, config) + .then(res => res) + .catch((error) => error.response); + + if(result && result.data && result.data.code==200){ + console.log("cek res planning", result.data.data) + this.setState({ dataTable: result.data.data, totalPage: result.data.totalRecord }); + }else{ + NotificationManager.error('Gagal Menerima Data!!', 'Failed'); + } + } + + handleCloseDialogPlan = () => { + this.setState({openDialogPlan: false}) + } + + handleOpenDialogPlan = (param) => { + this.setState({openDialogPlan: true}) + this.setState({dataRealisasi: param.realisasi}) + } + + + toggleAddDialogPlan = () => { + this.setState({openDialogPlan: !this.stateopenDialogPlan}) +} + + + handleOpenDialog = (type) => { + if(type==="Map"){ + this.setState({ openDialog: true }) + this.showChildDialog(); + }else{ + this.setState({ openDialogEdit: true }) + this.showDialogEdit(); + } + + } + + handleCloseDialog = () => { + this.setState({ openDialog: false }) + } + + handleCloseDialogEdit = (type, data) => { + if(type==="save"){ + this.updateStatusResponse(data); + } + this.setState({ openDialogEdit: false }) + } + + toggleMapDialog = () => { + this.setState({ openDialog: !this.state.openDialog }) + } + + toggleEditDialog = () => { + this.setState({ openDialogEdit:!this.state.openDialogEdit }); + } + + handleMap = data => { + this.setState({ dataMap: data }); + this.handleOpenDialog('Map'); + } + + handleEdit = data => { + this.setState({ dataEdit:data }); + this.handleOpenDialog('Edit'); + } + + handleDelete = (id) => { + this.setState({ alertDelete: true, idDelete: id }); + } + + onShowSizeChange = (current, pageSize) => { + this.setState({ rowsPerPage: pageSize }, () => { + this.getDataReportPlanning(); + }) + } + + onPagination = (current, pageSize) => { + this.setState({ currentPage: current, page: (current - 1) * pageSize }, () => { + this.getDataReportPlanning(); + }) + } + + toggle = (param) => { + if (param === "map") { + this.setState(prevState => ({ tooltipMap: !prevState.tooltipMap })) + }else if(param==="edit"){ + this.setState(prevState => ({ tooltipEdit: !prevState.tooltipEdit })) + } else if (param === "delete") { + this.setState(prevState => ({ tooltipDelete: !prevState.tooltipDelete })) + } else if (param === "export") { + this.setState(prevState => ({ tooltipExport: !prevState.tooltipExport })) + } + } + + handleDatePicker = (date, dateString) => { + this.setState({ startDate:date[0],endDate:date[1] },()=>{ + this.getDataReportPlanning(); + }) + } + + handleTipe = (e) => { + this.setState({ typeClock:e.target.value }, () => { + this.getDataReportPlanning(); + }); + } + + handleExportExcel = async () => { + let dateStart = moment(this.state.startDate).format("YYYY-MM-DD 00:00:00"); + let dateEnd = moment(this.state.endDate).format("YYYY-MM-DD 23:59:59"); + + const payload = { + "columns": [ + { + "name": "created_at", + "logic_operator": "range", + "value": dateStart, + "value1": dateEnd, + "operator": "AND" + }, + { + "name": "nama", + "logic_operator": "like", + "value": this.state.search, + "operator": "AND", + } + ], + "joins": [ + { + "name": "m_proyek", + "column_join": "proyek_id", + "column_results": [ + "nama", + "biaya", + "color_progress", + "jumlah_pekerja", + "pic", + "mulai_proyek", + "akhir_proyek" + ] + }, + { + "name": "m_subproyek", + "column_join": "subproyek_id", + "column_results": [ + "nama", + "biaya", + "color_progress", + "jumlah_pekerja", + "pic", + "mulai_proyek", + "akhir_proyek" + ] + }, + { + "name": "m_users", + "column_join": "user_id", + "column_results": [ + "name", + "username", + "email", + "phone_number", + "gender" + ] + } + ], + "orders": { + "columns": [ + "planning_id" + ], + "ascending": true + }, + "paging": { + "start": 0, + "length": -1 + } + } + + if(parseInt(role_id)!==1){ + payload.columns.push( + { + "name": "id", + "logic_operator": "=", + "value": proyek_id, + "operator": "AND", + "table_name": "m_proyek" + } + ) + } + + const result = await axios + .post(PLANNING_REALISASI_SEARCH, payload, config) + .then(res => res) + .catch((error) => error.response); + + // console.log(result) + + if(result && result.data && result.data.code == 200){ + let resData = result.data.data; + const excelData = []; + // + // + // + // + // + // + // + // + resData.map((n, index) => { + let dataRow = { + "Proyek": n.join ? n.join.m_proyek_nama : "-", + "Pekerjaan": n.nama ? n.nama : "-", + "Target" : n.jumlah_titik ? n.jumlah_titik : "0", + "Actual" : n.realisasi ? this.renderActual(n.realisasi) : "0", + "Team Leader" : n.join.m_subproyek_pic ? `${n.join.m_subproyek_pic}` : "-", + "Waspang" : n.join.m_users_name ? `${n.join.m_users_name}` : "-", + "Tanggal" : n.target_planning ? moment(n.target_planning).format("DD-MM-YYYY") : "-", + } + excelData.push(dataRow) + }) + // console.log("cek excel data", excelData) + this.setState({dataExport:excelData}) + // exportExcel(); + }else{ + NotificationManager.error('Gagal Export Data!!', 'Failed'); + } + } + + exportExcel = () => { + const dataExcel = this.state.dataExport || []; + const fileName = `Data Planning vs realisasi.xlsx`; + const ws = XLSX.utils.json_to_sheet(dataExcel); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, `Data Planning vs realisasi`); + + XLSX.writeFile(wb, fileName); + this.setState({dataExport:[] }) + } + + updateStatusResponse = async (data) => { + let url = ``; + + const formData = new FormData(); + formData.append("lat", data.lat); + formData.append("lon", data.lon); + formData.append("status_response", data.status_response); + + const result = await axios + .post(url, formData) + .then(res => res) + .catch((error) => error.response); + // console.log(result) + if(result && result.data){ + if (result.data.code_status == 200) { + NotificationManager.success('Berhasil update status response!!', 'Success!'); + } else { + NotificationManager.error('Gagal update status response!!', 'Failed'); + } + } + } + + renderActual = (realisasi) => { + let data = realisasi || [] + let sumActual = 0 + data.map((val, index) => { + sumActual += parseInt(val.jumlah_pekerjaan) + }) + return sumActual + } + + renderTable = () => { + const dataTable2 = this.state.dataTable || []; + return ( + + {dataTable2.length!==0 ? dataTable2.map((n, index) => { + return ( + + + + + + + + + + + + ) + }) : + + + } + + ) + } + + handleChangeDay = (e) => { + const val = e.target.value; + this.setState({ currentDay:val }); + if(val==="today"){ + // console.log("test 1 test",val); + this.setState({ + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else if(val==="3 day"){ + // console.log("test test",val); + this.setState({ + startDate:moment(moment().subtract(3, "days").format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else if(val==="7 day"){ + // console.log("test test",val); + this.setState({ + startDate:moment(moment().subtract(7, "days").format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + }else{ + // console.log("test 2 test",val); + this.setState({ + startDate:moment(moment().format("YYYY-M-D")), + endDate:moment(moment().format("YYYY-M-D")), + currentPage: 1 + }) + } + } + + render() { + const { tooltipExport,dataTable, openDialogEdit, openDialog, currentPage, rowsPerPage, totalPage, search, tooltipMap, tooltipDelete } = this.state + return ( +
+ + {/* this.setState({ alertDelete: false, idDelete: 0 })} + focusCancelBtn + > + Data tipe karyawan akan terhapus!! + */} + this.toggleMapDialog} + dataMap={this.state.dataMap} + showDialog={showDialog => this.showChildDialog = showDialog} + /> + this.toggleEditDialog} + dataEdit={this.state.dataEdit} + showDialog={showDialog => this.showDialogEdit = showDialog} + /> + + +

Planning Vs Realisasi

+
+ {/* {' '} */} + + + +
+
+ +
+
+
+ this.handleChangeDay(e)} defaultValue={this.state.currentDay}> + + + + +
+
+ {' '} + +
+
+ +
+
{index + 1}{ n.join ? n.join.m_proyek_nama : "-" }{ n.nama ? n.nama : "-"}{ n.jumlah_titik ? n.jumlah_titik : "0" }{ n.realisasi ? this.renderActual(n.realisasi) : "0" }{ n.join.m_subproyek_pic ? `${n.join.m_subproyek_pic}` : "-" }{ n.join.m_users_name ? `${n.join.m_users_name}` : "-" }{ n.target_planning ? moment(n.target_planning).format("DD-MM-YYYY") : "-" }
{index + 1}{ n.join ? n.join.m_proyek_nama : "-" }{ n.nama ? n.nama : "-"}{ n.jumlah_titik ? n.jumlah_titik : "0" }{ n.realisasi ? this.renderActual(n.realisasi) : "0" }{ n.join.m_subproyek_pic ? `${n.join.m_subproyek_pic}` : "-" }{ n.join.m_users_name ? `${n.join.m_users_name}` : "-" }{ n.target_planning ? moment(n.target_planning).format("DD-MM-YYYY") : "-" } + {/* this.handleDelete(n.id)}> + this.toggle("delete")}> + Delete + */} + {/* this.handleEdit(n)}> + this.toggle("edit")}> + Edit + + {' '} */} + + this.handleOpenDialogPlan(n)} id="tooltipMap" className="fa fa-eye fa-lg" style={{ color: 'black', cursor: "pointer" }}> + +
No Data Available
+ + + {/* */} + {column.map((i, index) => { + return ( + + ) + })} + + + { this.renderTable() } +
Actions{i.name}
+ +
+
+ + this.handleCloseDialogPlan()} + toggleDialog={() => this.toggleAddDialogPlan()} + dataPlanning={this.state.dataRealisasi} + // nameProyekParent={nameProyek} + /> +
+ ) + } +} diff --git a/src/views/Master/Proyek/DialogForm.js b/src/views/Master/Proyek/DialogForm.js new file mode 100644 index 0000000..a0ae78d --- /dev/null +++ b/src/views/Master/Proyek/DialogForm.js @@ -0,0 +1,275 @@ +import React, { useEffect, useState } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input, Col, Row } from 'reactstrap'; +import { DatePicker, Tooltip } from 'antd'; +// import { Language } from 'src/const/LanguageConst'; +import moment from 'moment'; +import 'antd/dist/antd.css'; +// const { Option } = Select +import DialogMap from './DialogMap'; + +const DialogForm = ({openDialog, closeDialog, toggleDialog, typeDialog, dataEdit}) => { + const [id, setId] = useState(0) + const [name, setName] = useState('') + const [description, setDescription] = useState('') + const [biaya, setBiaya] = useState(0) + const [color, setColor] = useState('green') + const [sdm, setSdm] = useState(0) + const [pic, setPic] = useState('') + const [startDate, setStartDate] = useState(moment()) + const [endDate, setEndDate] = useState(moment()) + const [workArea, setWorkArea] = useState('') + const [officeLocation, setOfficeLocation] = useState('') + const [openDialogMap, setOpenDialogMap] = useState(false) + const [lat, setLat] = useState('') + const [lon, setLon] = useState('') + const [radius, setRadius] = useState(0) + + + // useEffect(()=> { + // console.log("data parent", dataParent) + // },[]) + + useEffect(()=> { + if(typeDialog==="Edit"){ + console.log("cel data Edit", dataEdit) + setId(dataEdit.id) + setName(dataEdit.nama) + setDescription(dataEdit.description) + setBiaya(dataEdit.biaya) + setColor(dataEdit.color_progress) + setSdm(dataEdit.jumlah_pekerja) + setPic(dataEdit.pic) + setStartDate(moment(moment(dataEdit.mulai_proyek))) + setEndDate(moment(moment(dataEdit.akhir_proyek))) + setWorkArea(dataEdit.area_kerja ? dataEdit.area_kerja : '') + setLat(dataEdit.lat ? dataEdit.lat : '') + setLon(dataEdit.lon ? dataEdit.lon : '') + setRadius(dataEdit.buffer_radius ? dataEdit.buffer_radius : 0) + setOfficeLocation(dataEdit.lokasi_kantor ? dataEdit.lokasi_kantor : '') + }else{ + setId(0) + setName('') + setDescription('') + setBiaya(0) + setColor('green') + setSdm(0) + setPic('') + setStartDate(moment()) + setEndDate(moment()) + setWorkArea('') + setLat('') + setLon('') + setRadius(0) + setOfficeLocation('') + } + },[dataEdit,openDialog]) + + const handleSave = () => { + let data = ''; + if(typeDialog==="Save"){ + data = { + nama:name, + biaya, + mulai_proyek:startDate, + akhir_proyek:endDate, + jumlah_pekerja:parseInt(sdm), + // color_progress:color, + pic, + deskripsi:description, + area_kerja: workArea, + lat:lat, + lon:lon, + buffer_radius:parseFloat(radius), + lokasi_kantor:officeLocation + } + + closeDialog('save', data); + }else{ + data = { + id, + nama:name, + biaya, + mulai_proyek:startDate, + akhir_proyek:endDate, + jumlah_pekerja:parseInt(sdm), + // color_progress:color, + pic, + deskripsi:description, + area_kerja: workArea, + lat:lat, + lon:lon, + buffer_radius:parseFloat(radius), + lokasi_kantor:officeLocation + } + + closeDialog('edit', data); + } + setId(0) + setName('') + setDescription('') + setBiaya(0) + setColor('green') + setSdm(0) + setPic('') + setStartDate(moment()) + setEndDate(moment()) + setWorkArea('') + setLat('') + setLon('') + setRadius(0) + setOfficeLocation('') + } + + const handleCancel = () => { + closeDialog('cancel', 'none') + setId(0) + setName('') + setDescription('') + setBiaya(0) + setColor('green') + setSdm(0) + setPic('') + setStartDate(moment()) + setEndDate(moment()) + setWorkArea('') + setLat('') + setLon('') + setRadius(0) + setOfficeLocation('') + } + + const handleDatePickerStart = (date, dateString) => { + setStartDate(date) + } + + const handleDatePickerEnd = (date, dateString) => { + setEndDate(date) + } + + const onChangeColor = (val) => { + setColor(val) + } + + const handleCloseDialogMap = (type, data) => { + if (type === 'save') { + // set to new state + setWorkArea(data.workArea) + setLat(data.lat ? data.lat : '') + setLon(data.lon ? data.lon : '') + setRadius(data.radius? data.radius: 0) + } + else if (type === 'cancel') { + // set as default + setWorkArea(dataEdit && dataEdit.area_kerja ? dataEdit.area_kerja : '') + setLat(dataEdit && dataEdit.lat ? dataEdit.lat : '') + setLon(dataEdit && dataEdit.lon ? dataEdit.lon : '') + setRadius(dataEdit && dataEdit.buffer_radius ? dataEdit.buffer_radius : 0) + } + setOpenDialogMap(false) + } + + const toggleMapDialog = () => { + setOpenDialogMap(!openDialogMap) + } + + const renderForm = () => { + return( +
+ + + + + setName(e.target.value)} placeholder={`Nama..`}/> + + + + setBiaya(e.target.value)} placeholder={`Biaya..`} /> + + {/* + + + */} + + + setSdm(e.target.value)} placeholder={`Sdm..`} /> + + + + + + + setWorkArea(e.target.value)} placeholder={`Area..`}/> + + + {/* setWorkArea(e.target.value)} placeholder={`Area..`}/> */} + + + + + + + + + + + + setPic(e.target.value)} placeholder={`pm..`} /> + + + + + + + + + + + + setOfficeLocation(e.target.value)} placeholder={`Lokasi..`}/> + + {/* + + setDescription(e.target.value)} placeholder={`Deskripsi..`} /> + */} + + + +
+ ) + } + + + return ( + <> + + {typeDialog=="Save" ? `Tambah` : "Edit"} Proyek + + {renderForm()} + + + {' '} + + + + + toggleMapDialog} + dataEdit={dataEdit} + workArea_={workArea} + lat_={lat} + lon_={lon} + radius_={radius} + /> + + ) + +} + +export default DialogForm; \ No newline at end of file diff --git a/src/views/Master/Proyek/DialogFormPlanning.js b/src/views/Master/Proyek/DialogFormPlanning.js new file mode 100644 index 0000000..af2b170 --- /dev/null +++ b/src/views/Master/Proyek/DialogFormPlanning.js @@ -0,0 +1,217 @@ +import React, { useEffect, useState } from 'react' +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Col, Row } from 'reactstrap'; +import { Select, DatePicker, Input } from 'antd' +import { BASE_SIMPRO } from '../../../const/ApiConst'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +// import { Language } from 'src/const/LanguageConst'; +// import moment from 'moment'; +import 'antd/dist/antd.css'; +import axios from 'axios'; +import moment from 'moment'; +const { TextArea } = Input; +const { Option } = Select + +const DialogForm = ({ openDialog, closeDialog, toggleDialog, idSubtask,listWaspang, listSatuanKerja }) => { + const token = localStorage.getItem("token") + const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + } + } + const [id, setId] = useState(0) + const [name, setName] = useState('') + const [jumlahTitik, setJumlahTitik] = useState(0) + const [address, setAddress] = useState('') + const [datePlanning, setDatePlanning] = useState(null) + const [waspangId, setWaspangId] = useState(null) + const [satuanKerjaId, setSatuanKerjaId] = useState(null) + // const [dataProyek, setDataProyek] = useState([]) + // const [dataSubProyek, setDataSubProyek] = useState([]) + + + // const handleGetDataProyek = async () => { + // const URL = `${BASE_SIMPRO}/proyek/list` + // const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + // console.log("proyek", result) + // if (result.data.code == 200) { + // setDataProyek(result.data.data) + // } else { + // NotificationManager.error('Gaga Mengambil Data!!', 'Failed'); + // } + // } + // const handleGetDataSubProyek = async () => { + // const URL = `${BASE_SIMPRO}/sub-proyek/list` + // const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + // console.log("sub proyek", result) + // if (result.data.code == 200) { + // setDataSubProyek(result.data.data) + // } else { + // NotificationManager.error('Gaga Mengambil Data!!', 'Failed'); + // } + // } + + useEffect(() => { + // handleGetDataProyek() + // handleGetDataSubProyek() + }, []) + + useEffect(() => { + setId(0) + setName('') + console.log("test", idSubtask) + }, [openDialog]) + + const handleAddPlanning = async () => { + const URL = `${BASE_SIMPRO}/planning/add` + const payload = { + address, + jumlah_titik : parseInt(jumlahTitik), + satuan_id : parseInt(satuanKerjaId), + nama : name, + subproyek_id : idSubtask, + target_planning : datePlanning.format(), + user_id : parseInt(waspangId) + } + // console.log(payload) + const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response) + return result + } + + const handleSave = async () => { + + const result = await handleAddPlanning() + // console.log(result) + if (result.data.code === 200) { + closeDialog(); + } else { + NotificationManager.error('Gaga Menambah Data!!', 'Failed'); + } + setId(0) + setName('') + } + + const handleCancel = () => { + closeDialog('cancel', 'none') + setId(0) + setName('') + } + + const onChangeWaspang = (val) => { + setWaspangId(val) + } + + const onChangeSatuanerja = (val) => { + setSatuanKerjaId(val) + } + + const setupOptionWaspang = () => { + return( + <> + {listWaspang.map((val, index)=> { + return( + + ) + })} + + ) + } + + const setupOptionSatuanKerja = () => { + return( + <> + {listSatuanKerja.map((val, index)=> { + return( + + ) + })} + + ) + } + + const renderForm = () => { + return ( +
+ + + {/* + + + + + + + */} + + + setName(e.target.value)} placeholder={`Isikan Nama Pekerjaan`} /> + + + + + + setJumlahTitik(e.target.value)} placeholder={`Isikan Volume Pekerjaan`} /> + + + + + + + + + + + + + + + +
+ setDatePlanning(date)} + showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }} + /> +
+ + +