@ -0,0 +1,35 @@
|
||||
/* eslint-disable no-unused-vars */ |
||||
// ** React Imports
|
||||
import React from 'react' |
||||
const Ind = require('flatpickr/dist/l10n/id.js').default.id // ** Reactstrap Imports
|
||||
import { Label } from 'reactstrap' |
||||
|
||||
// ** Third Party Components
|
||||
import Flatpickr from 'react-flatpickr' |
||||
// ** Styles
|
||||
import '../scss/react/libs/flatpickr/flatpickr.scss' |
||||
const Tanggal = ({ startOrEnd, value, setValue }) => { |
||||
// ** State
|
||||
// const [picker, setPicker] = useState(new Date())
|
||||
return ( |
||||
<> |
||||
<Label className="form-label" for="hf-picker"> |
||||
{startOrEnd} |
||||
</Label> |
||||
<Flatpickr |
||||
value={value} |
||||
id="hf-picker" |
||||
className="form-control tanggal" |
||||
onChange={(date) => setValue(date[0].toISOString())} |
||||
options={{ |
||||
locale: Ind, |
||||
altInput: true, |
||||
altFormat: 'd M Y', |
||||
dateFormat: 'Y-m-d' |
||||
}} |
||||
/> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Tanggal |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/layers.png"; |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 696 B |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/layers-2x.png"; |
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/marker-icon.png"; |
@ -0,0 +1,43 @@
|
||||
/* @preserve |
||||
* Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com |
||||
* (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade |
||||
*/ |
||||
|
||||
/*! |
||||
Copyright (c) 2016 Dominik Moritz |
||||
|
||||
This file is part of the leaflet locate control. It is licensed under the MIT license. |
||||
You can find the project at: https://github.com/domoritz/leaflet-locatecontrol |
||||
*/ |
||||
|
||||
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-dom.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* scheduler.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
@ -0,0 +1,35 @@
|
||||
/* eslint-disable no-unused-vars */ |
||||
// ** React Imports
|
||||
import React from 'react' |
||||
const Ind = require('flatpickr/dist/l10n/id.js').default.id // ** Reactstrap Imports
|
||||
import { Label } from 'reactstrap' |
||||
|
||||
// ** Third Party Components
|
||||
import Flatpickr from 'react-flatpickr' |
||||
// ** Styles
|
||||
import '../scss/react/libs/flatpickr/flatpickr.scss' |
||||
const Tanggal = ({ startOrEnd, value, setValue }) => { |
||||
// ** State
|
||||
// const [picker, setPicker] = useState(new Date())
|
||||
return ( |
||||
<> |
||||
<Label className="form-label" for="hf-picker"> |
||||
{startOrEnd} |
||||
</Label> |
||||
<Flatpickr |
||||
value={value} |
||||
id="hf-picker" |
||||
className="form-control tanggal" |
||||
onChange={(date) => setValue(date[0].toISOString())} |
||||
options={{ |
||||
locale: Ind, |
||||
altInput: true, |
||||
altFormat: 'd M Y', |
||||
dateFormat: 'Y-m-d' |
||||
}} |
||||
/> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Tanggal |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/layers.png"; |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 696 B |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/layers-2x.png"; |
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/marker-icon.png"; |
@ -0,0 +1,126 @@
|
||||
/*! |
||||
Copyright (c) 2018 Jed Watson. |
||||
Licensed under the MIT License (MIT), see |
||||
http://jedwatson.github.io/classnames |
||||
*/ |
||||
|
||||
/*! |
||||
* jQuery JavaScript Library v3.7.1 |
||||
* https://jquery.com/ |
||||
* |
||||
* Copyright OpenJS Foundation and other contributors |
||||
* Released under the MIT license |
||||
* https://jquery.org/license |
||||
* |
||||
* Date: 2023-08-28T13:37Z |
||||
*/ |
||||
|
||||
/** |
||||
* @license @tabler/icons-react v3.16.0 - MIT |
||||
* |
||||
* This source code is licensed under the MIT license. |
||||
* See the LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-dom.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-jsx-runtime.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* scheduler.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* use-sync-external-store-with-selector.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* match-sorter-utils |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* react-virtual |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* table-core |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* virtual-core |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** @license React v16.13.1 |
||||
* react-is.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
@ -0,0 +1,126 @@
|
||||
/*! |
||||
Copyright (c) 2018 Jed Watson. |
||||
Licensed under the MIT License (MIT), see |
||||
http://jedwatson.github.io/classnames |
||||
*/ |
||||
|
||||
/*! |
||||
* jQuery JavaScript Library v3.7.1 |
||||
* https://jquery.com/ |
||||
* |
||||
* Copyright OpenJS Foundation and other contributors |
||||
* Released under the MIT license |
||||
* https://jquery.org/license |
||||
* |
||||
* Date: 2023-08-28T13:37Z |
||||
*/ |
||||
|
||||
/** |
||||
* @license @tabler/icons-react v3.16.0 - MIT |
||||
* |
||||
* This source code is licensed under the MIT license. |
||||
* See the LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-dom.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-jsx-runtime.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* scheduler.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* use-sync-external-store-with-selector.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* match-sorter-utils |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* react-virtual |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* table-core |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* virtual-core |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** @license React v16.13.1 |
||||
* react-is.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
@ -0,0 +1,43 @@
|
||||
/* @preserve |
||||
* Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com |
||||
* (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade |
||||
*/ |
||||
|
||||
/*! |
||||
Copyright (c) 2016 Dominik Moritz |
||||
|
||||
This file is part of the leaflet locate control. It is licensed under the MIT license. |
||||
You can find the project at: https://github.com/domoritz/leaflet-locatecontrol |
||||
*/ |
||||
|
||||
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-dom.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* scheduler.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
@ -0,0 +1,35 @@
|
||||
/* eslint-disable no-unused-vars */ |
||||
// ** React Imports
|
||||
import React from 'react' |
||||
const Ind = require('flatpickr/dist/l10n/id.js').default.id // ** Reactstrap Imports
|
||||
import { Label } from 'reactstrap' |
||||
|
||||
// ** Third Party Components
|
||||
import Flatpickr from 'react-flatpickr' |
||||
// ** Styles
|
||||
import '../scss/react/libs/flatpickr/flatpickr.scss' |
||||
const Tanggal = ({ startOrEnd, value, setValue }) => { |
||||
// ** State
|
||||
// const [picker, setPicker] = useState(new Date())
|
||||
return ( |
||||
<> |
||||
<Label className="form-label" for="hf-picker"> |
||||
{startOrEnd} |
||||
</Label> |
||||
<Flatpickr |
||||
value={value} |
||||
id="hf-picker" |
||||
className="form-control tanggal" |
||||
onChange={(date) => setValue(date[0].toISOString())} |
||||
options={{ |
||||
locale: Ind, |
||||
altInput: true, |
||||
altFormat: 'd M Y', |
||||
dateFormat: 'Y-m-d' |
||||
}} |
||||
/> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Tanggal |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/layers.png"; |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 696 B |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/layers-2x.png"; |
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1 @@
|
||||
export default "/engineN/public/kpdl/marker-icon.png"; |
@ -0,0 +1,126 @@
|
||||
/*! |
||||
Copyright (c) 2018 Jed Watson. |
||||
Licensed under the MIT License (MIT), see |
||||
http://jedwatson.github.io/classnames |
||||
*/ |
||||
|
||||
/*! |
||||
* jQuery JavaScript Library v3.7.1 |
||||
* https://jquery.com/ |
||||
* |
||||
* Copyright OpenJS Foundation and other contributors |
||||
* Released under the MIT license |
||||
* https://jquery.org/license |
||||
* |
||||
* Date: 2023-08-28T13:37Z |
||||
*/ |
||||
|
||||
/** |
||||
* @license @tabler/icons-react v3.16.0 - MIT |
||||
* |
||||
* This source code is licensed under the MIT license. |
||||
* See the LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-dom.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-jsx-runtime.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* scheduler.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* use-sync-external-store-with-selector.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* match-sorter-utils |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* react-virtual |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* table-core |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* virtual-core |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** @license React v16.13.1 |
||||
* react-is.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
@ -0,0 +1,126 @@
|
||||
/*! |
||||
Copyright (c) 2018 Jed Watson. |
||||
Licensed under the MIT License (MIT), see |
||||
http://jedwatson.github.io/classnames |
||||
*/ |
||||
|
||||
/*! |
||||
* jQuery JavaScript Library v3.7.1 |
||||
* https://jquery.com/ |
||||
* |
||||
* Copyright OpenJS Foundation and other contributors |
||||
* Released under the MIT license |
||||
* https://jquery.org/license |
||||
* |
||||
* Date: 2023-08-28T13:37Z |
||||
*/ |
||||
|
||||
/** |
||||
* @license @tabler/icons-react v3.16.0 - MIT |
||||
* |
||||
* This source code is licensed under the MIT license. |
||||
* See the LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-dom.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-jsx-runtime.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* scheduler.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* use-sync-external-store-with-selector.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* match-sorter-utils |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* react-virtual |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* table-core |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* virtual-core |
||||
* |
||||
* Copyright (c) TanStack |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE.md file in the root directory of this source tree. |
||||
* |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** @license React v16.13.1 |
||||
* react-is.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
@ -0,0 +1,43 @@
|
||||
/* @preserve |
||||
* Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com |
||||
* (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade |
||||
*/ |
||||
|
||||
/*! |
||||
Copyright (c) 2016 Dominik Moritz |
||||
|
||||
This file is part of the leaflet locate control. It is licensed under the MIT license. |
||||
You can find the project at: https://github.com/domoritz/leaflet-locatecontrol |
||||
*/ |
||||
|
||||
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */ |
||||
|
||||
/** |
||||
* @license React |
||||
* react-dom.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* react.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
||||
|
||||
/** |
||||
* @license React |
||||
* scheduler.production.min.js |
||||
* |
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
* |
||||
* This source code is licensed under the MIT license found in the |
||||
* LICENSE file in the root directory of this source tree. |
||||
*/ |
@ -0,0 +1,49 @@
|
||||
import React, { useRef, useState } from 'react' |
||||
import { Card, CardBody, CardHeader, CardTitle } from 'reactstrap' |
||||
import { Stepper } from 'primereact/stepper' |
||||
import { StepperPanel } from 'primereact/stepperpanel' |
||||
import PenugasanKpdl from './componentPenugasanAktifitas/PenugasanKpdl.js' |
||||
const TabPenugasan = ({ dataSend }) => { |
||||
const stepperRef = useRef(null) |
||||
return ( |
||||
<> |
||||
<Card> |
||||
<CardHeader className="d-flex justify-content-center p-2"> |
||||
<CardTitle tag={'h1'} className="font-weight-bold"> |
||||
Statistik Penugasan Matoa Tahun Berjalan |
||||
</CardTitle> |
||||
</CardHeader> |
||||
|
||||
<CardBody className="mb-0"> |
||||
<div className="card flex justify-content-center"> |
||||
<Stepper ref={stepperRef} style={{ flexBasis: '30rem' }}> |
||||
<StepperPanel header="Identifikasi Lapangan (KPDL/MATOA)"> |
||||
<div className="flex flex-column h-12rem"> |
||||
<div className="font-medium"> |
||||
<PenugasanKpdl dataSend={dataSend} /> |
||||
</div> |
||||
</div> |
||||
</StepperPanel> |
||||
{/* <StepperPanel header="Aktifitas pasca identifikasi"> |
||||
<div className="flex flex-column h-12rem"> |
||||
<div className="border-2 border-dashed surface-border border-round surface-ground flex-auto flex justify-content-center align-items-center font-medium"> |
||||
OTW |
||||
</div> |
||||
</div> |
||||
</StepperPanel> |
||||
<StepperPanel header="Hasil pasca aktifitas"> |
||||
<div className="flex flex-column h-12rem"> |
||||
<div className="border-2 border-dashed surface-border border-round surface-ground flex-auto flex justify-content-center align-items-center font-medium"> |
||||
OTW |
||||
</div> |
||||
</div> |
||||
</StepperPanel> */} |
||||
</Stepper> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default TabPenugasan |
@ -0,0 +1,63 @@
|
||||
import React, { useState } from "react" |
||||
import { TabView, TabPanel } from "primereact/tabview" |
||||
// import Identifikasi from "./componentProgresifitas/Identifikasi"
|
||||
import Pembayaran from "./componentProgresifitas/Pembayaran" |
||||
import Identifikasi from "./componentProgresifitas/Identifikasi" |
||||
import PayComp from "./componentProgresifitas/PayComp" |
||||
import Sof from "./componentProgresifitas/Sof" |
||||
import JenisStatusWp from "./componentProgresifitas/JenisStatusWp" |
||||
import Pengampu from "./componentProgresifitas/Pengampu" |
||||
import SPTTahunan from "./componentProgresifitas/SPTTahunan" |
||||
import KLU from "./componentProgresifitas/KLU" |
||||
import ZonaPengawasan from "./componentProgresifitas/ZonaPengawasan" |
||||
import { useDispatch, useSelector } from "react-redux" |
||||
import { isObjEmpty } from "./util" |
||||
|
||||
export default function TabProgresifitas({ dataSend }) { |
||||
const dispatch = useDispatch() |
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
|
||||
return ( |
||||
<div className="card"> |
||||
<TabView scrollable> |
||||
<TabPanel id="tab_1" header="Identifikasi"> |
||||
<Identifikasi dataSend={dataSend} /> |
||||
</TabPanel> |
||||
|
||||
<TabPanel id="tab_2" header="Pembayaran"> |
||||
<Pembayaran dataSend={dataSend} /> |
||||
</TabPanel> |
||||
|
||||
<TabPanel id="tab_3" header="Payment Compliance"> |
||||
<PayComp dataSend={dataSend} /> |
||||
</TabPanel> |
||||
|
||||
<TabPanel id="tab_4" header="Strength Of Figure"> |
||||
<div> |
||||
<Sof dataSend={dataSend} /> |
||||
</div> |
||||
</TabPanel> |
||||
<TabPanel id="tab_7" header="SPT Tahunan"> |
||||
<SPTTahunan dataSend={dataSend} /> |
||||
</TabPanel> |
||||
|
||||
<TabPanel id="tab_5" header="Jenis/Status WP"> |
||||
<JenisStatusWp dataSend={dataSend} /> |
||||
</TabPanel> |
||||
{storeKpdl && !isObjEmpty(storeKpdl) && storeKpdl.selectedOpsi && storeKpdl.selectedOpsi?.name !== "pengampu" ? ( |
||||
<TabPanel id="tab_5" header="Pengampu"> |
||||
<Pengampu dataSend={dataSend} /> |
||||
</TabPanel> |
||||
) : null} |
||||
{storeKpdl && !isObjEmpty(storeKpdl) && storeKpdl.selectedOpsi && storeKpdl.selectedOpsi?.name !== "zona" ? ( |
||||
<TabPanel id="tab_6" header="Zona Pengawasan"> |
||||
<ZonaPengawasan dataSend={dataSend} /> |
||||
</TabPanel> |
||||
) : null} |
||||
<TabPanel id="tab_8" header="KLU (Golongan Pokok)"> |
||||
<KLU dataSend={dataSend} /> |
||||
</TabPanel> |
||||
</TabView> |
||||
</div> |
||||
) |
||||
} |
@ -0,0 +1,185 @@
|
||||
import React, { useEffect, useRef, useState } from 'react' |
||||
import { MultiSelect } from 'react-multi-select-component' |
||||
import { Col, Label, Row } from 'reactstrap' |
||||
import Select from 'react-select' |
||||
import { Button as ButtonP } from 'primereact/button' |
||||
import { isObjEmpty } from '../util' |
||||
import $ from 'jquery' |
||||
import collect from 'collect.js' |
||||
import { setSelectedOpsi } from '../store/KpdlStore' |
||||
import { useDispatch, useSelector } from 'react-redux' |
||||
const NipPengampu = ({ dataSend, setDataSend, activeTab, toast, setHiddenGraphMatoa, dataOpsi }) => { |
||||
const dispatch = useDispatch() |
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
|
||||
const [kanwil, setKanwil] = useState({}) |
||||
const [kpp, setKpp] = useState({}) |
||||
const [seksi, setSeksi] = useState([]) |
||||
const [ar, setAr] = useState([]) |
||||
|
||||
const [kanwilSelected, setKanwilSelected] = useState(null) |
||||
const [kppSelected, setKppSelected] = useState({}) |
||||
const [seksiSelected, setSeksiSelected] = useState([]) |
||||
const [arSelected, setArSelected] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
useEffect(() => { |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kanwil', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
success: (data) => { |
||||
setKanwil(data) |
||||
} |
||||
}) |
||||
}, []) |
||||
|
||||
useEffect(() => { |
||||
setKpp(null) |
||||
setSeksi([]) |
||||
setAr([]) |
||||
setKppSelected(null) |
||||
setSeksiSelected([]) |
||||
setArSelected([]) |
||||
if (kanwilSelected && !isObjEmpty(kanwilSelected)) { |
||||
const kanwil = kanwilSelected.value |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kpp', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
data: { kanwil }, |
||||
success: (data) => { |
||||
setKpp(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [kanwilSelected]) |
||||
|
||||
useEffect(() => { |
||||
setSeksi([]) |
||||
setAr([]) |
||||
setSeksiSelected([]) |
||||
setArSelected([]) |
||||
if (kppSelected && !isObjEmpty(kppSelected)) { |
||||
const kpp = kppSelected.value |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/seksi', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
data: { kpp }, |
||||
success: (data) => { |
||||
setSeksi(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [kppSelected]) |
||||
|
||||
useEffect(() => { |
||||
setAr([]) |
||||
setArSelected([]) |
||||
const seksi = collect(seksiSelected).pluck('value').all() |
||||
if (seksi.length && !isObjEmpty(seksiSelected)) { |
||||
const kpp = kppSelected.value |
||||
|
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/ar', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { kpp, seksi }, |
||||
success: (data) => { |
||||
setAr(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [seksiSelected]) |
||||
const buttonProsesOnClick = () => { |
||||
const nip_ar_pengampu = collect(arSelected).pluck('value').all() |
||||
if (nip_ar_pengampu.length) { |
||||
dispatch(setSelectedOpsi(dataOpsi.pengampu)) |
||||
setDataSend({ opsiWilZona: dataOpsi.pengampu.key, nip_ar_pengampu }) |
||||
setHiddenGraphMatoa(true) |
||||
} else { |
||||
toast.current.show({ severity: 'info', summary: 'Info', detail: 'AR Pengampu harus dipilih' }) |
||||
} |
||||
} |
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="basicInput"> |
||||
Kanwil |
||||
</Label> |
||||
<Select |
||||
placeholder="Pilih Kanwil" |
||||
className="basic-single w-100" |
||||
onChange={(e) => { |
||||
setKanwilSelected(e) |
||||
}} |
||||
classNamePrefix="select" |
||||
// defaultValue={kanwilSelected}
|
||||
value={kanwilSelected} |
||||
isClearable={false} |
||||
options={kanwil} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih KPP"> |
||||
KPP |
||||
</Label> |
||||
<Select |
||||
placeholder="Pilih KPP" |
||||
className="basic-single w-100" |
||||
onChange={(e) => { |
||||
setKppSelected(e) |
||||
}} |
||||
classNamePrefix="select" |
||||
// defaultValue={kanwilSelected}
|
||||
value={kppSelected} |
||||
isClearable={false} |
||||
options={kpp} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih Seksi"> |
||||
Seksi |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={seksi} |
||||
value={seksiSelected} |
||||
onChange={(e) => { |
||||
setSeksiSelected(e) |
||||
}} |
||||
labelledBy="Pilih Seksi" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih Seksi' }} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih AR"> |
||||
AR |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={ar} |
||||
value={arSelected} |
||||
onChange={(e) => { |
||||
setArSelected(e) |
||||
}} |
||||
labelledBy="Pilih AR" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih AR' }} |
||||
/> |
||||
</Col> |
||||
</Row> |
||||
<Row className="mt-2"> |
||||
<Col sm="12"> |
||||
<ButtonP onClick={() => buttonProsesOnClick()} label="Proses" severity="" rounded className="w-10rem text-white text-base" /> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default NipPengampu |
@ -0,0 +1,189 @@
|
||||
import React, { useEffect, useRef, useState } from 'react' |
||||
import { MultiSelect } from 'react-multi-select-component' |
||||
import { Col, Label, Row } from 'reactstrap' |
||||
import Select from 'react-select' |
||||
import { Button as ButtonP } from 'primereact/button' |
||||
import { isObjEmpty } from '../util' |
||||
import $ from 'jquery' |
||||
import collect from 'collect.js' |
||||
import { useDispatch, useSelector } from 'react-redux' |
||||
import { setSelectedOpsi } from '../store/KpdlStore' |
||||
|
||||
const NipPerekam = ({ dataSend, setDataSend, activeTab, toast, setHiddenGraphMatoa, dataOpsi }) => { |
||||
const dispatch = useDispatch() |
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
|
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const [kanwil, setKanwil] = useState({}) |
||||
const [kpp, setKpp] = useState(null) |
||||
const [seksi, setSeksi] = useState([]) |
||||
const [ar, setAr] = useState([]) |
||||
|
||||
const [kanwilSelected, setKanwilSelected] = useState(null) |
||||
const [kppSelected, setKppSelected] = useState() |
||||
const [seksiSelected, setSeksiSelected] = useState([]) |
||||
const [arSelected, setArSelected] = useState([]) |
||||
|
||||
useEffect(() => { |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kanwilPratama', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
success: (data) => { |
||||
setKanwil(data) |
||||
} |
||||
}) |
||||
}, []) |
||||
|
||||
useEffect(() => { |
||||
setKpp(null) |
||||
setSeksi([]) |
||||
setAr([]) |
||||
setKppSelected(null) |
||||
setSeksiSelected([]) |
||||
setArSelected([]) |
||||
if (kanwilSelected && !isObjEmpty(kanwilSelected)) { |
||||
const kanwil = kanwilSelected.value |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kppPratama', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
data: { kanwil }, |
||||
success: (data) => { |
||||
setKpp(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [kanwilSelected]) |
||||
|
||||
useEffect(() => { |
||||
setSeksi([]) |
||||
setAr([]) |
||||
setSeksiSelected([]) |
||||
setArSelected([]) |
||||
if (kppSelected && !isObjEmpty(kppSelected)) { |
||||
const kpp = kppSelected.value |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/seksi', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
data: { kpp }, |
||||
success: (data) => { |
||||
setSeksi(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [kppSelected]) |
||||
|
||||
useEffect(() => { |
||||
setAr([]) |
||||
setArSelected([]) |
||||
const seksi = collect(seksiSelected).pluck('value').all() |
||||
if (seksi.length && !isObjEmpty(seksiSelected)) { |
||||
const kpp = kppSelected.value |
||||
// const seksi = collect(seksiSelected).pluck("value").all()
|
||||
|
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/ar', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { kpp, seksi }, |
||||
success: (data) => { |
||||
setAr(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [seksiSelected]) |
||||
|
||||
const buttonProsesOnClick = () => { |
||||
const nip_ar_perekam = collect(arSelected).pluck('value').all() |
||||
if (nip_ar_perekam.length) { |
||||
dispatch(setSelectedOpsi(dataOpsi.perekam)) |
||||
setDataSend({ opsiWilZona: dataOpsi.perekam.key, nip_ar_perekam }) |
||||
setHiddenGraphMatoa(true) |
||||
} else { |
||||
toast.current.show({ severity: 'info', summary: 'Info', detail: 'AR Perekam harus dipilih' }) |
||||
} |
||||
} |
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="basicInput"> |
||||
Kanwil |
||||
</Label> |
||||
<Select |
||||
placeholder="Pilih Kanwil" |
||||
className="basic-single w-100" |
||||
onChange={(e) => { |
||||
setKanwilSelected(e) |
||||
}} |
||||
classNamePrefix="select" |
||||
value={kanwilSelected} |
||||
isClearable={false} |
||||
options={kanwil} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih KPP"> |
||||
KPP |
||||
</Label> |
||||
<Select |
||||
placeholder="Pilih KPP" |
||||
className="basic-single w-100" |
||||
onChange={(e) => { |
||||
setKppSelected(e) |
||||
}} |
||||
classNamePrefix="select" |
||||
// defaultValue={kanwilSelected}
|
||||
value={kppSelected} |
||||
isClearable={false} |
||||
options={kpp} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih Seksi"> |
||||
Seksi |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={seksi} |
||||
value={seksiSelected} |
||||
onChange={(e) => { |
||||
setSeksiSelected(e) |
||||
}} |
||||
labelledBy="Pilih Seksi" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih Seksi' }} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih AR"> |
||||
AR Perekam |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={ar} |
||||
value={arSelected} |
||||
onChange={(e) => { |
||||
setArSelected(e) |
||||
}} |
||||
labelledBy="Pilih AR" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih AR' }} |
||||
/> |
||||
</Col> |
||||
</Row> |
||||
<Row className="mt-2"> |
||||
<Col sm="12"> |
||||
<ButtonP onClick={() => buttonProsesOnClick()} label="Proses" severity="" rounded className="w-10rem text-white text-base" /> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default NipPerekam |
@ -0,0 +1,350 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Col, Row } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import collect from 'collect.js' |
||||
import $ from 'jquery' |
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
const fetchSize = 101 |
||||
|
||||
const Identifikasi = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
const refChart = useRef(null) |
||||
const [dataGraph, setDataGraph] = useState(null) |
||||
const [dataDetail, setDataDetail] = useState({ meta: { data: [], total: 0 } }) |
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
|
||||
useEffect(() => { |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranIdentifikasi', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend |
||||
}, |
||||
success: (data) => { |
||||
setDataGraph(data.data) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsChart = (data, title) => { |
||||
const total_wp = collect(data).sum('y') |
||||
return { |
||||
chart: { |
||||
plotBackgroundColor: null, |
||||
plotBorderWidth: null, |
||||
plotShadow: false, |
||||
type: 'pie', |
||||
zoomType: 'xy', |
||||
height: '300' |
||||
}, |
||||
title: { |
||||
text: title, |
||||
style: { fontSize: '10px' } |
||||
}, |
||||
tooltip: { |
||||
pointFormat: '<b>{point.percentage:.1f}%</b><br>: {point.y} dari ' + format_angka(total_wp) + ' total lokasi Matoa' |
||||
}, |
||||
accessibility: { |
||||
point: { |
||||
valueSuffix: '%' |
||||
} |
||||
}, |
||||
plotOptions: { |
||||
pie: { |
||||
allowPointSelect: true, |
||||
cursor: 'pointer', |
||||
dataLabels: { |
||||
enabled: true, |
||||
style: { fontSize: '10px' }, |
||||
// format: "{point.name}: {point.y} <br> {point.percentage:.1f} %"
|
||||
formatter: function () { |
||||
return `${this.key} : ${Number(this.y).toLocaleString('id-ID', { |
||||
minimumFractionDigits: 0, |
||||
maximumFractionDigits: 0 |
||||
})} <br> ${Number(this.percentage).toLocaleString('id-ID')}%` |
||||
} |
||||
} |
||||
}, |
||||
series: { |
||||
cursor: 'pointer', |
||||
point: { |
||||
events: { |
||||
click: function (a) { |
||||
setQuery(this.key) |
||||
setVisibleSidebar(true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: '', |
||||
data |
||||
} |
||||
] |
||||
} |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/identifikasi/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NAMA', |
||||
header: 'Nama' |
||||
}, |
||||
|
||||
{ |
||||
accessorKey: 'MERK_USAHA', |
||||
header: 'Merk Usaha' |
||||
}, |
||||
{ |
||||
accessorKey: 'NO_IDENTITAS', |
||||
header: 'No Identitas' |
||||
}, |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN} ${dataRow.KECAMATAN} ${dataRow.KABUPATEN} ${dataRow.PROVINSI}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR_PENGAMPU', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_AR_PENGAMPU', |
||||
header: 'AR Pengampu' |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_PEMBAYARAN_THN_TERAKHIR', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'SUM_NILAI', |
||||
header: 'NILAI DATA', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KPP_ZONA', |
||||
header: 'KPP Lokasi' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_AR_ZONA', |
||||
header: 'AR Wilayah', |
||||
filter: false |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_PEREKAM', |
||||
header: 'Perekam' |
||||
}, |
||||
{ |
||||
accessorKey: 'CREATION_DATE', |
||||
header: 'Tgl Rekam', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue()).format('YYYY-MM-DD HH:mm:ss') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
|
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col> |
||||
<HighchartsReact ref={refChart} highcharts={Highcharts} options={optionsChart(dataGraph, 'Identifikasi Lokasi Matoa')} /> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Identifikasi |
@ -0,0 +1,332 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Col, Row } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import collect from 'collect.js' |
||||
import $ from 'jquery' |
||||
|
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
var customParseFormat = require('dayjs/plugin/customParseFormat') |
||||
const fetchSize = 101 |
||||
|
||||
const JenisStatusWp = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const refChart = useRef(null) |
||||
const refChart2 = useRef(null) |
||||
const [dataJenis, setDataJenis] = useState(null) |
||||
const [dataStatus, setDataStatus] = useState(null) |
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
const [jenisStatus, setJenisStatus] = useState('') |
||||
|
||||
useEffect(() => { |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranJenisStatusWp', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend |
||||
}, |
||||
success: (data) => { |
||||
setDataJenis(data.dataJenis) |
||||
setDataStatus(data.dataStatus) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsChart = (data, title) => { |
||||
const total_wp = collect(data).sum('y') |
||||
return { |
||||
chart: { |
||||
plotBackgroundColor: null, |
||||
plotBorderWidth: null, |
||||
plotShadow: false, |
||||
type: 'pie', |
||||
zoomType: 'xy', |
||||
height: '300' |
||||
}, |
||||
title: { |
||||
text: title, |
||||
style: { fontSize: '10px' } |
||||
}, |
||||
tooltip: { |
||||
pointFormat: '<b>{point.percentage:.1f}%</b><br>: {point.y} dari ' + format_angka(total_wp) + ' total NPWP' |
||||
}, |
||||
accessibility: { |
||||
point: { |
||||
valueSuffix: '%' |
||||
} |
||||
}, |
||||
plotOptions: { |
||||
pie: { |
||||
allowPointSelect: true, |
||||
cursor: 'pointer', |
||||
dataLabels: { |
||||
enabled: true, |
||||
style: { fontSize: '10px' }, |
||||
format: '{point.name}: <br> {point.percentage:.1f} %' |
||||
} |
||||
}, |
||||
series: { |
||||
cursor: 'pointer', |
||||
point: { |
||||
events: { |
||||
click: function () { |
||||
setQuery(this.key) |
||||
setJenisStatus(this.series.name) |
||||
setVisibleSidebar(true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: title, |
||||
data |
||||
} |
||||
] |
||||
} |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query, jenisStatus }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/jenisstatus/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
jenisStatus, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_WP', |
||||
header: 'Nama' |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT_MFWP', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN_MFWP', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN_MFWP ?? ''} ${dataRow.KECAMATAN_MFWP ?? ''} ${dataRow.KOTA_MFWP ?? ''} ${dataRow.PROPINSI_MFWP ?? ''}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_AR_MFWP', |
||||
header: 'AR' |
||||
}, |
||||
{ |
||||
accessorKey: 'FLAG_WPS_WPK', |
||||
header: 'WPS/WPK', |
||||
size: 100, |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_PEMBAYARAN_THN_TERAKHIR', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'TANGGAL_DAFTAR', |
||||
header: 'Tgl Daftar', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue(), 'DD-MMM-YY').format('YYYY-MM-DD') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' }, |
||||
mantineTableBodyProps: { className: 'mb-3' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
|
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col md="6"> |
||||
<HighchartsReact ref={refChart} highcharts={Highcharts} options={optionsChart(dataJenis, 'Jenis WP')} /> |
||||
</Col> |
||||
<Col md="6"> |
||||
<HighchartsReact ref={refChart2} highcharts={Highcharts} options={optionsChart(dataStatus, 'Status WP')} /> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} jenisStatus={jenisStatus} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default JenisStatusWp |
@ -0,0 +1,437 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Col, Row } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import collect from 'collect.js' |
||||
import $ from 'jquery' |
||||
|
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
var customParseFormat = require('dayjs/plugin/customParseFormat') |
||||
const fetchSize = 101 |
||||
|
||||
const KLU = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const refChart = useRef(null) |
||||
const refChart1 = useRef(null) |
||||
const refChart2 = useRef(null) |
||||
const refChart3 = useRef(null) |
||||
const [dataKluTerdaftar, setDataKluTerdaftar] = useState(null) |
||||
const [limaKluTerdaftar, setLimaKluTerdaftar] = useState([]) |
||||
|
||||
const [dataKluYgBayar, setDataKluYgBayar] = useState(null) |
||||
const [limaKluYgBayar, setLimaKluYgBayar] = useState([]) |
||||
|
||||
const [dataKluYgTidakBayar, setDataKluYgTidakBayar] = useState(null) |
||||
const [limaKluYgTidakBayar, setLimaKluYgTidakBayar] = useState([]) |
||||
|
||||
const [dataRupiahBayar, setDataRupiahBayar] = useState(null) |
||||
const [limaRupiahBayar, setLimaRupiahBayar] = useState([]) |
||||
|
||||
const [limaBesar, setLimaBesar] = useState([]) |
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
const [jenisChart, setJenisChart] = useState(null) |
||||
|
||||
useEffect(() => { |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranKLU', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend |
||||
}, |
||||
success: (resp) => { |
||||
setDataKluTerdaftar(() => { |
||||
const data = resp.dataKluTerdaftar |
||||
const dataRet = [] |
||||
let jmlLainnya = 0 |
||||
|
||||
for (let index = 0; index < data.length; index++) { |
||||
const element = data[index] |
||||
if (index < 5) { |
||||
dataRet.push({ name: element.name, y: element.y, key: element.key }) |
||||
} else { |
||||
jmlLainnya += element.y |
||||
} |
||||
} |
||||
setLimaKluTerdaftar(collect(dataRet).pluck('key').all()) |
||||
dataRet.push({ name: 'Lainnya', y: jmlLainnya, key: "<?=encryptData('lainnya')?>" }) |
||||
return dataRet |
||||
}) |
||||
setDataKluYgBayar(() => { |
||||
const data = resp.dataKluYgBayar |
||||
const dataRet = [] |
||||
let jmlLainnya = 0 |
||||
|
||||
for (let index = 0; index < data.length; index++) { |
||||
const element = data[index] |
||||
if (index < 5) { |
||||
dataRet.push({ name: element.name, y: element.y, key: element.key }) |
||||
} else { |
||||
jmlLainnya += element.y |
||||
} |
||||
} |
||||
setLimaKluYgBayar(collect(dataRet).pluck('key').all()) |
||||
dataRet.push({ name: 'Lainnya', y: jmlLainnya, key: "<?=encryptData('lainnya')?>" }) |
||||
return dataRet |
||||
}) |
||||
|
||||
setDataKluYgTidakBayar(() => { |
||||
const data = resp.dataKluYgTidakBayar |
||||
const dataRet = [] |
||||
let jmlLainnya = 0 |
||||
|
||||
for (let index = 0; index < data.length; index++) { |
||||
const element = data[index] |
||||
if (index < 5) { |
||||
dataRet.push({ name: element.name, y: element.y, key: element.key }) |
||||
} else { |
||||
jmlLainnya += element.y |
||||
} |
||||
} |
||||
setLimaKluYgTidakBayar(collect(dataRet).pluck('key').all()) |
||||
dataRet.push({ name: 'Lainnya', y: jmlLainnya, key: "<?=encryptData('lainnya')?>" }) |
||||
return dataRet |
||||
}) |
||||
|
||||
setDataRupiahBayar(() => { |
||||
const data = resp.dataRupiahBayar |
||||
const dataRet = [] |
||||
let jmlLainnya = 0 |
||||
|
||||
for (let index = 0; index < data.length; index++) { |
||||
const element = data[index] |
||||
if (index < 5) { |
||||
dataRet.push({ name: element.name, y: element.y, key: element.key }) |
||||
} else { |
||||
jmlLainnya += element.y |
||||
} |
||||
} |
||||
setLimaRupiahBayar(collect(dataRet).pluck('key').all()) |
||||
dataRet.push({ name: 'Lainnya', y: jmlLainnya, key: "<?=encryptData('lainnya')?>" }) |
||||
return dataRet |
||||
}) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsChart = (data, title, attribute1, jenisChart) => { |
||||
const total_wp = collect(data).sum('y') |
||||
return { |
||||
chart: { |
||||
plotBackgroundColor: null, |
||||
plotBorderWidth: null, |
||||
plotShadow: false, |
||||
type: 'pie', |
||||
zoomType: 'xy', |
||||
height: '300' |
||||
}, |
||||
title: { |
||||
text: `<u>${title}</u>`, |
||||
style: { fontSize: '14px' }, |
||||
useHTML: true |
||||
}, |
||||
tooltip: { |
||||
pointFormat: '<b>{point.percentage:.1f}%</b><br>: {point.y} dari ' + format_angka(total_wp) + ' total ' + attribute1 |
||||
}, |
||||
accessibility: { |
||||
point: { |
||||
valueSuffix: '%' |
||||
} |
||||
}, |
||||
plotOptions: { |
||||
pie: { |
||||
allowPointSelect: true, |
||||
cursor: 'pointer', |
||||
dataLabels: { |
||||
enabled: true, |
||||
style: { fontSize: '10px' }, |
||||
format: '{point.name}: <br> {point.percentage:.1f} %' |
||||
} |
||||
}, |
||||
series: { |
||||
cursor: 'pointer', |
||||
point: { |
||||
events: { |
||||
click: function () { |
||||
setLimaBesar(collect(this.series.data).pluck('key').all().slice(0, 5)) |
||||
setQuery(this.key) |
||||
setJenisChart(this.series.name) |
||||
setVisibleSidebar(true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: jenisChart, |
||||
data |
||||
} |
||||
] |
||||
} |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/klu/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
jenisChart, |
||||
limaBesar, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_WP', |
||||
header: 'Nama' |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT_MFWP', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN_MFWP', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN_MFWP ?? ''} ${dataRow.KECAMATAN_MFWP ?? ''} ${dataRow.KOTA_MFWP ?? ''} ${dataRow.PROPINSI_MFWP ?? ''}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_AR_MFWP', |
||||
header: 'AR' |
||||
}, |
||||
{ |
||||
accessorKey: 'FLAG_WPS_WPK', |
||||
header: 'WPS/WPK', |
||||
size: 100, |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_PEMBAYARAN_THN_TERAKHIR', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_GOLPOK', |
||||
header: 'Golongan Pokok' |
||||
}, |
||||
{ |
||||
accessorKey: 'TANGGAL_DAFTAR', |
||||
header: 'Tgl Daftar', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue(), 'DD-MMM-YY').format('YYYY-MM-DD') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
|
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col md="6"> |
||||
<HighchartsReact ref={refChart} highcharts={Highcharts} options={optionsChart(dataKluTerdaftar, 'KLU Terdaftar', 'NPWP', 'dataKluTerdaftar')} /> |
||||
</Col> |
||||
<Col md="6"> |
||||
<HighchartsReact |
||||
ref={refChart3} |
||||
highcharts={Highcharts} |
||||
options={optionsChart(dataRupiahBayar, 'Dominasi KLU berdarkan Jumlah Pembayaran (Rp)', 'Keseluruhan Pembayaran', 'dataRupiahBayar')} |
||||
/> |
||||
</Col> |
||||
<Col md="6"> |
||||
<HighchartsReact |
||||
ref={refChart1} |
||||
highcharts={Highcharts} |
||||
options={optionsChart(dataKluYgBayar, 'Dominasi KLU dengan pembayaran >0', 'NPWP', 'dataKluYgBayar')} |
||||
/> |
||||
</Col> |
||||
<Col md="6"> |
||||
<HighchartsReact |
||||
ref={refChart2} |
||||
highcharts={Highcharts} |
||||
options={optionsChart(dataKluYgTidakBayar, 'Dominasi KLU pembayaran <=0', 'NPWP', 'dataKluYgTidakBayar')} |
||||
/> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default KLU |
@ -0,0 +1,367 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Col, Row } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import $ from 'jquery' |
||||
|
||||
import { Skeleton } from 'primereact/skeleton' |
||||
import collect from 'collect.js' |
||||
|
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
var customParseFormat = require('dayjs/plugin/customParseFormat') |
||||
const fetchSize = 101 |
||||
|
||||
const PayComp = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const refChart = useRef(null) |
||||
const refChart1 = useRef(null) |
||||
const refChart2 = useRef(null) |
||||
const [dataC, setDataC] = useState(null) |
||||
const [dataMin1, setDataMin1] = useState(null) |
||||
const [dataMin2, setDataMin2] = useState(null) |
||||
const [loading, setLoading] = useState(false) |
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
const [tahunBulan, setTahunBulan] = useState(null) |
||||
|
||||
const currentMonth = '<?=currentMonth()?>' |
||||
const currentYear = '<?=currentYear()?>' |
||||
|
||||
useEffect(() => { |
||||
setLoading(true) |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranPayComp', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend, |
||||
tahun: currentYear, |
||||
bulan: currentMonth |
||||
}, |
||||
success: (data) => { |
||||
setDataC(data.dataC) |
||||
setDataMin1(data.dataMin1) |
||||
setDataMin2(data.dataMin2) |
||||
setLoading(false) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsChart = (data, title, type) => { |
||||
const total_wp = collect(data).sum('y') |
||||
return { |
||||
chart: { |
||||
plotBackgroundColor: null, |
||||
plotBorderWidth: null, |
||||
plotShadow: false, |
||||
type: 'pie', |
||||
zoomType: 'xy', |
||||
height: '300' |
||||
}, |
||||
title: { |
||||
text: title, |
||||
style: { fontSize: '10px' } |
||||
}, |
||||
tooltip: { |
||||
pointFormat: '<b>{point.percentage:.1f}%</b><br>Jml NPWP : {point.y} dari ' + format_angka(total_wp) + ' yang terdapat data penerimaannya' |
||||
}, |
||||
accessibility: { |
||||
point: { |
||||
valueSuffix: '%' |
||||
} |
||||
}, |
||||
plotOptions: { |
||||
pie: { |
||||
allowPointSelect: true, |
||||
cursor: 'pointer', |
||||
// colors: warna_garis,
|
||||
dataLabels: { |
||||
enabled: true, |
||||
style: { fontSize: '0.7rem' }, |
||||
format: '{point.name}: <br> {point.percentage:.1f} %' |
||||
} |
||||
//showInLegend: true
|
||||
}, |
||||
series: { |
||||
cursor: 'pointer', |
||||
point: { |
||||
events: { |
||||
click: function (a) { |
||||
setQuery(this.key) |
||||
setTahunBulan(this.thn_bln) |
||||
setVisibleSidebar(true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: type, |
||||
data |
||||
} |
||||
] |
||||
} |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/paycomp/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
tahunBulan, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_WP', |
||||
header: 'Nama' |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT_MFWP', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN_MFWP', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN_MFWP ?? ''} ${dataRow.KECAMATAN_MFWP ?? ''} ${dataRow.KOTA_MFWP ?? ''} ${dataRow.PROPINSI_MFWP ?? ''}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_AR_MFWP', |
||||
header: 'AR' |
||||
}, |
||||
{ |
||||
accessorKey: 'FLAG_WPS_WPK', |
||||
header: 'WPS/WPK', |
||||
size: 100, |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JML', |
||||
header: 'Jml Bulan', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'center' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_PEMBAYARAN_THN_TERAKHIR', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'TANGGAL_DAFTAR', |
||||
header: 'Tgl Daftar', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue(), 'DD-MMM-YY').format('YYYY-MM-DD') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
return ( |
||||
<> |
||||
{loading ? ( |
||||
<Row> |
||||
<Col md="12"> |
||||
<Skeleton className="" shape="rectangle" height="17rem" width="100%"></Skeleton> |
||||
</Col> |
||||
</Row> |
||||
) : ( |
||||
<Row> |
||||
<Col md="4"> |
||||
<HighchartsReact ref={refChart} highcharts={Highcharts} options={optionsChart(dataC, 's.d. bulan ini', 'C')} /> |
||||
</Col> |
||||
<Col md="4"> |
||||
<HighchartsReact ref={refChart1} highcharts={Highcharts} options={optionsChart(dataMin1, 's.d. bulan lalu', 'Min1')} /> |
||||
</Col> |
||||
<Col md="4"> |
||||
<HighchartsReact ref={refChart2} highcharts={Highcharts} options={optionsChart(dataMin2, 's.d. 2 bulan yang lalu', 'Min2')} /> |
||||
</Col> |
||||
</Row> |
||||
)} |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default PayComp |
@ -0,0 +1,363 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Col, Row } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import $ from 'jquery' |
||||
|
||||
import { Skeleton } from 'primereact/skeleton' |
||||
import collect from 'collect.js' |
||||
|
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
var customParseFormat = require('dayjs/plugin/customParseFormat') |
||||
const fetchSize = 101 |
||||
|
||||
const Pembayaran = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const refChart = useRef(null) |
||||
const refChart1 = useRef(null) |
||||
const refChart2 = useRef(null) |
||||
const [dataC, setDataC] = useState(null) |
||||
const [dataMin1, setDataMin1] = useState(null) |
||||
const [dataMin2, setDataMin2] = useState(null) |
||||
const [loading, setLoading] = useState(false) |
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
const [tahunBulan, setTahunBulan] = useState(null) |
||||
|
||||
const currentMonth = '<?=currentMonth()?>' |
||||
const currentYear = '<?=currentYear()?>' |
||||
|
||||
useEffect(() => { |
||||
setLoading(true) |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranPembayaran', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend, |
||||
tahun: currentYear, |
||||
bulan: currentMonth |
||||
}, |
||||
success: (data) => { |
||||
setDataC(data.dataC) |
||||
setDataMin1(data.dataMin1) |
||||
setDataMin2(data.dataMin2) |
||||
setLoading(false) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsChart = (data, title, type) => { |
||||
const total_wp = collect(data).sum('y') |
||||
return { |
||||
chart: { |
||||
plotBackgroundColor: null, |
||||
plotBorderWidth: null, |
||||
plotShadow: false, |
||||
type: 'pie', |
||||
zoomType: 'xy', |
||||
height: '300' |
||||
}, |
||||
title: { |
||||
text: title, |
||||
style: { fontSize: '10px' } |
||||
}, |
||||
tooltip: { |
||||
pointFormat: '<b>{point.percentage:.1f}%</b><br>Jml NPWP : {point.y} dari ' + format_angka(total_wp) |
||||
}, |
||||
accessibility: { |
||||
point: { |
||||
valueSuffix: '%' |
||||
} |
||||
}, |
||||
plotOptions: { |
||||
pie: { |
||||
allowPointSelect: true, |
||||
cursor: 'pointer', |
||||
//colors : warna_garis,
|
||||
dataLabels: { |
||||
enabled: true, |
||||
style: { fontSize: '10px' }, |
||||
format: '{point.name}: <br> {point.percentage:.1f} %' |
||||
} |
||||
// showInLegend: true
|
||||
}, |
||||
series: { |
||||
cursor: 'pointer', |
||||
point: { |
||||
events: { |
||||
click: function (a) { |
||||
setQuery(this.key) |
||||
setTahunBulan(this.thn_bln) |
||||
setVisibleSidebar(true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: type, |
||||
data |
||||
} |
||||
] |
||||
} |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/pembayaran/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
tahunBulan, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_WP', |
||||
header: 'Nama' |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT_MFWP', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN_MFWP', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN_MFWP ?? ''} ${dataRow.KECAMATAN_MFWP ?? ''} ${dataRow.KOTA_MFWP ?? ''} ${dataRow.PROPINSI_MFWP ?? ''}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_AR_MFWP', |
||||
header: 'AR' |
||||
}, |
||||
{ |
||||
accessorKey: 'FLAG_WPS_WPK', |
||||
header: 'WPS/WPK', |
||||
size: 100, |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JML', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'TANGGAL_DAFTAR', |
||||
header: 'Tgl Daftar', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue(), 'DD-MMM-YY').format('YYYY-MM-DD') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
|
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col md="4" className=""> |
||||
{loading ? ( |
||||
<div className="text-center"> |
||||
<Skeleton className="" shape="circle" size="15rem"></Skeleton> |
||||
</div> |
||||
) : ( |
||||
<HighchartsReact ref={refChart} highcharts={Highcharts} options={optionsChart(dataC, 's.d. bulan ini', 'C')} /> |
||||
)} |
||||
</Col> |
||||
<Col md="4"> |
||||
{loading ? ( |
||||
<Skeleton className="" shape="circle" size="15rem"></Skeleton> |
||||
) : ( |
||||
<HighchartsReact ref={refChart1} highcharts={Highcharts} options={optionsChart(dataMin1, 's.d. bulan lalu', 'Min1')} /> |
||||
)} |
||||
</Col> |
||||
<Col md="4"> |
||||
{loading ? ( |
||||
<Skeleton className="center text-center" shape="circle" size="15rem"></Skeleton> |
||||
) : ( |
||||
<HighchartsReact ref={refChart2} highcharts={Highcharts} options={optionsChart(dataMin2, 's.d. 2 bulan yang lalu', 'Min2')} /> |
||||
)} |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Pembayaran |
@ -0,0 +1,332 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Col, Row } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import collect from 'collect.js' |
||||
import $ from 'jquery' |
||||
|
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
var customParseFormat = require('dayjs/plugin/customParseFormat') |
||||
const fetchSize = 101 |
||||
|
||||
const Pengampu = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const refChart = useRef(null) |
||||
const refChart2 = useRef(null) |
||||
const [dataAssign, setDataAssign] = useState(null) |
||||
const [dataUnAssign, setDataUnAssign] = useState(null) |
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
const [jenisChart, setJenisChart] = useState(null) |
||||
|
||||
useEffect(() => { |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranPengampu', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend |
||||
}, |
||||
success: (data) => { |
||||
setDataAssign(data.assign) |
||||
setDataUnAssign(data.unassign) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsChart = (data, title, kode) => { |
||||
const total_wp = collect(data).sum('y') |
||||
return { |
||||
chart: { |
||||
plotBackgroundColor: null, |
||||
plotBorderWidth: null, |
||||
plotShadow: false, |
||||
type: 'pie', |
||||
zoomType: 'xy', |
||||
height: '300' |
||||
}, |
||||
title: { |
||||
text: title, |
||||
style: { fontSize: '10px' } |
||||
}, |
||||
tooltip: { |
||||
pointFormat: '<b>{point.percentage:.1f}%</b><br>: {point.y} dari ' + format_angka(total_wp) + ' total NPWP yang ada' |
||||
}, |
||||
accessibility: { |
||||
point: { |
||||
valueSuffix: '%' |
||||
} |
||||
}, |
||||
plotOptions: { |
||||
pie: { |
||||
allowPointSelect: true, |
||||
cursor: 'pointer', |
||||
dataLabels: { |
||||
enabled: true, |
||||
style: { fontSize: '10px' }, |
||||
format: '{point.name}: <br> {point.percentage:.1f} %' |
||||
} |
||||
}, |
||||
series: { |
||||
cursor: 'pointer', |
||||
point: { |
||||
events: { |
||||
click: function () { |
||||
setQuery(this.key) |
||||
setJenisChart(this.series.name) |
||||
setVisibleSidebar(true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: kode, |
||||
data |
||||
} |
||||
] |
||||
} |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query, jenisChart }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/pengampu/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
jenisChart, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_WP', |
||||
header: 'Nama' |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT_MFWP', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN_MFWP', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN_MFWP ?? ''} ${dataRow.KECAMATAN_MFWP ?? ''} ${dataRow.KOTA_MFWP ?? ''} ${dataRow.PROPINSI_MFWP ?? ''}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_AR_MFWP', |
||||
header: 'AR' |
||||
}, |
||||
{ |
||||
accessorKey: 'FLAG_WPS_WPK', |
||||
header: 'WPS/WPK', |
||||
size: 100, |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_PEMBAYARAN_THN_TERAKHIR', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'TANGGAL_DAFTAR', |
||||
header: 'Tgl Daftar', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue(), 'DD-MMM-YY').format('YYYY-MM-DD') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' }, |
||||
mantineTableBodyProps: { className: 'mb-3' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
|
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col md="6"> |
||||
<HighchartsReact ref={refChart} highcharts={Highcharts} options={optionsChart(dataAssign, 'KPP Terdaftar', 'assign')} /> |
||||
</Col> |
||||
<Col md="6"> |
||||
<HighchartsReact ref={refChart2} highcharts={Highcharts} options={optionsChart(dataUnAssign, 'Status UnAssign', 'unassign')} /> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} jenisChart={jenisChart} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Pengampu |
@ -0,0 +1,323 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Col, Row } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import collect from 'collect.js' |
||||
import $ from 'jquery' |
||||
|
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
var customParseFormat = require('dayjs/plugin/customParseFormat') |
||||
const fetchSize = 101 |
||||
|
||||
const SPTTahunan = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const refChart = useRef(null) |
||||
const [data, setData] = useState(null) |
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
|
||||
useEffect(() => { |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranSPTTahunan', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend |
||||
}, |
||||
success: (data) => { |
||||
setData(data.data) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsChart = (data, title) => { |
||||
const total_wp = collect(data).sum('y') |
||||
return { |
||||
chart: { |
||||
plotBackgroundColor: null, |
||||
plotBorderWidth: null, |
||||
plotShadow: false, |
||||
type: 'pie', |
||||
zoomType: 'xy', |
||||
height: '300' |
||||
}, |
||||
title: { |
||||
text: title, |
||||
style: { fontSize: '10px' } |
||||
}, |
||||
tooltip: { |
||||
pointFormat: '<b>{point.percentage:.1f}%</b><br>: {point.y} dari ' + format_angka(total_wp) + ' total NPWP yang ada' |
||||
}, |
||||
accessibility: { |
||||
point: { |
||||
valueSuffix: '%' |
||||
} |
||||
}, |
||||
plotOptions: { |
||||
pie: { |
||||
allowPointSelect: true, |
||||
cursor: 'pointer', |
||||
dataLabels: { |
||||
enabled: true, |
||||
style: { fontSize: '10px' }, |
||||
format: '{point.name}: <br> {point.percentage:.1f} %' |
||||
} |
||||
}, |
||||
series: { |
||||
cursor: 'pointer', |
||||
point: { |
||||
events: { |
||||
click: function (a) { |
||||
setQuery(this.key) |
||||
setVisibleSidebar(true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: '', |
||||
data |
||||
} |
||||
] |
||||
} |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query, tahunBulan }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/spttahunan/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_WP', |
||||
header: 'Nama' |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT_MFWP', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN_MFWP', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN_MFWP ?? ''} ${dataRow.KECAMATAN_MFWP ?? ''} ${dataRow.KOTA_MFWP ?? ''} ${dataRow.PROPINSI_MFWP ?? ''}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_AR_MFWP', |
||||
header: 'AR' |
||||
}, |
||||
{ |
||||
accessorKey: 'FLAG_WPS_WPK', |
||||
header: 'WPS/WPK', |
||||
size: 100, |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_PEMBAYARAN_THN_TERAKHIR', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'TANGGAL_DAFTAR', |
||||
header: 'Tgl Daftar', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue(), 'DD-MMM-YY').format('YYYY-MM-DD') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' }, |
||||
mantineTableBodyProps: { className: 'mb-3' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
|
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col> |
||||
<HighchartsReact ref={refChart} highcharts={Highcharts} options={optionsChart(data, 'SPT Tahunan')} /> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default SPTTahunan |
@ -0,0 +1,369 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import { Col, Row, Table } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import collect from 'collect.js' |
||||
import $ from 'jquery' |
||||
import { Skeleton } from 'primereact/skeleton' |
||||
|
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
var customParseFormat = require('dayjs/plugin/customParseFormat') |
||||
const fetchSize = 101 |
||||
|
||||
const Sof = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const [data, setData] = useState([]) |
||||
const [total, setTotal] = useState({ totalC: 0, totalP1: 0, totalP2: 0 }) |
||||
const [loading, setLoading] = useState(false) |
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
const [tahunBulan, setTahunBulan] = useState(null) |
||||
|
||||
const currentMonth = '<?=currentMonth()?>' |
||||
const currentYear = '<?=currentYear()?>' |
||||
|
||||
useEffect(() => { |
||||
setLoading(true) |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranSof', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend, |
||||
tahun: currentYear, |
||||
bulan: currentMonth |
||||
}, |
||||
success: (data) => { |
||||
setData(data.data) |
||||
setTotal({ |
||||
totalC: collect(data.data).sum('JML_C'), |
||||
totalP1: collect(data.data).sum('JML_P1'), |
||||
totalP2: collect(data.data).sum('JML_P2') |
||||
}) |
||||
setLoading(false) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const angkaOnClick = (key, tahunBulan) => { |
||||
setQuery(key) |
||||
setTahunBulan(tahunBulan) |
||||
setVisibleSidebar(true) |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query, tahunBulan }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/sof/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
tahunBulan, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_WP', |
||||
header: 'Nama' |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT_MFWP', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN_MFWP', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN_MFWP ?? ''} ${dataRow.KECAMATAN_MFWP ?? ''} ${dataRow.KOTA_MFWP ?? ''} ${dataRow.PROPINSI_MFWP ?? ''}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NAMA_AR_MFWP', |
||||
header: 'AR' |
||||
}, |
||||
{ |
||||
accessorKey: 'FLAG_WPS_WPK', |
||||
header: 'WPS/WPK', |
||||
size: 100, |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'LAPISAN', |
||||
header: 'Lapisan' |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_PEMBAYARAN_THN_TERAKHIR', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'TANGGAL_DAFTAR', |
||||
header: 'Tgl Daftar', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue(), 'DD-MMM-YY').format('YYYY-MM-DD') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' }, |
||||
mantineTableBodyProps: { className: 'mb-3' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
|
||||
return ( |
||||
<> |
||||
{loading ? ( |
||||
<Row> |
||||
<Col> |
||||
<Skeleton className="" shape="rectangle" height="20rem" width="100%"></Skeleton> |
||||
</Col> |
||||
</Row> |
||||
) : ( |
||||
<Row> |
||||
<Col> |
||||
<div className="d-flex justify-content-center"> |
||||
<Table bordered style={{ width: 'auto', fontSize: '0.85rem' }}> |
||||
<thead className="bg-primary text-white"> |
||||
<tr> |
||||
<th className="text-center text-white" rowSpan="2"> |
||||
Lapisan |
||||
</th> |
||||
<th className="text-center text-white" colSpan="2"> |
||||
s.d Sekarang |
||||
</th> |
||||
<th className="text-center text-white" colSpan="2"> |
||||
s.d Bulan Lalu |
||||
</th> |
||||
<th className="text-center text-white" colSpan="2"> |
||||
s.d 2 Bulan Lalu |
||||
</th> |
||||
</tr> |
||||
<tr> |
||||
<th className="text-center text-white">Jml WP</th> |
||||
<th className="text-center text-white">%</th> |
||||
<th className="text-center text-white">Jml WP</th> |
||||
<th className="text-center text-white">%</th> |
||||
<th className="text-center text-white">Jml WP</th> |
||||
<th className="text-center text-white">%</th> |
||||
</tr> |
||||
<tr className=""> |
||||
<th className="text-center text-white">1</th> |
||||
<th className="text-center text-white">2</th> |
||||
<th className="text-center text-white">3</th> |
||||
<th className="text-center text-white">4</th> |
||||
<th className="text-center text-white">5</th> |
||||
<th className="text-center text-white">6</th> |
||||
<th className="text-center text-white">7</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
{data.map((val, idx) => { |
||||
return ( |
||||
<tr key={idx}> |
||||
<td className="text-start p-1 font-weight-bold">{val.LAPISAN}</td> |
||||
<td className="text-center p-1 cursor-pointer text-blue underline" onClick={() => angkaOnClick(val.key, val.THNBLN_C)}> |
||||
{Number(val.JML_C).toLocaleString('id-ID')} |
||||
</td> |
||||
<td className="text-center p-1">{((val.JML_C / total.totalC) * 100).toFixed(2) + '%'}</td> |
||||
<td className="text-center p-1 cursor-pointer text-blue underline" onClick={() => angkaOnClick(val.key, val.THNBLN_P1)}> |
||||
{Number(val.JML_P1).toLocaleString('id-ID')} |
||||
</td> |
||||
<td className="text-center p-1">{((val.JML_P1 / total.totalP1) * 100).toFixed(2) + '%'}</td> |
||||
<td className="text-center p-1 cursor-pointer text-blue underline" onClick={() => angkaOnClick(val.key, val.THNBLN_P2)}> |
||||
{Number(val.JML_P2).toLocaleString('id-ID')} |
||||
</td> |
||||
<td className="text-center p-1">{((val.JML_P2 / total.totalP2) * 100).toFixed(2) + '%'}</td> |
||||
</tr> |
||||
) |
||||
})} |
||||
</tbody> |
||||
<tfoot> |
||||
<tr className="font-weight-bold"> |
||||
<td className="text-center">Total</td> |
||||
<td className="text-center">{Number(total.totalC).toLocaleString('id-ID')}</td> |
||||
<td className="text-center">100%</td> |
||||
<td className="text-center">{Number(total.totalP1).toLocaleString('id-ID')}</td> |
||||
<td className="text-center">100%</td> |
||||
<td className="text-center">{Number(total.totalP2).toLocaleString('id-ID')}</td> |
||||
<td className="text-center">100%</td> |
||||
</tr> |
||||
</tfoot> |
||||
</Table> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
)} |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} tahunBulan={tahunBulan} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Sof |
@ -0,0 +1,356 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Col, Row } from 'reactstrap' |
||||
import { format_angka } from '../util' |
||||
import collect from 'collect.js' |
||||
import $ from 'jquery' |
||||
|
||||
import { Sidebar } from 'primereact/sidebar' |
||||
import '/node_modules/primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import { Text } from '@mantine/core' |
||||
|
||||
import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query' |
||||
import dayjs from 'dayjs' |
||||
var relativeTime = require('dayjs/plugin/relativeTime') |
||||
var customParseFormat = require('dayjs/plugin/customParseFormat') |
||||
const fetchSize = 101 |
||||
|
||||
const ZonaPengawasan = ({ dataSend }) => { |
||||
const base_url = '<?=base_url()?>' |
||||
|
||||
const refChart = useRef(null) |
||||
const refChart2 = useRef(null) |
||||
const [dataAll, setDataAll] = useState(null) |
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(false) |
||||
const [query, setQuery] = useState(null) |
||||
|
||||
useEffect(() => { |
||||
$.get({ |
||||
url: base_url + 'kewilayahan/kytp/sebaranZonaPengawasan', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { |
||||
...dataSend |
||||
}, |
||||
success: (data) => { |
||||
setDataAll(data.all) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsChart = (data, title) => { |
||||
const total_wp = collect(data).sum('y') |
||||
return { |
||||
chart: { |
||||
plotBackgroundColor: null, |
||||
plotBorderWidth: null, |
||||
plotShadow: false, |
||||
type: 'pie', |
||||
zoomType: 'xy', |
||||
height: '300' |
||||
}, |
||||
title: { |
||||
text: title, |
||||
style: { fontSize: '10px' } |
||||
}, |
||||
tooltip: { |
||||
pointFormat: '<b>{point.percentage:.1f}%</b><br>: {point.y} dari ' + format_angka(total_wp) + ' total Lokasi KPDL' |
||||
}, |
||||
accessibility: { |
||||
point: { |
||||
valueSuffix: '%' |
||||
} |
||||
}, |
||||
plotOptions: { |
||||
pie: { |
||||
allowPointSelect: true, |
||||
cursor: 'pointer', |
||||
dataLabels: { |
||||
enabled: true, |
||||
style: { fontSize: '10px' }, |
||||
format: '{point.name}: <br> {point.percentage:.1f} %' |
||||
} |
||||
}, |
||||
series: { |
||||
cursor: 'pointer', |
||||
point: { |
||||
events: { |
||||
click: function () { |
||||
setQuery(this.key) |
||||
setVisibleSidebar(true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: '', |
||||
data |
||||
} |
||||
] |
||||
} |
||||
} |
||||
|
||||
const TableDetailGraph = ({ dataSend, query }) => { |
||||
const tableContainerRef = useRef(null) |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [columnFilters, setColumnFilters] = useState([]) |
||||
const [globalFilter, setGlobalFilter] = useState() |
||||
const [sorting, setSorting] = useState([]) |
||||
const base_url = '<?=base_url()?>' |
||||
const { data, fetchNextPage, isError, isFetching, isLoading } = useInfiniteQuery({ |
||||
queryKey: ['table-data', columnFilters, globalFilter, sorting], |
||||
|
||||
queryFn: async ({ pageParam = 0 }) => { |
||||
const url = new URL(base_url + 'kewilayahan/sebaran/zonapengawasan/detail') |
||||
url.searchParams.set('start', `${pageParam * fetchSize}`) |
||||
url.searchParams.set('size', `${fetchSize}`) |
||||
url.searchParams.set('filters', JSON.stringify(columnFilters ?? [])) |
||||
url.searchParams.set('globalFilter', globalFilter ?? '') |
||||
url.searchParams.set('sorting', JSON.stringify(sorting ?? [])) |
||||
|
||||
const response = await fetch(url.href, { |
||||
method: 'POST', |
||||
headers: { |
||||
Accept: 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
body: JSON.stringify({ |
||||
query, |
||||
...dataSend |
||||
}) |
||||
}) |
||||
|
||||
const json = await response.json() |
||||
return json |
||||
}, |
||||
getNextPageParam: (_lastGroup, groups) => groups.length, |
||||
keepPreviousData: true, |
||||
refetchOnWindowFocus: false |
||||
}) |
||||
|
||||
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]) |
||||
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 |
||||
const totalFetched = flatData.length |
||||
|
||||
const fetchMoreOnBottomReached = useCallback( |
||||
(containerRefElement) => { |
||||
if (containerRefElement) { |
||||
const { scrollHeight, scrollTop, clientHeight } = containerRefElement |
||||
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
||||
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) { |
||||
fetchNextPage() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
[fetchNextPage, isFetching, totalFetched, totalDBRowCount] |
||||
) |
||||
|
||||
const columns = [ |
||||
{ |
||||
accessorKey: 'NAMA', |
||||
header: 'Nama' |
||||
}, |
||||
|
||||
{ |
||||
accessorKey: 'MERK_USAHA', |
||||
header: 'Merk Usaha' |
||||
}, |
||||
{ |
||||
accessorKey: 'NO_IDENTITAS', |
||||
header: 'No Identitas' |
||||
}, |
||||
{ |
||||
accessorKey: 'NPWP', |
||||
header: 'NPWP', |
||||
enableClickToCopy: true, |
||||
size: 150 |
||||
}, |
||||
{ |
||||
accessorKey: 'ALAMAT', |
||||
header: 'Alamat' |
||||
}, |
||||
{ |
||||
accessorKey: 'KELURAHAN', |
||||
header: 'Wil. Adm.', |
||||
Cell: (data) => { |
||||
const dataRow = data.row.original |
||||
return `${dataRow.KELURAHAN} ${dataRow.KECAMATAN} ${dataRow.KABUPATEN} ${dataRow.PROVINSI}` |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'STATUS_WP_MFWP', |
||||
header: 'Status WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'JNS_WP_MFWP', |
||||
header: 'Jenis WP' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KANTOR_PENGAMPU', |
||||
header: 'KPP Terdaftar' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_AR_PENGAMPU', |
||||
header: 'AR Pengampu' |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_PEMBAYARAN_THN_TERAKHIR', |
||||
header: 'Rp', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
size: 100 |
||||
}, |
||||
{ |
||||
accessorKey: 'KETERANGAN', |
||||
header: 'SPT' |
||||
}, |
||||
{ |
||||
accessorKey: 'SUM_NILAI', |
||||
header: 'NILAI DATA', |
||||
Cell: ({ cell }) => parseFloat(cell.getValue()).toLocaleString('id-ID'), |
||||
mantineTableHeadCellProps: { |
||||
align: 'right' |
||||
}, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_KPP_ZONA', |
||||
header: 'KPP Lokasi' |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_AR_ZONA', |
||||
header: 'AR Wilayah', |
||||
filter: false |
||||
}, |
||||
{ |
||||
accessorKey: 'NM_PEREKAM', |
||||
header: 'Perekam' |
||||
}, |
||||
{ |
||||
accessorKey: 'CREATION_DATE', |
||||
header: 'Tgl Rekam', |
||||
Cell: ({ cell }) => { |
||||
return dayjs(cell.getValue()).format('YYYY-MM-DD HH:mm:ss') |
||||
} |
||||
} |
||||
] |
||||
|
||||
useEffect(() => { |
||||
if (rowVirtualizerInstanceRef.current) { |
||||
try { |
||||
rowVirtualizerInstanceRef.current.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.error(e) |
||||
} |
||||
} |
||||
}, [sorting, columnFilters, globalFilter]) |
||||
|
||||
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
||||
|
||||
useEffect(() => { |
||||
fetchMoreOnBottomReached(tableContainerRef.current) |
||||
}, [fetchMoreOnBottomReached]) |
||||
|
||||
const table1 = useMantineReactTable({ |
||||
columns, |
||||
data: flatData, |
||||
enablePagination: false, |
||||
enableRowNumbers: true, |
||||
enableRowVirtualization: true, //optional, but recommended if it is likely going to be more than 100 rows
|
||||
manualFiltering: true, |
||||
manualSorting: true, |
||||
mantineTableContainerProps: { |
||||
ref: tableContainerRef, //get access to the table container element
|
||||
sx: { maxHeight: '600px' }, //give the table a max height
|
||||
onScroll: ( |
||||
event //add an event listener to the table container element
|
||||
) => fetchMoreOnBottomReached(event.target) |
||||
}, |
||||
mantineToolbarAlertBannerProps: { |
||||
color: 'red', |
||||
children: 'Error loading data' |
||||
}, |
||||
onColumnFiltersChange: setColumnFilters, |
||||
onGlobalFilterChange: setGlobalFilter, |
||||
onSortingChange: setSorting, |
||||
renderBottomToolbarCustomActions: () => ( |
||||
<Text className="text-sm"> |
||||
Fetched {totalFetched} of {totalDBRowCount} total rows. |
||||
</Text> |
||||
), |
||||
state: { |
||||
columnFilters, |
||||
globalFilter, |
||||
isLoading, |
||||
showAlertBanner: isError, |
||||
showProgressBars: isFetching, |
||||
sorting |
||||
}, |
||||
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
||||
rowVirtualizerProps: { overscan: 10 }, |
||||
mantineTableBodyCellProps: { className: 'p-1 text-xs' }, |
||||
mantineTableBodyProps: { className: 'mb-3' } |
||||
}) |
||||
|
||||
return <MantineReactTable table={table1} /> |
||||
} |
||||
|
||||
const queryClient = new QueryClient() |
||||
|
||||
return ( |
||||
<> |
||||
<Row className="center"> |
||||
<Col md="12"> |
||||
<HighchartsReact |
||||
ref={refChart} |
||||
highcharts={Highcharts} |
||||
options={optionsChart(dataAll, 'Sebaran Zona Pengawasan yang telah dilakukan kegiatan MATOA/KPDL')} |
||||
/> |
||||
<div className="center text-center"> |
||||
<span className="text-center">Sebaran Lokasi Usaha atas WP Terdaftar</span> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col> |
||||
<Sidebar |
||||
header={ |
||||
<> |
||||
<h4>Detail Data</h4> |
||||
</> |
||||
} |
||||
visible={visibleSidebar} |
||||
position="bottom" |
||||
onHide={() => setVisibleSidebar(false)} |
||||
style={{ height: 'calc(100vh - 100px)' }} |
||||
blockScroll |
||||
pt={{ header: { className: 'p-1' }, closeButton: { style: { width: '2rem', height: '1rem' } } }} |
||||
> |
||||
<Row> |
||||
<Col> |
||||
<QueryClientProvider client={queryClient}> |
||||
<TableDetailGraph dataSend={dataSend} query={query} /> |
||||
</QueryClientProvider> |
||||
</Col> |
||||
</Row> |
||||
</Sidebar> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default ZonaPengawasan |
@ -0,0 +1,754 @@
|
||||
import React, { useEffect, useRef, useState } from 'react' |
||||
import ReactDOM from 'react-dom' |
||||
import { Card, CardBody, CardHeader, CardText, CardTitle, Col, Label, Nav, NavItem, NavLink, Row, TabContent, TabPane } from 'reactstrap' |
||||
import Select from 'react-select' |
||||
import jquery, { data, getJSON } from 'jquery' |
||||
import collect from 'collect.js' |
||||
import { format_angka, isObjEmpty } from './util' |
||||
import { MultiSelect } from 'react-multi-select-component' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Toast } from 'primereact/toast' |
||||
import { Button as ButtonP } from 'primereact/button' |
||||
import 'primereact/resources/themes/bootstrap4-light-blue/theme.css' |
||||
import 'primeflex/primeflex.css' |
||||
import TabProgresifitas from './TabProgresifitas' |
||||
import TabPenugasan from './TabPenugasan' |
||||
import NipPerekam from './componentDepan/NipPerekam' |
||||
import NipPengampu from './componentDepan/NipPengampu' |
||||
import { Provider, useDispatch, useSelector } from 'react-redux' |
||||
import { store } from './store/store' |
||||
import { setSelectedOpsi } from './store/KpdlStore' |
||||
// import 'bootstrap/dist/css/bootstrap.m in.css';
|
||||
// let datasend = {}
|
||||
|
||||
const Root = () => { |
||||
const base_url = '<?=base_url()?>' |
||||
const refChart1 = useRef(null) |
||||
const toast = useRef(null) |
||||
const dispatch = useDispatch() |
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
const [dataOpsi, setDataOpsi] = useState({}) |
||||
const [active, setActive] = useState('zona') |
||||
|
||||
const [hiddenGraphMatoa, setHiddenGraphMatoa] = useState(false) |
||||
|
||||
const toggle = (tab) => { |
||||
setActive(tab) |
||||
} |
||||
|
||||
const [prop, setProp] = useState({}) |
||||
const [kota, setKota] = useState({}) |
||||
const [kec, setKec] = useState([]) |
||||
const [kel, setKel] = useState([]) |
||||
|
||||
const [propSelected, setPropSelected] = useState({}) |
||||
const [kotaSelected, setKotaSelected] = useState({}) |
||||
const [kecSelected, setKecSelected] = useState([]) |
||||
const [kelSelected, setKelSelected] = useState([]) |
||||
|
||||
const [kanwil, setKanwil] = useState({}) |
||||
const [kpp, setKpp] = useState({}) |
||||
const [seksi, setSeksi] = useState([]) |
||||
const [ar, setAr] = useState([]) |
||||
const [zp, setZp] = useState([]) |
||||
|
||||
const [kanwilSelected, setKanwilSelected] = useState({}) |
||||
const [kppSelected, setKppSelected] = useState({}) |
||||
const [seksiSelected, setSeksiSelected] = useState([]) |
||||
const [arSelected, setArSelected] = useState([]) |
||||
const [zpSelected, setZpSelected] = useState([]) |
||||
|
||||
const [dataSend, setDataSend] = useState({ opsiWilZona: null, adm4_pcode: [], id_poly_zona: [], nip_ar_perekam: [], nip_ar_pengampu: [] }) |
||||
const [dataGraphMatoa, setDataGraphMatoa] = useState({ poi_agg: [], kpdl_agg: [] }) |
||||
// let session = null
|
||||
const [session, setSession] = useState({}) |
||||
useEffect(() => { |
||||
// setTimeout(() => {
|
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/user', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
success: (data) => { |
||||
setSession(data) |
||||
if (data.kppadm === '000') { |
||||
toggle('wilayah') |
||||
} else { |
||||
toggle('zona') |
||||
} |
||||
} |
||||
}) |
||||
// }, 2000)
|
||||
|
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/propinsi', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
success: (data) => { |
||||
setProp(data) |
||||
} |
||||
}) |
||||
|
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/zpkanwil', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
success: (data) => { |
||||
setKanwil(data) |
||||
} |
||||
}) |
||||
|
||||
jquery.getJSON(base_url + 'kewilayahan/ref/opsi').then((response) => { |
||||
setDataOpsi(response) |
||||
dispatch(setSelectedOpsi(response.default)) |
||||
}) |
||||
|
||||
let judul = document.getElementById('judul') |
||||
judul.innerHTML = '<h3><b><strong>E-Geospatial Thematic Tax</strong></b></h3>' |
||||
}, []) |
||||
|
||||
useEffect(() => { |
||||
setKota({}) |
||||
setKec([]) |
||||
setKel([]) |
||||
setKotaSelected({}) |
||||
setKecSelected([]) |
||||
setKelSelected([]) |
||||
if (propSelected && !isObjEmpty(propSelected)) { |
||||
const prop = propSelected.value |
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kota', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
data: { prop }, |
||||
success: (data) => { |
||||
setKota(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [propSelected]) |
||||
|
||||
useEffect(() => { |
||||
setKec([]) |
||||
setKel([]) |
||||
setKecSelected([]) |
||||
setKelSelected([]) |
||||
if (kotaSelected && !isObjEmpty(kotaSelected)) { |
||||
const kota = kotaSelected.value |
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kecamatan', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
data: { kota }, |
||||
success: (data) => { |
||||
setKec(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [kotaSelected]) |
||||
|
||||
useEffect(() => { |
||||
setKel([]) |
||||
setKelSelected([]) |
||||
if (kecSelected.length && !isObjEmpty(kecSelected)) { |
||||
const kec = collect(kecSelected).pluck('value').all() |
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kelurahan', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { kec }, |
||||
success: (data) => { |
||||
setKel(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [kecSelected]) |
||||
|
||||
useEffect(() => { |
||||
setKpp(null) |
||||
setSeksi([]) |
||||
setAr([]) |
||||
setZp([]) |
||||
setKppSelected(null) |
||||
setSeksiSelected([]) |
||||
setArSelected([]) |
||||
setZpSelected([]) |
||||
if (kanwilSelected && !isObjEmpty(kanwilSelected)) { |
||||
const kanwil = kanwilSelected.value |
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/zpkpp', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { kanwil }, |
||||
success: (data) => { |
||||
setKpp(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [kanwilSelected]) |
||||
|
||||
useEffect(() => { |
||||
setSeksi([]) |
||||
setAr([]) |
||||
setZp([]) |
||||
setSeksiSelected([]) |
||||
setArSelected([]) |
||||
setZpSelected([]) |
||||
if (kppSelected && !isObjEmpty(kppSelected)) { |
||||
const kpp = kppSelected.value |
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/zpseksi', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { kpp }, |
||||
success: (data) => { |
||||
setSeksi(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [kppSelected]) |
||||
|
||||
useEffect(() => { |
||||
setAr([]) |
||||
setZp([]) |
||||
setArSelected([]) |
||||
setZpSelected([]) |
||||
if (seksiSelected && !isObjEmpty(seksiSelected)) { |
||||
const kpp = kppSelected.value |
||||
const seksi = collect(seksiSelected).pluck('value').all() |
||||
|
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/zpar', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { kpp, seksi }, |
||||
success: (data) => { |
||||
setAr(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [seksiSelected]) |
||||
|
||||
useEffect(() => { |
||||
setZp([]) |
||||
setZpSelected([]) |
||||
if (arSelected && !isObjEmpty(arSelected)) { |
||||
const kpp = kppSelected.value |
||||
const seksi = collect(seksiSelected).pluck('value').all() |
||||
const ar = collect(arSelected).pluck('value').all() |
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/ref/zpzp', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { kpp, seksi, ar }, |
||||
success: (data) => { |
||||
setZp(data) |
||||
} |
||||
}) |
||||
} |
||||
}, [arSelected]) |
||||
|
||||
const buttonProsesOnClick = () => { |
||||
const opsiWilZona = active |
||||
const adm4_pcode = collect(kelSelected).pluck('value').all() |
||||
const id_poly_zona = collect(zpSelected).pluck('value').all() |
||||
const nip_ar_pengampu = collect().pluck('value').all() |
||||
|
||||
switch (opsiWilZona) { |
||||
case 'wilayah': |
||||
if (adm4_pcode.length) { |
||||
dispatch(setSelectedOpsi(dataOpsi.wilayah)) |
||||
setDataSend({ opsiWilZona: dataOpsi.wilayah.key, adm4_pcode, id_poly_zona: [] }) |
||||
setHiddenGraphMatoa(false) |
||||
} else { |
||||
toast.current.show({ severity: 'info', summary: 'Info', detail: 'Kelurahan harus dipilih' }) |
||||
} |
||||
break |
||||
case 'zona': |
||||
if (id_poly_zona.length) { |
||||
dispatch(setSelectedOpsi(dataOpsi.zona)) |
||||
setDataSend({ opsiWilZona: dataOpsi.zona.key, adm4_pcode: [], id_poly_zona }) |
||||
setHiddenGraphMatoa(false) |
||||
} else { |
||||
toast.current.show({ severity: 'info', summary: 'Info', detail: 'Zona harus dipilih' }) |
||||
} |
||||
break |
||||
|
||||
default: |
||||
break |
||||
} |
||||
} |
||||
|
||||
useEffect(() => {}, [storeKpdl.selectedOpsi]) |
||||
|
||||
useEffect(() => { |
||||
Highcharts.setOptions({ |
||||
accessibility: false, |
||||
lang: { |
||||
decimalPoint: ',', |
||||
thousandsSep: '.', |
||||
numericSymbols: ['rb', 'jt', 'M', 'T', 'P', 'E'] |
||||
}, |
||||
|
||||
tooltip: { |
||||
yDecimals: 2 // If you want to add 2 decimals
|
||||
} |
||||
}) |
||||
jquery.ajax({ |
||||
url: base_url + 'kewilayahan/kytp/graph_matoa', |
||||
dataType: 'json', |
||||
type: 'POST', |
||||
data: { ...dataSend }, |
||||
success: (data) => { |
||||
setDataGraphMatoa(data) |
||||
} |
||||
}) |
||||
}, [dataSend]) |
||||
|
||||
const optionsGraphMatoaAgg = { |
||||
chart: { |
||||
zoomType: 'xy', |
||||
height: '320' |
||||
}, |
||||
title: { |
||||
text: 'Poi Google Vs Matoa', |
||||
style: { fontSize: '14px' } |
||||
}, |
||||
|
||||
xAxis: [ |
||||
{ |
||||
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], |
||||
crosshair: true |
||||
} |
||||
], |
||||
yAxis: [ |
||||
{ |
||||
gridLineWidth: 0, |
||||
title: { |
||||
text: '', |
||||
style: { |
||||
color: Highcharts.getOptions().colors[0] |
||||
} |
||||
}, |
||||
labels: { |
||||
//format: '{value}',
|
||||
style: { |
||||
color: Highcharts.getOptions().colors[0] |
||||
} |
||||
}, |
||||
visible: false |
||||
}, |
||||
{ |
||||
labels: { |
||||
//format: '{value}',
|
||||
style: { |
||||
color: Highcharts.getOptions().colors[1] |
||||
} |
||||
}, |
||||
title: { |
||||
text: 'NPWP Work True', |
||||
style: { |
||||
color: Highcharts.getOptions().colors[1] |
||||
} |
||||
}, |
||||
opposite: true, |
||||
visible: false |
||||
}, |
||||
{ |
||||
gridLineWidth: 0, |
||||
title: { |
||||
text: 'Rupiah', |
||||
style: { |
||||
color: Highcharts.getOptions().colors[2] |
||||
} |
||||
}, |
||||
labels: { |
||||
//format: '{value}',
|
||||
style: { |
||||
color: Highcharts.getOptions().colors[2] |
||||
} |
||||
}, |
||||
opposite: true, |
||||
visible: false |
||||
}, |
||||
{ |
||||
gridLineWidth: 0, |
||||
title: { |
||||
text: '', |
||||
style: { |
||||
color: '#FF0000' |
||||
} |
||||
}, |
||||
labels: { |
||||
//format: '{value}',
|
||||
style: { |
||||
color: '##FF0000' |
||||
} |
||||
}, |
||||
opposite: true |
||||
} |
||||
], |
||||
tooltip: { |
||||
shared: true |
||||
}, |
||||
legend: { |
||||
layout: 'horizontal', |
||||
align: 'center', |
||||
//x: 80,
|
||||
verticalAlign: 'top', |
||||
//y: 55,
|
||||
//floating: true,
|
||||
backgroundColor: |
||||
Highcharts.defaultOptions.legend.backgroundColor || // theme
|
||||
'rgba(255,255,255,0.25)' |
||||
}, |
||||
series: [ |
||||
{ |
||||
color: '#FF0000', |
||||
name: 'Jml PoI', |
||||
type: 'column', |
||||
yAxis: 3, |
||||
data: dataGraphMatoa.poi_agg, |
||||
marker: { |
||||
enabled: true |
||||
}, |
||||
tooltip: { |
||||
valueSuffix: ' PoI' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'Matoa', |
||||
type: 'spline', |
||||
yAxis: 3, |
||||
data: dataGraphMatoa.kpdl_agg, |
||||
marker: { |
||||
enabled: true |
||||
}, |
||||
tooltip: { |
||||
pointFormatter: function () { |
||||
const idx = this.index |
||||
let pct_coverage |
||||
const jml_poi_agg = dataGraphMatoa.poi_agg |
||||
if (jml_poi_agg[idx] && jml_poi_agg[idx] !== 0) { |
||||
pct_coverage = format_angka(parseFloat((parseFloat(this.y) / jml_poi_agg[idx]) * 100).toFixed(2)) + '%' |
||||
} |
||||
|
||||
let s = |
||||
'<span style="color:' + |
||||
this.color + |
||||
'">\u25CF</span> ' + |
||||
this.series.name + |
||||
': <b>' + |
||||
format_angka(this.y) + |
||||
' lokasi kpdl</b> ' + |
||||
(pct_coverage ? '(' + pct_coverage + ')<br>\n' : '<br>') |
||||
return s |
||||
}, |
||||
shared: false |
||||
}, |
||||
visible: true, |
||||
color: '#8000ff' |
||||
} |
||||
] |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
<Row> |
||||
<Col sm="12"> |
||||
<Card> |
||||
<CardBody> |
||||
<Nav tabs> |
||||
{session.kppadm === '000' ? ( |
||||
<NavItem> |
||||
<NavLink |
||||
style={{ cursor: 'pointer' }} |
||||
active={active === 'wilayah'} |
||||
onClick={() => { |
||||
toggle('wilayah') |
||||
}} |
||||
> |
||||
Wilayah Adm. |
||||
</NavLink> |
||||
</NavItem> |
||||
) : null} |
||||
<NavItem> |
||||
<NavLink |
||||
style={{ cursor: 'pointer' }} |
||||
active={active === 'zona'} |
||||
onClick={() => { |
||||
toggle('zona') |
||||
}} |
||||
> |
||||
Zona Pengawasan |
||||
</NavLink> |
||||
</NavItem> |
||||
<NavItem> |
||||
<NavLink |
||||
style={{ cursor: 'pointer' }} |
||||
active={active === 'perekam'} |
||||
onClick={() => { |
||||
toggle('perekam') |
||||
}} |
||||
> |
||||
Perekam |
||||
</NavLink> |
||||
</NavItem> |
||||
<NavItem> |
||||
<NavLink |
||||
style={{ cursor: 'pointer' }} |
||||
active={active === 'pengampu'} |
||||
onClick={() => { |
||||
toggle('pengampu') |
||||
}} |
||||
> |
||||
Pengampu |
||||
</NavLink> |
||||
</NavItem> |
||||
</Nav> |
||||
<TabContent className="py-3" activeTab={active}> |
||||
<TabPane tabId="wilayah"> |
||||
<Row> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="basicInput"> |
||||
Propinsi |
||||
</Label> |
||||
<Select |
||||
placeholder="Pilih Propinsi" |
||||
className="basic-single w-100" |
||||
onChange={(e) => { |
||||
setPropSelected(e) |
||||
}} |
||||
classNamePrefix="select" |
||||
defaultValue={propSelected} |
||||
value={propSelected} |
||||
isClearable={false} |
||||
options={prop} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="basicInput"> |
||||
Kota/Kab |
||||
</Label> |
||||
<Select |
||||
placeholder="Pilih Kota/Kab" |
||||
className="basic-single w-100" |
||||
onChange={(e) => { |
||||
setKotaSelected(e) |
||||
}} |
||||
classNamePrefix="select" |
||||
defaultValue={kotaSelected} |
||||
value={kotaSelected} |
||||
isClearable={false} |
||||
options={kota} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih Kecamatan"> |
||||
Kecamatan |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={kec} |
||||
value={kecSelected} |
||||
onChange={(e) => { |
||||
setKecSelected(e) |
||||
}} |
||||
labelledBy="Pilih Kecamatan" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih Kecamatan' }} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih Kelurahan"> |
||||
Kelurahan |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={kel} |
||||
value={kelSelected} |
||||
onChange={(e) => { |
||||
setKelSelected(e) |
||||
}} |
||||
labelledBy="Pilih Kelurahan" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih Kelurahan' }} |
||||
/> |
||||
</Col> |
||||
</Row> |
||||
<Row className="mt-2"> |
||||
<Col sm="12"> |
||||
<ButtonP onClick={() => buttonProsesOnClick()} label="Proses" severity="" rounded className="w-10rem text-white text-base" /> |
||||
</Col> |
||||
</Row> |
||||
</TabPane> |
||||
<TabPane tabId="zona"> |
||||
<Row> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="basicInput"> |
||||
Kanwil |
||||
</Label> |
||||
<Select |
||||
placeholder="Pilih Kanwil" |
||||
className="basic-single w-100" |
||||
onChange={(e) => { |
||||
setKanwilSelected(e) |
||||
}} |
||||
classNamePrefix="select" |
||||
// defaultValue={kanwilSelected}
|
||||
value={kanwilSelected} |
||||
isClearable={false} |
||||
options={kanwil} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih KPP"> |
||||
KPP |
||||
</Label> |
||||
<Select |
||||
placeholder="Pilih KPP" |
||||
className="basic-single w-100" |
||||
onChange={(e) => { |
||||
setKppSelected(e) |
||||
}} |
||||
classNamePrefix="select" |
||||
// defaultValue={kanwilSelected}
|
||||
value={kppSelected} |
||||
isClearable={false} |
||||
options={kpp} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih Seksi"> |
||||
Seksi |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={seksi} |
||||
value={seksiSelected} |
||||
onChange={(e) => { |
||||
setSeksiSelected(e) |
||||
}} |
||||
labelledBy="Pilih Seksi" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih Seksi' }} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih AR"> |
||||
AR |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={ar} |
||||
value={arSelected} |
||||
onChange={(e) => { |
||||
setArSelected(e) |
||||
}} |
||||
labelledBy="Pilih AR" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih AR' }} |
||||
/> |
||||
</Col> |
||||
</Row> |
||||
<Row className="mt-2"> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih Zona"> |
||||
Zona Pengawasan |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={zp} |
||||
value={zpSelected} |
||||
onChange={(e) => { |
||||
setZpSelected(e) |
||||
}} |
||||
labelledBy="Pilih Zona" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih Zona' }} |
||||
/> |
||||
</Col> |
||||
<Col md="3" className="pt-4"> |
||||
<ButtonP onClick={() => buttonProsesOnClick()} label="Proses" severity="" rounded className="w-10rem text-white text-base" /> |
||||
</Col> |
||||
</Row> |
||||
</TabPane> |
||||
<TabPane tabId="perekam"> |
||||
<NipPerekam |
||||
dataSend={dataSend} |
||||
setDataSend={setDataSend} |
||||
activeTab={active} |
||||
toast={toast} |
||||
setHiddenGraphMatoa={setHiddenGraphMatoa} |
||||
dataOpsi={dataOpsi} |
||||
/> |
||||
</TabPane> |
||||
<TabPane tabId="pengampu"> |
||||
<NipPengampu |
||||
dataSend={dataSend} |
||||
setDataSend={setDataSend} |
||||
activeTab={active} |
||||
toast={toast} |
||||
setHiddenGraphMatoa={setHiddenGraphMatoa} |
||||
dataOpsi={dataOpsi} |
||||
/> |
||||
</TabPane> |
||||
</TabContent> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
</Row> |
||||
|
||||
<Row hidden={['pengampu', 'perekam'].includes(storeKpdl.selectedOpsi?.name)}> |
||||
<Col sm="12"> |
||||
<Card> |
||||
<CardHeader className="d-flex justify-content-center p-2"> |
||||
<CardTitle tag={'h1'} className="font-weight-bold"> |
||||
Statistik Penguasaan Wilayah |
||||
</CardTitle> |
||||
</CardHeader> |
||||
<CardBody className="p-1"> |
||||
<div id="graph_matoa_agg"> |
||||
<HighchartsReact ref={refChart1} highcharts={Highcharts} options={optionsGraphMatoaAgg} /> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col sm="12"> |
||||
<Card> |
||||
<CardHeader className="d-flex justify-content-center p-2"> |
||||
<CardTitle tag={'h1'} className="font-weight-bold"> |
||||
Statistik Progresifitas & Sebaran Data Hasil Kegiatan Matoa |
||||
</CardTitle> |
||||
</CardHeader> |
||||
<CardBody> |
||||
<TabProgresifitas dataSend={dataSend} /> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<Col sm="12"> |
||||
<TabPenugasan dataSend={dataSend} /> |
||||
</Col> |
||||
</Row> |
||||
<Toast ref={toast} /> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
const container = document.getElementById('app') |
||||
const component = ( |
||||
<Provider store={store}> |
||||
<Root /> |
||||
</Provider> |
||||
) |
||||
|
||||
ReactDOM.render(component, container) |
@ -0,0 +1,27 @@
|
||||
<link rel="stylesheet" href="<?=base_url('public/kpdl/dist/kpdl.css')?>">
|
||||
<style> |
||||
.highcharts-credits{ |
||||
visibility: hidden; |
||||
} |
||||
</style> |
||||
<?php |
||||
helper('Kpdl'); |
||||
?> |
||||
|
||||
<div class="main-content"> |
||||
<div class="container-fluid"> |
||||
<div id="app"></div> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
|
||||
<?php $isDevelopment = ENVIRONMENT === 'development';?> |
||||
|
||||
<script type="text/javascript"> |
||||
|
||||
<?php echo view('kewilayahan/dist/kpdl.js') ?> |
||||
</script> |
||||
|
||||
<?php echo view('inc/js.php') ?> |
@ -0,0 +1,24 @@
|
||||
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' |
||||
export const kpdlSlice = createSlice({ |
||||
name: 'kpdl', |
||||
initialState: { |
||||
selectedOpsi: null, |
||||
dataMonitoring: [], |
||||
dataUrut: { urutKanwil: [], urutKpp: [], urutSeksi: [], urutPegawai: [] } |
||||
}, |
||||
reducers: { |
||||
setSelectedOpsi: (state, action) => { |
||||
state.selectedOpsi = action.payload |
||||
}, |
||||
setDataMonitoring: (state, action) => { |
||||
state.dataMonitoring = action.payload |
||||
}, |
||||
setDataUrut: (state, action) => { |
||||
state.dataUrut = action.payload |
||||
} |
||||
} |
||||
}) |
||||
|
||||
export const { setSelectedOpsi, setDataMonitoring, setDataUrut } = kpdlSlice.actions |
||||
|
||||
export default kpdlSlice.reducer |
@ -0,0 +1,15 @@
|
||||
import { configureStore } from "@reduxjs/toolkit" |
||||
import kpdl from "./KpdlStore" |
||||
const rootReducer = { |
||||
kpdl |
||||
} |
||||
const store = configureStore({ |
||||
reducer: rootReducer, |
||||
middleware: (getDefaultMiddleware) => { |
||||
return getDefaultMiddleware({ |
||||
serializableCheck: false |
||||
}) |
||||
} |
||||
}) |
||||
|
||||
export { store } |
@ -0,0 +1,16 @@
|
||||
export const isObjEmpty = (obj) => Object.keys(obj).length === 0 |
||||
export function format_angka(num) { |
||||
var num_parts = num.toString().split('.') |
||||
num_parts[0] = num_parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.') |
||||
return num_parts.join(',') |
||||
} |
||||
export function titleCase(str) { |
||||
var splitStr = str.toLowerCase().split(' ') |
||||
for (var i = 0; i < splitStr.length; i++) { |
||||
// You do not need to check if i is larger than splitStr length, as your for does that for you
|
||||
// Assign it back to the array
|
||||
splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1) |
||||
} |
||||
// Directly return the joined string
|
||||
return splitStr.join(' ') |
||||
} |
@ -0,0 +1,255 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Card, CardBody, CardHeader, CardTitle, Col, Label, Row } from 'reactstrap' |
||||
import collect from 'collect.js' |
||||
import { Toast } from 'primereact/toast' |
||||
import 'primereact/resources/themes/bootstrap4-light-blue/theme.css' |
||||
import 'primeflex/primeflex.css' |
||||
import { MantineReactTable } from 'mantine-react-table' |
||||
import $ from 'jquery' |
||||
import { isObjEmpty, titleCase } from '../../kytp/util' |
||||
import { useSelector } from 'react-redux' |
||||
const date = new Date() |
||||
|
||||
const Kanwil = () => { |
||||
const base_url = '<?=base_url()?>' |
||||
const toast = useRef(null) |
||||
|
||||
const refChart1 = useRef() |
||||
const refChart2 = useRef() |
||||
const refChart3 = useRef() |
||||
// const refChart4 = useRef()
|
||||
|
||||
const [data, setData] = useState([]) |
||||
|
||||
const [urutKanwil, setUrutKanwil] = useState({ categories: [], dataSeries: [] }) |
||||
const [urutKpp, setUrutKpp] = useState({ categories: [], dataSeries: [] }) |
||||
const [urutSeksi, setUrutSeksi] = useState({ categories: [], dataSeries: [] }) |
||||
const [urutPegawai, setUrutPegawai] = useState({ categories: [], dataSeries: [] }) |
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
|
||||
useEffect(() => { |
||||
// if (isObjEmpty(paramSelected)) {
|
||||
// return
|
||||
// }
|
||||
// const kanwil = collect(kanwilSelected).pluck('value').all()
|
||||
// const kpp = collect(kppSelected).pluck('value').all()
|
||||
const data = storeKpdl.dataUrut |
||||
|
||||
setUrutKanwil(() => { |
||||
const categories = [] |
||||
const dataSeries = [] |
||||
data.urutKanwil.map((val, idx) => { |
||||
categories.push(String(val.LABEL).replace('Kanwil DJP', '')) |
||||
dataSeries.push(val.RATA_RATA) |
||||
}) |
||||
return { categories, dataSeries } |
||||
}) |
||||
setUrutKpp(() => { |
||||
const categories = [] |
||||
const dataSeries = [] |
||||
data.urutKpp.map((val, idx) => { |
||||
categories.push(String(val.LABEL).replace('KPP Pratama', '')) |
||||
dataSeries.push(val.RATA_RATA) |
||||
}) |
||||
return { categories, dataSeries } |
||||
}) |
||||
setUrutSeksi(() => { |
||||
const categories = [] |
||||
const dataSeries = [] |
||||
data.urutSeksi.map((val, idx) => { |
||||
categories.push(String(val.LABEL).replace('Seksi', '') + '-' + String(val.NAMA_KANTOR).replace('KPP Pratama', '')) |
||||
dataSeries.push(val.RATA_RATA) |
||||
}) |
||||
return { categories, dataSeries } |
||||
}) |
||||
setUrutPegawai(() => { |
||||
const categories = [] |
||||
const dataSeries = [] |
||||
data.urutPegawai.map((val, idx) => { |
||||
categories.push(String(val.LABEL) + ' - ' + String(val.NAMA_UNIT_ES4).replace('Seksi', '') + ' - ' + String(val.NAMA_KANTOR).replace('KPP Pratama', '')) |
||||
dataSeries.push(val.JUMLAH) |
||||
}) |
||||
return { categories, dataSeries } |
||||
}) |
||||
}, [storeKpdl.dataMonitoring, storeKpdl.dataUrut]) |
||||
|
||||
const columns = useMemo( |
||||
() => [ |
||||
{ |
||||
accessorKey: 'LABEL', |
||||
header: 'Unit/Pegawai', |
||||
size: 200, |
||||
mantineTableBodyCellProps: ({ cell }) => { |
||||
const rowObject = cell.row.original |
||||
if (!cell.row.getCanExpand()) { |
||||
if (rowObject.ISEXIST_INWAS === 'FALSE') { |
||||
return { style: { color: 'red' } } |
||||
} |
||||
if (rowObject.NAMA_JABATAN === 'Kepala Seksi') { |
||||
return { style: { color: 'blue' } } |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH', |
||||
header: 'Jml. Subjek', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID') |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_AR', |
||||
header: 'AR Existing', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID') |
||||
}, |
||||
{ |
||||
accessorKey: 'RATA_RATA', |
||||
header: 'Rata-rata Per AR', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID', { maximumFractionDigits: 2 }), |
||||
sortDescFirst: true |
||||
} |
||||
], |
||||
[] |
||||
) |
||||
|
||||
const optionsChart = (props, title, urutChart) => { |
||||
return { |
||||
chart: { |
||||
type: 'bar', |
||||
height: 350, |
||||
borderColor: '#EBBA95', |
||||
borderWidth: 2 |
||||
}, |
||||
title: { |
||||
text: `${title}`, |
||||
style: { fontSize: '14px' }, |
||||
useHTML: true |
||||
}, |
||||
|
||||
xAxis: { |
||||
categories: props.categories, |
||||
labels: { |
||||
formatter: function () { |
||||
return urutChart === 4 ? String(this.value).split('-')[0] : this.value |
||||
}, |
||||
allowOverlap: false, |
||||
style: { |
||||
whiteSpace: 'wrap', |
||||
overflow: 'allow' |
||||
} |
||||
} |
||||
}, |
||||
yAxis: { |
||||
min: 0, |
||||
title: { |
||||
text: null |
||||
}, |
||||
labels: { |
||||
overflow: 'justify' |
||||
}, |
||||
gridLineWidth: 0 |
||||
}, |
||||
tooltip: { |
||||
valueSuffix: ' Subjek', |
||||
backgroundColor: '#FCFFC5', |
||||
// pointFormat: '{point.y}',
|
||||
formatter: function () { |
||||
return '<b>' + this.x + '</b> : <b>' + Number(this.y).toLocaleString('id-ID', { maximumFractionDigits: 2 }) + '</b>' |
||||
}, |
||||
enabled: true |
||||
}, |
||||
plotOptions: { |
||||
bar: { |
||||
borderRadius: '20%', |
||||
dataLabels: { |
||||
enabled: true, |
||||
formatter: function () { |
||||
return `${Number(this.y).toLocaleString('id-ID', { |
||||
minimumFractionDigits: 0, |
||||
maximumFractionDigits: 2 |
||||
})}` |
||||
} |
||||
}, |
||||
groupPadding: 0.1 |
||||
} |
||||
}, |
||||
credits: { |
||||
enabled: false |
||||
}, |
||||
series: [{ name: 'Subjek KPDL', data: props.dataSeries }] |
||||
} |
||||
} |
||||
return ( |
||||
<> |
||||
<Row style={{ minHeight: '200px' }}> |
||||
<Col md="8"> |
||||
<MantineReactTable |
||||
initialState={{ sorting: [{ id: 'RATA_RATA', desc: true }] }} |
||||
columns={columns} |
||||
data={storeKpdl.dataMonitoring} |
||||
enableExpanding |
||||
enableExpandAll //default
|
||||
mantineTopToolbarProps={{ className: 'z-0', allowFullScreen: false }} |
||||
enableFullScreenToggle={false} |
||||
enablePagination={false} |
||||
mantineTableBodyCellProps={({ cell }) => ({ |
||||
className: 'text-xs py-1', |
||||
sx: { |
||||
backgroundColor: cell.row.depth === 1 || !cell.row.getCanExpand() ? 'ButtonHighlight' : 'inherit', |
||||
fontWeight: cell.row.depth === 0 ? '600' : 'inherit' |
||||
} |
||||
})} |
||||
mantineTableBodyProps={{ className: 'mb-3' }} |
||||
mantineTableHeadCellProps={{ align: 'center', className: 'text-xs p-1' }} |
||||
enableTableFooter |
||||
renderBottomToolbar={false} |
||||
mantineTableProps={{ |
||||
highlightOnHover: false, |
||||
withColumnBorders: true |
||||
}} |
||||
content |
||||
/> |
||||
</Col> |
||||
<Col md="4"> |
||||
<Row> |
||||
{urutKanwil.dataSeries.length ? ( |
||||
<Col md="12" className="m-1"> |
||||
<HighchartsReact ref={refChart1} highcharts={Highcharts} options={optionsChart(urutKanwil, 'Urut Rata-rata Kanwil', 1)} /> |
||||
</Col> |
||||
) : null} |
||||
{urutKpp.dataSeries.length ? ( |
||||
<Col md="12" className="m-1"> |
||||
<HighchartsReact ref={refChart2} highcharts={Highcharts} options={optionsChart(urutKpp, 'Urut Rata-rata KPP', 2)} /> |
||||
</Col> |
||||
) : null} |
||||
{urutKpp.dataSeries.length ? ( |
||||
<Col md="12" className="m-1"> |
||||
<HighchartsReact ref={refChart3} highcharts={Highcharts} options={optionsChart(urutSeksi, 'Urut Rata-rata Seksi', 3)} /> |
||||
</Col> |
||||
) : null} |
||||
{urutKpp.dataSeries.length ? ( |
||||
<Col md="12" className="m-1"> |
||||
<HighchartsReact ref={refChart3} highcharts={Highcharts} options={optionsChart(urutPegawai, 'Jml. Subjek Urut AR', 4)} /> |
||||
</Col> |
||||
) : null} |
||||
</Row> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Kanwil |
@ -0,0 +1,138 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Card, CardBody, CardHeader, CardTitle, Col, Label, Row } from 'reactstrap' |
||||
import collect from 'collect.js' |
||||
import { Toast } from 'primereact/toast' |
||||
import 'primereact/resources/themes/bootstrap4-light-blue/theme.css' |
||||
import 'primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import $ from 'jquery' |
||||
import { isObjEmpty, titleCase } from '../../kytp/util' |
||||
import { useSelector } from 'react-redux' |
||||
const date = new Date() |
||||
|
||||
const Kpp = () => { |
||||
const base_url = '<?=base_url()?>' |
||||
const toast = useRef(null) |
||||
const [isLoading, setIsLoading] = useState() |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
|
||||
const [data, setData] = useState([]) |
||||
const [sorting, setSorting] = useState([{ id: 'RATA_RATA', desc: true }]) |
||||
|
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
useEffect(() => { |
||||
if (data.length) { |
||||
try { |
||||
//scroll to the top of the table when the sorting changes
|
||||
rowVirtualizerInstanceRef.current?.scrollToIndex(0) |
||||
} catch (e) { |
||||
// console.log(e)
|
||||
} |
||||
} |
||||
}, [sorting]) |
||||
|
||||
useEffect(() => { |
||||
const sharedData = storeKpdl.dataMonitoring |
||||
const data = [] |
||||
sharedData.map((val, _idx) => { |
||||
val.subRows.map((val2, _idx2) => { |
||||
data.push(val2) |
||||
}) |
||||
}) |
||||
setData(data) |
||||
setIsLoading(false) |
||||
}, [storeKpdl.dataMonitoring]) |
||||
|
||||
const columns = useMemo( |
||||
() => [ |
||||
{ |
||||
accessorKey: 'LABEL', |
||||
header: 'Unit/Pegawai', |
||||
size: 200, |
||||
mantineTableBodyCellProps: ({ cell }) => { |
||||
const rowObject = cell.row.original |
||||
if (!cell.row.getCanExpand()) { |
||||
if (rowObject.ISEXIST_INWAS === 'FALSE') { |
||||
return { style: { color: 'red' } } |
||||
} |
||||
if (rowObject.NAMA_JABATAN === 'Kepala Seksi') { |
||||
return { style: { color: 'blue' } } |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH', |
||||
header: 'Jml. Subjek', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID') |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_AR', |
||||
header: 'AR Existing', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID') |
||||
}, |
||||
{ |
||||
accessorKey: 'RATA_RATA', |
||||
header: 'Rata-rata Per AR', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID', { maximumFractionDigits: 2 }) |
||||
} |
||||
], |
||||
[] |
||||
) |
||||
const table = useMantineReactTable({ |
||||
columns, |
||||
data, //10,000 rows
|
||||
enableBottomToolbar: false, |
||||
enableGlobalFilterModes: true, |
||||
enablePagination: false, |
||||
enableRowVirtualization: true, |
||||
mantineTableContainerProps: { sx: { maxHeight: '600px' }, className: 'p-2' }, |
||||
onSortingChange: setSorting, |
||||
state: { isLoading, sorting }, |
||||
rowVirtualizerProps: { overscan: 8 }, //optionally customize the virtualizer
|
||||
enableExpanding: true, |
||||
mantineTopToolbarProps: { allowFullScreen: false }, |
||||
enableFullScreenToggle: false, |
||||
enablePagination: false, |
||||
mantineTableBodyCellProps: ({ cell }) => ({ |
||||
className: 'text-xs py-1', |
||||
sx: { |
||||
backgroundColor: cell.row.depth === 1 ? 'ButtonHighlight' : 'inherit', |
||||
fontWeight: cell.row.depth === 0 ? '600' : 'inherit' |
||||
} |
||||
}), |
||||
mantineTableBodyProps: { className: 'mb-3' }, |
||||
mantineTableHeadCellProps: { align: 'center', className: 'text-xs p-1' }, |
||||
renderBottomToolbar: false, |
||||
mantineTableProps: { |
||||
highlightOnHover: false, |
||||
withColumnBorders: true |
||||
}, |
||||
rowVirtualizerInstanceRef |
||||
}) |
||||
return ( |
||||
<> |
||||
<Row style={{ minHeight: '200px' }}> |
||||
<Col> |
||||
<MantineReactTable table={table} /> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Kpp |
@ -0,0 +1,134 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Card, CardBody, CardHeader, CardTitle, Col, Label, Row } from 'reactstrap' |
||||
import collect from 'collect.js' |
||||
import { Toast } from 'primereact/toast' |
||||
import 'primereact/resources/themes/bootstrap4-light-blue/theme.css' |
||||
import 'primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import $ from 'jquery' |
||||
import { isObjEmpty, titleCase } from '../../kytp/util' |
||||
import { useSelector } from 'react-redux' |
||||
const date = new Date() |
||||
|
||||
const Pegawai = () => { |
||||
const base_url = '<?=base_url()?>' |
||||
const toast = useRef(null) |
||||
|
||||
const refChart1 = useRef() |
||||
const refChart2 = useRef() |
||||
const refChart3 = useRef() |
||||
// const refChart4 = useRef()
|
||||
|
||||
const [data, setData] = useState([]) |
||||
const [isLoading, setIsLoading] = useState() |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [sorting, setSorting] = useState([{ id: 'JUMLAH', desc: true }]) |
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
useEffect(() => { |
||||
try { |
||||
//scroll to the top of the table when the sorting changes
|
||||
rowVirtualizerInstanceRef.current?.scrollToIndex(0) |
||||
} catch (e) { |
||||
// console.log(e)
|
||||
} |
||||
}, [sorting]) |
||||
|
||||
useEffect(() => { |
||||
const sharedData = storeKpdl.dataMonitoring |
||||
const data = [] |
||||
sharedData.map((val, _idx) => { |
||||
val.subRows.map((val2, _idx2) => { |
||||
val2.subRows.map((val3, _idx3) => { |
||||
data.push(...val3.subRows) |
||||
}) |
||||
}) |
||||
}) |
||||
setData(data) |
||||
setIsLoading(false) |
||||
}, [storeKpdl.dataMonitoring]) |
||||
|
||||
const columns = useMemo( |
||||
() => [ |
||||
{ |
||||
accessorKey: 'LABEL', |
||||
header: 'Unit/Pegawai', |
||||
size: 200, |
||||
Cell: ({ cell }) => { |
||||
const rowData = cell.row.original |
||||
return cell.getValue() + ' - ' + rowData.NAMA_KANTOR + ' - ' + rowData.NAMA_UNIT_ES4 |
||||
}, |
||||
mantineTableBodyCellProps: ({ cell }) => { |
||||
const rowObject = cell.row.original |
||||
if (!cell.row.getCanExpand()) { |
||||
if (rowObject.ISEXIST_INWAS === 'FALSE') { |
||||
return { style: { color: 'red' } } |
||||
} |
||||
if (rowObject.NAMA_JABATAN === 'Kepala Seksi') { |
||||
return { style: { color: 'blue' } } |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH', |
||||
header: 'Jml. Subjek', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID') |
||||
} |
||||
], |
||||
[] |
||||
) |
||||
const table = useMantineReactTable({ |
||||
columns, |
||||
data, //10,000 rows
|
||||
enableBottomToolbar: false, |
||||
enableGlobalFilterModes: true, |
||||
enableRowVirtualization: true, |
||||
mantineTableContainerProps: { sx: { maxHeight: '600px' } }, |
||||
onSortingChange: setSorting, |
||||
state: { isLoading, sorting }, |
||||
rowVirtualizerProps: { overscan: 8 }, //optionally customize the virtualizer
|
||||
mantineTopToolbarProps: { allowFullScreen: false }, |
||||
enableFullScreenToggle: false, |
||||
enablePagination: false, |
||||
mantineTableBodyCellProps: ({ cell }) => ({ |
||||
className: 'text-xs py-1', |
||||
sx: { |
||||
fontWeight: cell.row.depth === 0 ? '600' : 'inherit' |
||||
} |
||||
}), |
||||
mantineTableBodyProps: { className: 'mb-3' }, |
||||
mantineTableHeadCellProps: { align: 'center', className: 'text-xs p-1' }, |
||||
renderBottomToolbar: false, |
||||
mantineTableProps: { |
||||
highlightOnHover: false, |
||||
withColumnBorders: true |
||||
}, |
||||
rowVirtualizerInstanceRef, |
||||
enableRowNumbers: true, |
||||
displayColumnDefOptions: { |
||||
'mrt-row-numbers': { |
||||
mantineTableBodyCellProps: { |
||||
align: 'center' |
||||
}, |
||||
size: 80 |
||||
} |
||||
} |
||||
}) |
||||
return ( |
||||
<> |
||||
<Row style={{ minHeight: '200px' }}> |
||||
<Col> |
||||
<MantineReactTable table={table} /> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Pegawai |
@ -0,0 +1,145 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
import Highcharts from 'highcharts' |
||||
import HighchartsReact from 'highcharts-react-official' |
||||
import { Card, CardBody, CardHeader, CardTitle, Col, Label, Row } from 'reactstrap' |
||||
import collect from 'collect.js' |
||||
import { Toast } from 'primereact/toast' |
||||
import 'primereact/resources/themes/bootstrap4-light-blue/theme.css' |
||||
import 'primeflex/primeflex.css' |
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table' |
||||
import $ from 'jquery' |
||||
import { isObjEmpty, titleCase } from '../../kytp/util' |
||||
import { useSelector } from 'react-redux' |
||||
const date = new Date() |
||||
|
||||
const Seksi = () => { |
||||
const base_url = '<?=base_url()?>' |
||||
const toast = useRef(null) |
||||
|
||||
const refChart1 = useRef() |
||||
const refChart2 = useRef() |
||||
const refChart3 = useRef() |
||||
// const refChart4 = useRef()
|
||||
|
||||
const [data, setData] = useState([]) |
||||
const [isLoading, setIsLoading] = useState() |
||||
const rowVirtualizerInstanceRef = useRef(null) |
||||
const [sorting, setSorting] = useState([{ id: 'RATA_RATA', desc: true }]) |
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
useEffect(() => { |
||||
try { |
||||
//scroll to the top of the table when the sorting changes
|
||||
rowVirtualizerInstanceRef.current?.scrollToIndex(0) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
}, [sorting]) |
||||
|
||||
useEffect(() => { |
||||
const sharedData = storeKpdl.dataMonitoring |
||||
const data = [] |
||||
sharedData.map((val, _idx) => { |
||||
val.subRows.map((val2, _idx2) => { |
||||
val2.subRows.map((val3, _idx3) => { |
||||
data.push(val3) |
||||
}) |
||||
}) |
||||
}) |
||||
setData(data) |
||||
setIsLoading(false) |
||||
}, [storeKpdl.dataMonitoring]) |
||||
|
||||
const columns = useMemo( |
||||
() => [ |
||||
{ |
||||
accessorKey: 'LABEL', |
||||
header: 'Unit/Pegawai', |
||||
size: 200, |
||||
Cell: ({ cell }) => { |
||||
const rowData = cell.row.original |
||||
return cell.getValue() + ' - ' + rowData.NAMA_KANTOR |
||||
}, |
||||
mantineTableBodyCellProps: ({ cell }) => { |
||||
const rowObject = cell.row.original |
||||
if (!cell.row.getCanExpand()) { |
||||
if (rowObject.ISEXIST_INWAS === 'FALSE') { |
||||
return { style: { color: 'red' } } |
||||
} |
||||
if (rowObject.NAMA_JABATAN === 'Kepala Seksi') { |
||||
return { style: { color: 'blue' } } |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH', |
||||
header: 'Jml. Subjek', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID') |
||||
}, |
||||
{ |
||||
accessorKey: 'JUMLAH_AR', |
||||
header: 'AR Existing', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID') |
||||
}, |
||||
{ |
||||
accessorKey: 'RATA_RATA', |
||||
header: 'Rata-rata Per AR', |
||||
size: 80, |
||||
mantineTableBodyCellProps: { |
||||
align: 'right' |
||||
}, |
||||
Cell: ({ cell }) => Number(cell.getValue()).toLocaleString('id-ID', { maximumFractionDigits: 2 }) |
||||
} |
||||
], |
||||
[] |
||||
) |
||||
const table = useMantineReactTable({ |
||||
columns, |
||||
data, //10,000 rows
|
||||
enableBottomToolbar: false, |
||||
enableGlobalFilterModes: true, |
||||
enableRowVirtualization: true, |
||||
mantineTableContainerProps: { sx: { maxHeight: '600px' } }, |
||||
onSortingChange: setSorting, |
||||
state: { isLoading, sorting }, |
||||
rowVirtualizerProps: { overscan: 8 }, //optionally customize the virtualizer
|
||||
enableExpanding: true, |
||||
mantineTopToolbarProps: { allowFullScreen: false }, |
||||
enableFullScreenToggle: false, |
||||
enablePagination: false, |
||||
mantineTableBodyCellProps: ({ cell }) => ({ |
||||
className: 'text-xs py-1', |
||||
sx: { |
||||
backgroundColor: cell.row.depth === 1 || !cell.row.getCanExpand() ? 'ButtonHighlight' : 'inherit', |
||||
fontWeight: cell.row.depth === 0 ? '600' : 'inherit' |
||||
} |
||||
}), |
||||
mantineTableBodyProps: { className: 'mb-3' }, |
||||
mantineTableHeadCellProps: { align: 'center', className: 'text-xs p-1' }, |
||||
renderBottomToolbar: false, |
||||
mantineTableProps: { |
||||
highlightOnHover: false, |
||||
withColumnBorders: true |
||||
}, |
||||
rowVirtualizerInstanceRef |
||||
}) |
||||
return ( |
||||
<> |
||||
<Row style={{ minHeight: '200px' }}> |
||||
<Col> |
||||
<MantineReactTable table={table} /> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Seksi |
@ -0,0 +1,221 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
import ReactDOM from 'react-dom' |
||||
import { store } from '../kytp/store/store' |
||||
import { Provider, useDispatch, useSelector } from 'react-redux' |
||||
import { Card, CardBody, CardFooter, CardHeader, CardSubtitle, CardTitle, Col, Label, Nav, NavItem, NavLink, Row, TabContent, TabPane } from 'reactstrap' |
||||
import collect from 'collect.js' |
||||
import { MultiSelect } from 'react-multi-select-component' |
||||
import { Toast } from 'primereact/toast' |
||||
import { Button as ButtonP } from 'primereact/button' |
||||
import 'primereact/resources/themes/bootstrap4-light-blue/theme.css' |
||||
import 'primeflex/primeflex.css' |
||||
import Tanggal from '../commons/Tanggal' |
||||
import $ from 'jquery' |
||||
import Kanwil from './componentMonitoring/Kanwil' |
||||
import Kpp from './componentMonitoring/Kpp' |
||||
import Seksi from './componentMonitoring/Seksi' |
||||
import { TabPanel, TabView } from 'primereact/tabview' |
||||
import Pegawai from './componentMonitoring/Pegawai' |
||||
import { setDataMonitoring, setDataUrut } from '../kytp/store/KpdlStore' |
||||
import { Skeleton } from 'primereact/skeleton' |
||||
const date = new Date() |
||||
|
||||
const App = () => { |
||||
const base_url = '<?=base_url()?>' |
||||
const toast = useRef(null) |
||||
const [active, setActive] = useState(0) |
||||
const refTab = useRef() |
||||
const toggle = (tab) => { |
||||
setActive(tab) |
||||
} |
||||
const [kanwil, setKanwil] = useState([]) |
||||
const [kanwilSelected, setKanwilSelected] = useState([]) |
||||
const [kpp, setKpp] = useState([]) |
||||
const [kppSelected, setKppSelected] = useState([]) |
||||
const [tanggalAwal, setTanggalAwal] = useState(new Date(date.getFullYear(), 0, 1).toISOString()) |
||||
const [tanggalAkhir, setTanggalAkhir] = useState(date.toISOString()) |
||||
const [paramSelected, setParamSelected] = useState({}) |
||||
const [loading, setLoading] = useState(false) |
||||
const storeKpdl = useSelector((state) => state.kpdl) |
||||
const dispatch = useDispatch() |
||||
|
||||
useEffect(() => { |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kanwil', |
||||
method: 'GET', |
||||
dataType: 'json', |
||||
success: (data) => { |
||||
setKanwil(data) |
||||
} |
||||
}) |
||||
}, []) |
||||
|
||||
useEffect(() => { |
||||
const kanwil = collect(kanwilSelected).pluck('value').all() |
||||
setKppSelected([]) |
||||
setKpp([]) |
||||
|
||||
if (!kanwil.length) { |
||||
return |
||||
} |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/ref/kppmultikanwil', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { |
||||
kanwil |
||||
}, |
||||
success: (data) => { |
||||
setKpp(data) |
||||
} |
||||
}) |
||||
}, [kanwilSelected]) |
||||
|
||||
const buttonProsesOnClick = () => { |
||||
if (!kppSelected.length) { |
||||
return toast.current.show({ severity: 'info', summary: 'Info', detail: 'KPP harus dipilih' }) |
||||
} |
||||
|
||||
if (!tanggalAwal) { |
||||
return toast.current.show({ severity: 'info', summary: 'Info', detail: 'Periode Awal harus dipilih' }) |
||||
} |
||||
if (!tanggalAkhir) { |
||||
return toast.current.show({ severity: 'info', summary: 'Info', detail: 'Periode Akhir harus dipilih' }) |
||||
} |
||||
setActive(0) |
||||
setParamSelected({ |
||||
kanwilSelected, |
||||
kppSelected, |
||||
tanggalAwal, |
||||
tanggalAkhir |
||||
}) |
||||
|
||||
const kanwil = collect(kanwilSelected).pluck('value').all() |
||||
const kpp = collect(kppSelected).pluck('value').all() |
||||
setLoading(true) |
||||
$.ajax({ |
||||
url: base_url + 'kewilayahan/monitoring/data', |
||||
method: 'POST', |
||||
dataType: 'json', |
||||
data: { |
||||
kanwil, |
||||
kpp, |
||||
tanggalAwal, |
||||
tanggalAkhir |
||||
}, |
||||
success: (dataReturn) => { |
||||
dispatch(setDataMonitoring(dataReturn.data)) |
||||
dispatch( |
||||
setDataUrut({ urutKanwil: dataReturn.urutKanwil, urutKpp: dataReturn.urutKpp, urutSeksi: dataReturn.urutSeksi, urutPegawai: dataReturn.urutPegawai }) |
||||
) |
||||
} |
||||
}).done(() => { |
||||
setLoading(false) |
||||
}) |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
<Card> |
||||
<CardHeader className="pb-1"> |
||||
<CardTitle tag={'h5'}>Pilih Unit Kerja Perekam dan Tanggal Kegiatan</CardTitle> |
||||
</CardHeader> |
||||
<CardBody className="pt-1"> |
||||
<Row className="mt-2"> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih Kanwil"> |
||||
Kanwil |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full multi-select" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={kanwil} |
||||
value={kanwilSelected} |
||||
onChange={(e) => { |
||||
setKanwilSelected(e) |
||||
}} |
||||
labelledBy="Pilih Kanwil" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih Kanwil' }} |
||||
/> |
||||
</Col> |
||||
<Col md="3"> |
||||
<Label className="form-label" for="Pilih KPP"> |
||||
KPP |
||||
</Label> |
||||
<MultiSelect |
||||
className="me-1 w-full multi-select" |
||||
hasSelectAll={true} |
||||
debounceDuration={300} |
||||
options={kpp} |
||||
value={kppSelected} |
||||
onChange={(e) => { |
||||
setKppSelected(e) |
||||
}} |
||||
labelledBy="Pilih KPP" |
||||
overrideStrings={{ allItemsAreSelected: 'Semua dipilih', selectSomeItems: 'Pilih KPP' }} |
||||
/> |
||||
</Col> |
||||
<Col md="2"> |
||||
<Tanggal setValue={setTanggalAwal} value={tanggalAwal} startOrEnd={'Periode Awal'} /> |
||||
</Col> |
||||
<Col md="2"> |
||||
<Tanggal setValue={setTanggalAkhir} value={tanggalAkhir} startOrEnd={'Periode Akhir'} /> |
||||
</Col> |
||||
<Col md="2" className="pt-4"> |
||||
<ButtonP onClick={() => buttonProsesOnClick()} label="Proses" severity="" rounded className="w-10rem text-white text-base" /> |
||||
</Col> |
||||
</Row> |
||||
</CardBody> |
||||
</Card> |
||||
<Card> |
||||
<CardBody className="pb-1"> |
||||
<CardTitle tag={'h5'}>Monitoring Kegiatan KPDL Seksi Pengawasan</CardTitle> |
||||
<CardSubtitle className="pb-0 mb-0 text-red-400">Sesuai dengan lingkup pilihan pencarian</CardSubtitle> |
||||
</CardBody> |
||||
<CardBody className="pt-1"> |
||||
{loading ? ( |
||||
<Skeleton className="" shape="rectangle" height="17rem" width="100%"></Skeleton> |
||||
) : ( |
||||
<TabView activeIndex={active} scrollable onTabChange={(e) => setActive(e.index)} className="p-0" pt={{ panelContainer: { className: 'p-0' } }}> |
||||
<TabPanel id="tab_1" header="Kanwil" className="p-0"> |
||||
<Kanwil /> |
||||
</TabPanel> |
||||
<TabPanel id="tab_2" header="KPP" className="p-0"> |
||||
<Kpp /> |
||||
</TabPanel> |
||||
<TabPanel id="tab_3" header="Seksi" className="p-0"> |
||||
<Seksi /> |
||||
</TabPanel> |
||||
<TabPanel id="tab_4" header="Pegawai" className="p-0"> |
||||
<Pegawai /> |
||||
</TabPanel> |
||||
</TabView> |
||||
)} |
||||
</CardBody> |
||||
<CardFooter> |
||||
<div> |
||||
<Label>Legenda Tabel :</Label> |
||||
<ul> |
||||
<li style={{ color: 'blue' }}>Kepala Seksi</li> |
||||
<li style={{ color: 'red' }}>Eks Kepala/Anggota Seksi terkait</li> |
||||
</ul> |
||||
</div> |
||||
</CardFooter> |
||||
</Card> |
||||
|
||||
<Row> |
||||
<Toast ref={toast} /> |
||||
</Row> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
const container = document.getElementById('app') |
||||
const component = ( |
||||
<Provider store={store}> |
||||
<App /> |
||||
</Provider> |
||||
) |
||||
|
||||
ReactDOM.render(component, container) |
@ -0,0 +1,40 @@
|
||||
<link rel="stylesheet" href="<?=base_url('public/kpdl/dist/monitoring.css')?>">
|
||||
|
||||
<style> |
||||
.highcharts-credits{ |
||||
visibility: hidden; |
||||
} |
||||
.tanggal { |
||||
min-height: 40px; |
||||
} |
||||
.multi-select { |
||||
.dropdown-content{ |
||||
z-index: 5 !important; |
||||
} |
||||
} |
||||
.mantine-TableHeadCell-Content-Labels { |
||||
padding-left: 0; |
||||
} |
||||
</style> |
||||
|
||||
<?php |
||||
helper('Kpdl'); |
||||
?> |
||||
|
||||
<div class="main-content"> |
||||
<div class="container-fluid"> |
||||
<div id="app"></div> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
|
||||
<?php $isDevelopment = ENVIRONMENT === 'development';?> |
||||
|
||||
<script type="text/javascript"> |
||||
|
||||
<?php echo view('kewilayahan/dist/monitoring.js') ?> |
||||
</script> |
||||
|
||||
<?php echo view('inc/js.php') ?> |
@ -0,0 +1,21 @@
|
||||
@import "../../kewilayahan/scss/base/bootstrap-extended/include"; // Bootstrap includes |
||||
@import "../../kewilayahan/scss/base/components/include"; // Components includes |
||||
.map-leaflet { |
||||
|
||||
width: 100%; |
||||
height: 100%; |
||||
z-index: 3; |
||||
|
||||
} |
||||
.leaflet-container { |
||||
width: 100%; |
||||
height: calc(100vh); |
||||
font-family: inherit !important; |
||||
|
||||
} |
||||
.leaflet-control-layers-base > label { |
||||
font-size: larger; |
||||
cursor: pointer; |
||||
/* color: $primary; */ |
||||
font-weight: bold; |
||||
} |
@ -0,0 +1,25 @@
|
||||
/* eslint-disable implicit-arrow-linebreak */ |
||||
/* eslint-disable no-unused-vars */ |
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
|
||||
import L from 'leaflet' |
||||
import './L.TileLayer.GeneralWMS' |
||||
|
||||
import { createTileLayerComponent, createElementObject, updateGridLayer } from '@react-leaflet/core' |
||||
|
||||
const GeneralWMS = createTileLayerComponent( |
||||
function createBetterWMSLayer({ options, url, layers, ...props }, context) { |
||||
const layer = new L.tileLayer.generalWms(url, { layers, ...props }, context) |
||||
|
||||
return createElementObject(layer, context) |
||||
}, |
||||
(layer, props, prevProps) => { |
||||
updateGridLayer(layer, props, prevProps) |
||||
|
||||
if (props.params !== null && props.params !== prevProps.params) { |
||||
layer.setParams(props.params) |
||||
} |
||||
} |
||||
) |
||||
|
||||
export default GeneralWMS |
@ -0,0 +1,88 @@
|
||||
/* eslint-disable no-unused-vars */ |
||||
// Credits to https://gist.github.com/rclark/6908938
|
||||
|
||||
L.TileLayer.GeneralWMS = L.TileLayer.WMS.extend({ |
||||
onAdd(map) { |
||||
// Triggered when the layer is added to a map.
|
||||
// Register a click listener, then do all the upstream WMS things
|
||||
L.TileLayer.WMS.prototype.onAdd.call(this, map) |
||||
map.on('click', this.getFeatureInfo, this) |
||||
map.on('move', () => console.log(this)) |
||||
console.log(this) |
||||
}, |
||||
onRemove(map) { |
||||
// Triggered when the layer is removed from a map.
|
||||
// Unregister a click listener, then do all the upstream WMS things
|
||||
L.TileLayer.WMS.prototype.onRemove.call(this, map) |
||||
map.off('click', this.getFeatureInfo, this) |
||||
}, |
||||
async getFeatureInfo(evt) { |
||||
// Make an AJAX request to the server and hope for the best
|
||||
const url = this.getFeatureInfoUrl(evt.latlng), |
||||
showResults = L.Util.bind(this.showGetFeatureInfo, this) |
||||
|
||||
await fetch(url) |
||||
.then((response) => response.json()) |
||||
.then((data) => { |
||||
// console.log(data)
|
||||
const err = typeof data === 'object' ? null : data |
||||
const numberReturned = data?.numberReturned |
||||
|
||||
if (numberReturned === 1) { |
||||
// showResults(err, evt.latlng, JSON.stringify(data))
|
||||
showResults(err, evt.latlng, data.features[0].properties) |
||||
// const id_md = data.features[0].properties.i
|
||||
} |
||||
if (numberReturned > 1) { |
||||
showList(err, evt.latlng, data) |
||||
} |
||||
}) |
||||
.catch((err) => { |
||||
// showResults(JSON.stringify(err))
|
||||
}) |
||||
}, |
||||
|
||||
getFeatureInfoUrl(latlng) { |
||||
// return
|
||||
// const map = useMap()
|
||||
// Construct a GetFeatureInfo request URL given a point
|
||||
const point = this._map.latLngToContainerPoint(latlng) |
||||
const size = this._map.getSize() |
||||
|
||||
// https://docs.geoserver.org/latest/en/user/services/wms/reference.html#wms-getfeatureinfo
|
||||
const params = { |
||||
request: 'GetFeatureInfo', |
||||
service: 'WMS', |
||||
crs: 'EPSG:4326', |
||||
styles: this.wmsParams.styles, |
||||
transparent: this.wmsParams.transparent, |
||||
version: this.wmsParams.version, |
||||
format: this.wmsParams.format, |
||||
bbox: this._map.getBounds().toBBoxString(), |
||||
height: size.y, |
||||
// height: this.wmsParams.height,
|
||||
width: size.x, |
||||
// width: this.wmsParams.width,
|
||||
layers: this.wmsParams.layers, |
||||
viewparams: this.wmsParams.viewparams, |
||||
cql_filter: this.wmsParams.cql_filter, |
||||
query_layers: this.wmsParams.layers, |
||||
info_format: 'application/json', |
||||
feature_count: 1 |
||||
} |
||||
|
||||
params[params.version === '1.3.0' ? 'i' : 'x'] = Math.round(point.x) |
||||
params[params.version === '1.3.0' ? 'j' : 'y'] = Math.round(point.y) |
||||
return this._url + L.Util.getParamString(params, this._url, true) |
||||
}, |
||||
|
||||
showGetFeatureInfo(err, latlng, content) { |
||||
if (err) { |
||||
console.log(err) |
||||
} |
||||
} |
||||
}) |
||||
|
||||
L.tileLayer.generalWms = function (url, options) { |
||||
return new L.TileLayer.GeneralWMS(url, options) |
||||
} |
@ -0,0 +1,89 @@
|
||||
/* eslint-disable no-unused-vars */ |
||||
// Credits to https://gist.github.com/rclark/6908938
|
||||
import L from 'leaflet' |
||||
import 'leaflet.vectorgrid' |
||||
// import { getParamString } from 'leaflet/src/core/Util'
|
||||
import { toBounds } from 'leaflet/src/geometry/Bounds' |
||||
// import { TileLayer } from 'leaflet/src/layer/tile/TileLayer'
|
||||
import Pbf from 'pbf' |
||||
import { VectorTile } from 'vector-tile' |
||||
|
||||
L.VectorGrid.VectorPoi = L.VectorGrid.Protobuf.extend({ |
||||
_getVectorTilePromise: function (coords) { |
||||
var data = { |
||||
s: this._getSubdomain(coords), |
||||
x: coords.x, |
||||
y: coords.y, |
||||
z: coords.z |
||||
// z: this._getZoomForUrl() /// TODO: Maybe replicate TileLayer's maxNativeZoom
|
||||
} |
||||
if (this._map && !this._map.options.crs.infinite) { |
||||
var invertedY = this._globalTileRange.max.y - coords.y |
||||
if (this.options.tms) { |
||||
// Should this option be available in Leaflet.VectorGrid?
|
||||
data['y'] = invertedY |
||||
} |
||||
data['-y'] = invertedY |
||||
} |
||||
|
||||
var tileBounds = this._tileCoordsToNwSe(coords), |
||||
// crs = this._crs,
|
||||
crs = this._map.options.crs, |
||||
bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])), |
||||
min = bounds.min, |
||||
max = bounds.max, |
||||
bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ? [min.y, min.x, max.y, max.x] : [min.x, min.y, max.x, max.y]).join(',') |
||||
// url = TileLayer.prototype.getTileUrl.call(this, coords)
|
||||
|
||||
var tileUrl = this._url + '&bbox=' + bbox |
||||
// var tileUrl = L.Util.template(this._url, L.extend(data, this.options))
|
||||
return fetch(tileUrl, this.options.fetchOptions) |
||||
.then(function (response) { |
||||
if (!response.ok) { |
||||
return { layers: [] } |
||||
} |
||||
|
||||
return response.blob().then(function (blob) { |
||||
// console.log(blob);
|
||||
|
||||
var reader = new FileReader() |
||||
return new Promise(function (resolve) { |
||||
reader.addEventListener('loadend', function () { |
||||
// reader.result contains the contents of blob as a typed array
|
||||
|
||||
// blob.type === 'application/x-protobuf'
|
||||
var pbf = new Pbf(reader.result) |
||||
console.log(pbf) |
||||
return resolve(new VectorTile(pbf)) |
||||
}) |
||||
reader.readAsArrayBuffer(blob) |
||||
}) |
||||
}) |
||||
}) |
||||
.then(function (json) { |
||||
// console.log('Vector tile:', json.layers)
|
||||
// console.log('Vector tile water:', json.layers.water); // Instance of VectorTileLayer
|
||||
|
||||
// Normalize feature getters into actual instanced features
|
||||
for (var layerName in json.layers) { |
||||
var feats = [] |
||||
|
||||
for (var i = 0; i < json.layers[layerName].length; i++) { |
||||
var feat = json.layers[layerName].feature(i) |
||||
feat.geometry = feat.loadGeometry() |
||||
feat.latlng = feat.loadGeometry() |
||||
console.log(feat) |
||||
feats.push(feat) |
||||
} |
||||
|
||||
json.layers[layerName].features = feats |
||||
} |
||||
|
||||
return json |
||||
}) |
||||
} |
||||
}) |
||||
|
||||
L.vectorGrid.vectorpoi = function (url, options) { |
||||
return new L.VectorGrid.VectorPoi(url, options) |
||||
} |
@ -0,0 +1,238 @@
|
||||
import { useEffect, useRef } from 'react' |
||||
import { createTileLayerComponent, updateGridLayer } from '@react-leaflet/core' |
||||
import L from 'leaflet' |
||||
import isObject from 'lodash/isObject' |
||||
import isFunction from 'lodash/isFunction' |
||||
import isString from 'lodash/isString' |
||||
import isEmpty from 'lodash/isEmpty' |
||||
import clone from 'lodash/clone' |
||||
import cloneDeep from 'lodash/cloneDeep' |
||||
import extend from 'lodash/extend' |
||||
import merge from 'lodash/merge' |
||||
import has from 'lodash/has' |
||||
import find from 'lodash/find' |
||||
import 'leaflet.vectorgrid' |
||||
import './L.VectorGrid.VectorPoi' |
||||
export const VectorGridd = createTileLayerComponent( |
||||
function createTileLayer(props, context) { |
||||
const highlight = useRef(null) |
||||
const active = useRef(null) |
||||
const { |
||||
data, |
||||
style, |
||||
hoverStyle, |
||||
activeStyle, |
||||
onClick, |
||||
onMouseover, |
||||
onMouseout, |
||||
onDblclick, |
||||
onContextmenu, |
||||
vectorTileLayerStyles, |
||||
url, |
||||
maxNativeZoom, |
||||
subdomains, |
||||
accessKey, |
||||
accessToken, |
||||
type = 'protobuf', |
||||
interactive = true, |
||||
idField = '', |
||||
...rest |
||||
} = props |
||||
delete rest.leaflet |
||||
|
||||
useEffect(() => { |
||||
const { tooltipClassName = '', tooltip = null, popup = null } = props |
||||
if (tooltip) { |
||||
vectorGrid.bindTooltip( |
||||
(layer) => { |
||||
if (isFunction(tooltip)) { |
||||
return tooltip(layer) |
||||
} else if (isString(tooltip) && has(layer.properties, tooltip)) { |
||||
return String(layer.properties[tooltip]) |
||||
} else if (isString(tooltip)) { |
||||
return tooltip |
||||
} |
||||
return '' |
||||
}, |
||||
{ |
||||
sticky: true, |
||||
direction: 'auto', |
||||
className: tooltipClassName |
||||
} |
||||
) |
||||
} |
||||
if (popup) { |
||||
vectorGrid.bindPopup(popup) |
||||
} |
||||
}, [props.tooltip, props.popup]) |
||||
|
||||
const baseStyle = (properties, zoom) => { |
||||
if (isFunction(style)) { |
||||
return style(properties) |
||||
} else if (isObject(style)) { |
||||
return style |
||||
} |
||||
return { |
||||
weight: 0.5, |
||||
opacity: 1, |
||||
color: '#ccc', |
||||
fillColor: '#390870', |
||||
fillOpacity: 0.6, |
||||
fill: true, |
||||
stroke: true |
||||
} |
||||
} |
||||
|
||||
const _getFeatureId = (feature) => { |
||||
const { idField } = props |
||||
if (isFunction(idField)) { |
||||
return idField(feature) |
||||
} else if (isString(idField)) { |
||||
return feature.properties[idField] |
||||
} |
||||
} |
||||
|
||||
const setFeatureStyle = (id, style) => { |
||||
vectorGrid.setFeatureStyle(id, style) |
||||
} |
||||
|
||||
const resetFeatureStyle = (id) => { |
||||
vectorGrid.resetFeatureStyle(id) |
||||
} |
||||
|
||||
const clearHighlight = (properties) => { |
||||
if (highlight.current) { |
||||
if (highlight.current !== active.current) { |
||||
resetFeatureStyle(highlight.current) |
||||
} else { |
||||
let st |
||||
if (isFunction(activeStyle)) { |
||||
st = activeStyle(properties) |
||||
} else if (isObject(activeStyle)) { |
||||
st = cloneDeep(activeStyle) |
||||
} |
||||
if (!isEmpty(st)) { |
||||
const base = cloneDeep(baseStyle(properties)) |
||||
const activeStyle = extend(base, st) |
||||
setFeatureStyle(active.current, activeStyle) |
||||
} |
||||
} |
||||
} |
||||
highlight.current = null |
||||
} |
||||
|
||||
const clearActive = () => { |
||||
if (active.current) { |
||||
resetFeatureStyle(active.current) |
||||
} |
||||
active.current = null |
||||
} |
||||
|
||||
const getFeature = (featureId) => { |
||||
const { data, idField } = props |
||||
if (isEmpty(data) || isEmpty(data.features)) return {} |
||||
const feature = find(data.features, ({ properties }) => properties[idField] === featureId) |
||||
return cloneDeep(feature) |
||||
} |
||||
|
||||
const _propagateEvent = (eventHandler, e) => { |
||||
if (!isFunction(eventHandler)) return |
||||
const featureId = _getFeatureId(e.layer) |
||||
const feature = getFeature(featureId) |
||||
const event = cloneDeep(e) |
||||
const mergedEvent = merge(event.target, { |
||||
feature |
||||
}) |
||||
eventHandler(event) |
||||
} |
||||
|
||||
let vectorGrid |
||||
if (type === 'slicer') { |
||||
vectorGrid = new L.vectorGrid.slicer(data, { |
||||
interactive: interactive, |
||||
getFeatureId: (feature) => _getFeatureId(feature), |
||||
rendererFactory: L.svg.tile, |
||||
vectorTileLayerStyles: vectorTileLayerStyles || { |
||||
sliced: (properties, zoom) => { |
||||
const bs = baseStyle(properties, zoom) |
||||
bs.fill = true |
||||
bs.stroke = true |
||||
return bs |
||||
} |
||||
}, |
||||
...rest |
||||
}) |
||||
} else { |
||||
vectorGrid = new L.vectorGrid.vectorpoi(url, { |
||||
interactive: interactive, |
||||
key: accessKey, |
||||
token: accessToken, |
||||
vectorTileLayerStyles: vectorTileLayerStyles, |
||||
getFeatureId: (feature) => _getFeatureId(feature), |
||||
rendererFactory: L.svg.tile, |
||||
...rest |
||||
}) |
||||
} |
||||
vectorGrid |
||||
// .on('mouseover', (e) => {
|
||||
// console.log(e)
|
||||
// const { properties } = e.layer
|
||||
// _propagateEvent(onMouseover, e)
|
||||
// let st
|
||||
// const featureId = _getFeatureId(e.layer)
|
||||
// if (isFunction(hoverStyle)) {
|
||||
// st = hoverStyle(properties)
|
||||
// } else if (isObject(hoverStyle)) {
|
||||
// st = cloneDeep(hoverStyle)
|
||||
// }
|
||||
// if (!isEmpty(st) && featureId) {
|
||||
// clearHighlight(properties)
|
||||
// highlight.current = featureId
|
||||
// const base = cloneDeep(baseStyle(properties))
|
||||
// const hoverStyle = extend(base, st)
|
||||
// setFeatureStyle(featureId, hoverStyle)
|
||||
// }
|
||||
// })
|
||||
// .on('mouseout', (e) => {
|
||||
// const { properties } = e.layer
|
||||
// _propagateEvent(onMouseout, e)
|
||||
// clearHighlight(properties)
|
||||
// })
|
||||
// .on('click', (e) => {
|
||||
// const { properties } = e.layer
|
||||
// const featureId = _getFeatureId(e.layer)
|
||||
// _propagateEvent(onClick, e)
|
||||
// let st
|
||||
// if (isFunction(activeStyle)) {
|
||||
// st = activeStyle(properties)
|
||||
// } else if (isObject(activeStyle)) {
|
||||
// st = cloneDeep(activeStyle)
|
||||
// }
|
||||
// if (!isEmpty(st) && featureId) {
|
||||
// clearActive()
|
||||
// active.current = featureId
|
||||
// const base = cloneDeep(baseStyle(properties))
|
||||
// const activeStyle = extend(base, st)
|
||||
// setFeatureStyle(featureId, activeStyle)
|
||||
// }
|
||||
// })
|
||||
// .on('dblclick', (e) => {
|
||||
// _propagateEvent(onDblclick, e)
|
||||
// clearActive()
|
||||
// })
|
||||
.on('contextmenu', (e) => { |
||||
_propagateEvent(onContextmenu, e) |
||||
clearActive() |
||||
}) |
||||
|
||||
return { |
||||
instance: vectorGrid, |
||||
context |
||||
} |
||||
}, |
||||
function upgrade(layer, props, prevprops) { |
||||
return updateGridLayer(layer, props, prevprops) |
||||
} |
||||
) |
||||
|
||||
export default VectorGridd |
@ -0,0 +1,28 @@
|
||||
/* eslint-disable implicit-arrow-linebreak */ |
||||
/* eslint-disable no-unused-vars */ |
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
|
||||
import L from 'leaflet' |
||||
import './L.VectorGrid.VectorPoi' |
||||
|
||||
import { createTileLayerComponent, createElementObject, updateGridLayer } from '@react-leaflet/core' |
||||
|
||||
const VectorPoi = createTileLayerComponent( |
||||
function createBetterWMSLayer({ url, options }, context) { |
||||
const layer = new L.vectorGrid.vectorpoi(url, options, context) |
||||
layer.on('click', (e) => { |
||||
const { properties } = e.layer |
||||
console.log(e) |
||||
}) |
||||
return createElementObject(layer, context) |
||||
}, |
||||
(layer, props, prevProps) => { |
||||
updateGridLayer(layer, props, prevProps) |
||||
|
||||
if (props.params !== null && props.params !== prevProps.params) { |
||||
layer.setParams(props.params) |
||||
} |
||||
} |
||||
) |
||||
|
||||
export default VectorPoi |
@ -0,0 +1,249 @@
|
||||
/* eslint-disable no-unused-vars */ |
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
import { useLeafletContext } from '@react-leaflet/core' |
||||
import L from 'leaflet' |
||||
import 'leaflet.vectorgrid' |
||||
import '../generalWms/L.VectorGrid.VectorPoi' |
||||
|
||||
function getVectorStyles(layerName, fieldName) { |
||||
const style = {} |
||||
style[layerName] = function (properties, zoom) { |
||||
const p = properties[fieldName] |
||||
return { |
||||
color: p < 0.001 ? 'transparent' : p < 5 ? '#800026' : p < 15 ? '#E31A1C' : p < 30 ? '#FEB24C' : '#00ff00', |
||||
fillOpacity: 0.2, |
||||
// fillOpacity: 1,
|
||||
stroke: true, |
||||
fill: true, |
||||
//opacity: 0.2,
|
||||
weight: 3 |
||||
} |
||||
} |
||||
return style |
||||
} |
||||
|
||||
export const Styles = { |
||||
water: { |
||||
fill: true, |
||||
weight: 1, |
||||
fillColor: '#06cccc', |
||||
color: '#06cccc', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
admin: { |
||||
weight: 1, |
||||
fillColor: 'pink', |
||||
color: 'pink', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
waterway: { |
||||
weight: 1, |
||||
fillColor: '#2375e0', |
||||
color: '#2375e0', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
landcover: { |
||||
fill: true, |
||||
weight: 1, |
||||
fillColor: '#53e033', |
||||
color: '#53e033', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
landuse: { |
||||
fill: true, |
||||
weight: 1, |
||||
fillColor: '#e5b404', |
||||
color: '#e5b404', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
park: { |
||||
fill: true, |
||||
weight: 1, |
||||
fillColor: '#84ea5b', |
||||
color: '#84ea5b', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
boundary: { |
||||
weight: 1, |
||||
fillColor: '#c545d3', |
||||
color: '#c545d3', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
aeroway: { |
||||
weight: 1, |
||||
fillColor: '#51aeb5', |
||||
color: '#51aeb5', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
road: { |
||||
// mapbox & nextzen only
|
||||
weight: 1, |
||||
fillColor: '#f2b648', |
||||
color: '#f2b648', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
tunnel: { |
||||
// mapbox only
|
||||
weight: 0.5, |
||||
fillColor: '#f2b648', |
||||
color: '#f2b648', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
// dashArray: [4, 4]
|
||||
}, |
||||
bridge: { |
||||
// mapbox only
|
||||
weight: 0.5, |
||||
fillColor: '#f2b648', |
||||
color: '#f2b648', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
// dashArray: [4, 4]
|
||||
}, |
||||
transportation: { |
||||
// openmaptiles only
|
||||
weight: 0.5, |
||||
fillColor: '#f2b648', |
||||
color: '#f2b648', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
// dashArray: [4, 4]
|
||||
}, |
||||
transit: { |
||||
// nextzen only
|
||||
weight: 0.5, |
||||
fillColor: '#f2b648', |
||||
color: '#f2b648', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
// dashArray: [4, 4]
|
||||
}, |
||||
building: { |
||||
fill: true, |
||||
weight: 1, |
||||
fillColor: '#2b2b2b', |
||||
color: '#2b2b2b', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
water_name: { |
||||
weight: 1, |
||||
fillColor: '#022c5b', |
||||
color: '#022c5b', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
transportation_name: { |
||||
weight: 1, |
||||
fillColor: '#bc6b38', |
||||
color: '#bc6b38', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
place: { |
||||
weight: 1, |
||||
fillColor: '#f20e93', |
||||
color: '#f20e93', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
housenumber: { |
||||
weight: 1, |
||||
fillColor: '#ef4c8b', |
||||
color: '#ef4c8b', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
poi: { |
||||
weight: 1, |
||||
fillColor: '#3bb50a', |
||||
color: '#3bb50a', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
kpdl_npwp_tidak_valid: { |
||||
weight: 1, |
||||
fillColor: '#3bb50a', |
||||
color: '#3bb50a', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
earth: { |
||||
// nextzen only
|
||||
fill: true, |
||||
weight: 1, |
||||
fillColor: '#c0c0c0', |
||||
color: '#c0c0c0', |
||||
fillOpacity: 0.2, |
||||
opacity: 0.4 |
||||
}, |
||||
|
||||
// Do not symbolize some stuff for mapbox
|
||||
country_label: [], |
||||
marine_label: [], |
||||
state_label: [], |
||||
place_label: [], |
||||
waterway_label: [], |
||||
poi_label: [], |
||||
road_label: [], |
||||
housenum_label: [], |
||||
|
||||
// Do not symbolize some stuff for openmaptiles
|
||||
country_name: [], |
||||
marine_name: [], |
||||
state_name: [], |
||||
place_name: [], |
||||
waterway_name: [], |
||||
poi_name: [], |
||||
road_name: [], |
||||
housenum_name: [] |
||||
} |
||||
|
||||
export const PoiKpdl = () => { |
||||
const context = useLeafletContext() |
||||
const { map } = context |
||||
|
||||
const providerUrl = `http://localhost:8080/geoserver/wms?REQUEST=GetMap
|
||||
&SERVICE=WMS&VERSION=1.1.0&FORMAT=application/vnd.mapbox-vector-tile&STYLES=&TRANSPARENT=true |
||||
&LAYERS=matoa:kpdl_npwp_tidak_valid |
||||
&TILED=false&WIDTH=256&HEIGHT=256&CRS=EPSG:3857` |
||||
|
||||
const vectorGrid = useMemo(() => { |
||||
const options = { |
||||
interactive: true, |
||||
type: 'protobuf', |
||||
vectorTileLayerStyles: { |
||||
kpdl_npwp_tidak_valid: { weight: 1, fillColor: '#f20e93', color: '#f20e93', fillOpacity: 0.2, opacity: 0.4 } |
||||
} |
||||
} |
||||
|
||||
return L.vectorGrid.vectorpoi(providerUrl, options) |
||||
}, [providerUrl, Styles]) |
||||
|
||||
vectorGrid.on('click', function (e) { |
||||
console.log(this) |
||||
console.log('clicked', e) |
||||
}) |
||||
|
||||
vectorGrid.on('mouseover', function (e) { |
||||
console.log('mouseover', e) |
||||
}) |
||||
|
||||
useEffect(() => { |
||||
map.addLayer(vectorGrid) |
||||
return () => { |
||||
map.removeLayer(vectorGrid) |
||||
} |
||||
}, [map, vectorGrid]) |
||||
|
||||
return null |
||||
} |
@ -0,0 +1,117 @@
|
||||
import L from 'leaflet' |
||||
import 'leaflet-bing-layer' |
||||
const tgl = new Date() |
||||
|
||||
const thn = tgl.getFullYear() |
||||
const BING_KEY = 'AuhiCJHlGzhg93IqUH_oCpl_-ZUrIE6SPftlyGYUvr9Amx5nzA-WqGcPquyFZl4L' |
||||
export const bing_ae_opt = { |
||||
attribution: 'Base Map : <a href="https://www.microsoft.com/en-us/maps/product/print-rights">Microsoft Bing</a>', |
||||
bingMapsKey: BING_KEY, |
||||
imagerySet: 'Aerial', |
||||
culture: 'id-ID', |
||||
minZoom: 1, |
||||
maxZoom: 23, |
||||
minNativeZoom: 1, |
||||
maxNativeZoom: 20 |
||||
} |
||||
|
||||
export const bing_r_opt = { |
||||
attribution: 'Base Map : <a href="https://www.microsoft.com/en-us/maps/product/print-rights">Microsoft Bing</a>', |
||||
bingMapsKey: BING_KEY, |
||||
imagerySet: 'Road', |
||||
culture: 'id-ID', |
||||
minZoom: 1, |
||||
maxZoom: 30, |
||||
minNativeZoom: 1, |
||||
maxNativeZoom: 30 |
||||
} |
||||
|
||||
const Road = L.tileLayer('https://mt{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', { |
||||
attribution: `Map data ©${thn} <a href="https://www.google.com/intl/id_id/help/terms_maps/">Google</a>`, |
||||
maxZoom: 30, |
||||
minZoom: 1, |
||||
tileSize: 256, |
||||
zoomOffset: 0, |
||||
noWrap: false, |
||||
subdomains: '0123', |
||||
accessToken: '' |
||||
}) |
||||
|
||||
const Road_Preview = L.tileLayer('https://mt{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', { |
||||
attribution: `Map data ©${thn} <a href="https://www.google.com/intl/id_id/help/terms_maps/">Google</a>`, |
||||
maxZoom: 30, |
||||
minZoom: 1, |
||||
tileSize: 256, |
||||
zoomOffset: 0, |
||||
noWrap: false, |
||||
subdomains: '0123', |
||||
accessToken: '' |
||||
}) |
||||
|
||||
const Satellite = L.tileLayer('https://mt{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', { |
||||
attribution: `Map data ©${thn} <a href="https://www.google.com/intl/id_id/help/terms_maps/">Google</a>`, |
||||
maxZoom: 30, |
||||
minZoom: 1, |
||||
tileSize: 256, |
||||
zoomOffset: 0, |
||||
noWrap: false, |
||||
subdomains: '0123', |
||||
accessToken: '' |
||||
}) |
||||
const Hybrid = L.tileLayer('https://mt{s}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', { |
||||
attribution: `Map data ©${thn} <a href="https://www.google.com/intl/id_id/help/terms_maps/">Google</a>`, |
||||
maxZoom: 30, |
||||
minZoom: 1, |
||||
tileSize: 256, |
||||
zoomOffset: 0, |
||||
noWrap: false, |
||||
subdomains: '0123', |
||||
accessToken: '' |
||||
}) |
||||
const OSM1 = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { |
||||
maxZoom: 30, |
||||
minZoom: 1, |
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' |
||||
}) |
||||
// export const Bing_ae = L.tileLayer.bing(bing_ae_opt)
|
||||
// export const Bing_r = L.tileLayer.bing(bing_r_opt)
|
||||
|
||||
// export const baseLayers = {
|
||||
// GoogleRoad: Road,
|
||||
// GoogleSatellite: Satellite,
|
||||
// GoogleHybrid: Hybrid,
|
||||
// OpenStreetMap: OSM1,
|
||||
// BingAerial: Bing_ae,
|
||||
// BingRoad: Bing_r
|
||||
// }
|
||||
|
||||
export const layersmaps = [ |
||||
{ |
||||
name: 'Open Street Map', |
||||
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', |
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', |
||||
subdomains: ['a', 'b', 'c'], |
||||
checked: true |
||||
}, |
||||
{ |
||||
name: 'Google Road', |
||||
url: 'https://mt{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', |
||||
attribution: `Map data ©${thn} <a href="https://www.google.com/intl/id_id/help/terms_maps/">Google</a>`, |
||||
subdomains: ['0', '1', '2', '3'], |
||||
checked: false |
||||
}, |
||||
{ |
||||
name: 'Google Satellite', |
||||
url: 'https://mt{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', |
||||
attribution: `Map data ©${thn} <a href="https://www.google.com/intl/id_id/help/terms_maps/">Google</a>`, |
||||
subdomains: ['0', '1', '2', '3'], |
||||
checked: false |
||||
}, |
||||
{ |
||||
name: 'Google Hybrid', |
||||
url: 'https://mt{s}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', |
||||
attribution: `Map data ©${thn} <a href="https://www.google.com/intl/id_id/help/terms_maps/">Google</a>`, |
||||
subdomains: ['0', '1', '2', '3'], |
||||
checked: false |
||||
} |
||||
] |
@ -0,0 +1,33 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
|
||||
import GeneralWMS from '../generalWms/GeneralWMS' |
||||
|
||||
// url = http://localhost:8080/geoserver/wms?REQUEST=GetMap
|
||||
// &SERVICE=WMS&VERSION=1.3.0&FORMAT=application/vnd.mapbox-vector-tile&STYLES=&TRANSPARENT=true
|
||||
// &LAYERS=matoa:kpdl_npwp_tidak_valid
|
||||
// &TILED=false&WIDTH=256&HEIGHT=256&CRS=EPSG:3857&BBOX=0,-20037508.342789244,20037508.342789244,0
|
||||
const wmsLayer = { |
||||
id: '777', |
||||
url: '/engineN/geoserver/wms', |
||||
layers: 'matoa:kpdl_npwp_tidak_valid', |
||||
props: { |
||||
id: '777', |
||||
url: '/engineN/geoserver/wms', |
||||
layers: 'matoa:kpdl_npwp_tidak_valid', |
||||
version: '1.1.0', |
||||
format: 'application/vnd.mapbox-vector-tile', |
||||
styles: 'point', |
||||
transparent: true, |
||||
tiled: false, |
||||
zIndex: 150, |
||||
// width: 256,
|
||||
// height: 256,
|
||||
uppercase: true, |
||||
// CRS: 'EPSG:3857',
|
||||
// opacity: '0.8',
|
||||
cql_filter: '1=1' |
||||
} |
||||
} |
||||
export const KPdlLayer = ({ poiKpdlRef }) => { |
||||
return <GeneralWMS ref={poiKpdlRef} key={wmsLayer.id} id={wmsLayer.id} url={wmsLayer.url} layers={wmsLayer.layers} {...wmsLayer.props} /> |
||||
} |
@ -0,0 +1,135 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react' |
||||
import ReactDOM from 'react-dom' |
||||
import { LayersControl, MapContainer, TileLayer, useMap } from 'react-leaflet' |
||||
import L, { CRS, Control, Evented, Events, LatLngExpression, Layer, Map } from 'leaflet' |
||||
import { layersmaps } from './layers/baseLayers' |
||||
|
||||
import 'leaflet/dist/Leaflet.css' |
||||
import 'leaflet.locatecontrol' |
||||
import 'leaflet.locatecontrol/dist/L.Control.Locate.css' |
||||
import '../scss/core.scss' |
||||
import './app-maps.scss' |
||||
// import PoiKpdl from './layers/PoiKpdl'
|
||||
// import VectorPoi from './generalWms/VectorPoi'
|
||||
// import VectorGridd from './generalWms/VectorGridd'
|
||||
import { PoiKpdl } from './layers/PoiKpdl' |
||||
const Root = () => { |
||||
// document.body.style.margin = '0'
|
||||
document.body.className = 'm-0 p-0' |
||||
|
||||
const [map, setMap] = useState() |
||||
const [center, setCenter] = useState([-6.1659502, 106.7140899]) |
||||
const [zoom, setZoom] = useState(14) |
||||
const [layCon, setLayCon] = useState() |
||||
const wmsRef = useRef() |
||||
const poiKpdlRef = useRef() |
||||
|
||||
const layers = [] |
||||
const latestBase = localStorage.getItem('latestBase') ?? 'Open Street Map' |
||||
layersmaps.map((val) => { |
||||
if (val.name === latestBase) { |
||||
val.checked = true |
||||
} else { |
||||
val.checked = false |
||||
} |
||||
|
||||
val = { ...val } |
||||
return layers.push(val) |
||||
}) |
||||
|
||||
function baselayerchange(e) { |
||||
// setSelectedBase(e.name)
|
||||
localStorage.setItem('latestBase', e.name) |
||||
} |
||||
|
||||
const whenReady = (e) => { |
||||
const map = e.target |
||||
|
||||
if (map) { |
||||
map.addControl( |
||||
L.control.locate({ |
||||
drawCircle: false, |
||||
position: `bottomright`, |
||||
locateOptions: { |
||||
enableHighAccuracy: true |
||||
} |
||||
}) |
||||
) |
||||
map.addControl(L.control.zoom({ position: 'bottomright' })) |
||||
map.on('baselayerchange', baselayerchange) |
||||
} |
||||
} |
||||
const optVectorKpdl = { |
||||
id: '777', |
||||
url: '/engineN/geoserver/wms?REQUEST=GetMap&SERVICE=WMS&FORMAT=application/vnd.mapbox-vector-tile&CRS=EPSG:3857&STYLES=&TILED=false&TRANSPARENT=true&VERSION=1.3.0&HEIGHT=256&WIDTH=256&LAYERS=matoa:kpdl_npwp_tidak_valid', |
||||
mapParam: { |
||||
url: '/engineN/geoserver/wms', |
||||
LAYERS: 'matoa:kpdl_npwp_tidak_valid', |
||||
VERSION: '1.1.0', |
||||
FORMAT: 'application/vnd.mapbox-vector-tile', |
||||
TRANSPARENT: true, |
||||
TILED: false, |
||||
// zIndex: 150,
|
||||
WIDTH: 256, |
||||
HEIGHT: 256 |
||||
// uppercase: true,
|
||||
// cql_filter: '1=1'
|
||||
}, |
||||
interactive: true, |
||||
type: 'protobuf' |
||||
} |
||||
const displayMap = useMemo( |
||||
() => ( |
||||
<MapContainer |
||||
style={{ minHeight: '400px', minWidth: '380px' }} |
||||
id="map-container" |
||||
className="map-container" |
||||
ref={setMap} |
||||
center={center} |
||||
zoom={zoom} |
||||
scrollWheelZoom={true} |
||||
zoomControl={false} |
||||
whenReady={(e) => { |
||||
whenReady(e) |
||||
}} |
||||
> |
||||
<LayersControl ref={setLayCon} position="topright"> |
||||
{layers.map((val, idx) => ( |
||||
<LayersControl.BaseLayer name={val.name} key={idx} checked={val.checked}> |
||||
<TileLayer attribution={val.attribution} url={val.url} subdomains={val.subdomains} maxZoom={23} /> |
||||
</LayersControl.BaseLayer> |
||||
))} |
||||
<LayersControl.Overlay name={'Poi Google Map'} checked={true}> |
||||
<PoiKpdl ref={poiKpdlRef} /> |
||||
{/* <VectorGridd |
||||
url={optVectorKpdl.url} |
||||
options={optVectorKpdl} |
||||
vectorTileLayerStyles={{ |
||||
kpdl_npwp_tidak_valid: { |
||||
weight: 0, |
||||
fillColor: '#9bc2c4', |
||||
fillOpacity: 1, |
||||
fill: true |
||||
} |
||||
}} |
||||
/> */} |
||||
</LayersControl.Overlay> |
||||
</LayersControl> |
||||
</MapContainer> |
||||
), |
||||
[] |
||||
) |
||||
return ( |
||||
<> |
||||
<div style={{ height: '100vh', width: '100%', padding: '0' }}> |
||||
<div className="map-leaflet" id="map-leaflet"> |
||||
{displayMap} |
||||
</div> |
||||
</div> |
||||
</> |
||||
) |
||||
} |
||||
const container = document.getElementById('app') |
||||
const component = <Root /> |
||||
|
||||
ReactDOM.render(component, container) |
@ -0,0 +1,27 @@
|
||||
<?php |
||||
helper('Kpdl'); |
||||
?> |
||||
<html> |
||||
<head> |
||||
<meta charset="utf-8" /> |
||||
<title>eGeoTax</title> |
||||
<link rel="shortcut icon" href="/engineN/public/favicon.ico" /> |
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1" /> |
||||
<meta name="theme-color" content="#000000" /> |
||||
<link rel="stylesheet" href="<?=base_url('public/kpdl/dist/peta.css')?>" type="text/css">
|
||||
<style> |
||||
.highcharts-credits{ |
||||
visibility: hidden; |
||||
} |
||||
</style> |
||||
|
||||
</head> |
||||
<body> |
||||
|
||||
<div id="app"></div> |
||||
|
||||
<?php $isDevelopment = ENVIRONMENT === 'development';?> |
||||
<script type="text/javascript" src="<?=base_url()?>public/kpdl/dist/peta.js"></script>
|
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,47 @@
|
||||
// ================================================================================================ |
||||
// File Name: bootstrap-extended.scss |
||||
// Description: List of modified Bootstrap files. This is an actual copy of bootstrap.scss |
||||
// excluding files that have not been modified. |
||||
// ---------------------------------------------------------------------------------------------- |
||||
// Item Name: Vuexy - Vuejs, HTML & Laravel Admin Dashboard Template |
||||
// Author: PIXINVENT |
||||
// Author URL: http://www.themeforest.net/user/pixinvent |
||||
// ================================================================================================ |
||||
|
||||
@import "bootstrap-extended/include"; // Bootstrap includes |
||||
@import "components/include"; // Components includes |
||||
|
||||
// Custom template mixins |
||||
@import "core/mixins/alert"; // Template custom mixins |
||||
|
||||
// Core CSS |
||||
@import "bootstrap-extended/reboot"; |
||||
@import "bootstrap-extended/helper"; |
||||
@import "bootstrap-extended/type"; |
||||
@import "bootstrap-extended/code"; |
||||
@import "bootstrap-extended/tables"; |
||||
@import "bootstrap-extended/forms"; |
||||
@import "bootstrap-extended/buttons"; |
||||
@import "bootstrap-extended/button-group"; |
||||
|
||||
// Components |
||||
@import "bootstrap-extended/dropdown"; |
||||
@import "bootstrap-extended/navbar"; |
||||
@import "bootstrap-extended/card"; |
||||
@import "bootstrap-extended/carousel"; |
||||
@import "bootstrap-extended/breadcrumb"; |
||||
@import "bootstrap-extended/badge"; |
||||
@import "bootstrap-extended/nav"; |
||||
@import "bootstrap-extended/alert"; |
||||
@import "bootstrap-extended/progress"; |
||||
@import "bootstrap-extended/list-group"; |
||||
@import "bootstrap-extended/toast"; |
||||
@import "bootstrap-extended/accordion"; |
||||
@import "bootstrap-extended/pagination"; |
||||
|
||||
// Components w/ JavaScript |
||||
@import "bootstrap-extended/modal"; |
||||
@import "bootstrap-extended/popover"; |
||||
|
||||
// Utility classes |
||||
@import "bootstrap-extended/utilities"; |
@ -0,0 +1,63 @@
|
||||
// Basic accordion |
||||
.accordion { |
||||
[data-bs-toggle='collapse'] { |
||||
font-weight: 500; |
||||
font-size: 1.1rem; |
||||
line-height: $line-height-base; |
||||
} |
||||
.accordion-item { |
||||
margin-bottom: 0; |
||||
&:last-of-type { |
||||
margin-bottom: 0; |
||||
} |
||||
&:not(:last-of-type) { |
||||
border-bottom: 1px solid $border-color; |
||||
} |
||||
} |
||||
.accordion-body { |
||||
padding-top: 0.42rem; |
||||
} |
||||
} |
||||
|
||||
// accordion without icon |
||||
.accordion { |
||||
&.accordion-without-arrow { |
||||
.accordion-button::after { |
||||
background-image: none !important; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// For Accordion with border |
||||
.accordion-border { |
||||
.accordion-item { |
||||
border: 1px solid $border-color; |
||||
border-radius: $card-border-radius; |
||||
&:not(:last-of-type) { |
||||
border-bottom: 0; |
||||
border-bottom-right-radius: 0; |
||||
border-bottom-left-radius: 0; |
||||
} |
||||
&:not(:first-of-type) { |
||||
border-top-left-radius: 0; |
||||
border-top-right-radius: 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// For Accordion with margin |
||||
.accordion-margin { |
||||
.accordion-item { |
||||
margin-top: 0.71rem; |
||||
margin-bottom: 0.71rem; |
||||
box-shadow: 0 2px 15px 0 rgba($black, 0.05) !important; |
||||
border-radius: $border-radius; |
||||
border-bottom: 0 solid transparent !important; |
||||
} |
||||
} |
||||
|
||||
.card.accordion-item { |
||||
.accordion-button { |
||||
border-radius: $border-radius; |
||||
} |
||||
} |
@ -0,0 +1,50 @@
|
||||
// Alerts |
||||
|
||||
.alert { |
||||
font-weight: 500; |
||||
padding: 0; |
||||
// close |
||||
&.alert-dismissible { |
||||
.btn-close { |
||||
padding: 1rem $alert-padding-x; |
||||
background-color: transparent !important; |
||||
box-shadow: none !important; |
||||
} |
||||
.alert-body { |
||||
padding: $alert-padding-y ($alert-padding-x * 2) $alert-padding-y $alert-padding-x; |
||||
} |
||||
} |
||||
.btn-close:focus { |
||||
outline: 0; |
||||
} |
||||
|
||||
.alert-link:hover { |
||||
text-decoration: underline; |
||||
} |
||||
|
||||
// For Alert Content |
||||
p { |
||||
font-weight: 500; |
||||
padding: 2px 0; |
||||
margin-bottom: 0; |
||||
vertical-align: middle; |
||||
} |
||||
|
||||
// For alert heading |
||||
.alert-heading { |
||||
font-weight: 600; |
||||
font-size: $font-size-base; |
||||
padding: $alert-padding-y $alert-padding-x; |
||||
margin-bottom: 0; |
||||
} |
||||
.alert-body { |
||||
padding: $alert-padding-y $alert-padding-x; |
||||
} |
||||
|
||||
// For dark alert |
||||
&.alert-dark { |
||||
.alert-heading { |
||||
@include alert-heading-bs($dark); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,89 @@
|
||||
// Badge |
||||
|
||||
.badge { |
||||
&[class*='bg-'] { |
||||
[class*='icon-'] { |
||||
line-height: 1; |
||||
} |
||||
|
||||
a { |
||||
color: $white; |
||||
} |
||||
|
||||
// badge dropdown alignment |
||||
.dropdown-toggle, |
||||
&.dropdown-toggle { |
||||
span, |
||||
i, |
||||
svg { |
||||
vertical-align: text-top; |
||||
} |
||||
i, |
||||
svg { |
||||
padding-left: 0.2rem; |
||||
} |
||||
&::after { |
||||
position: relative; |
||||
top: 0; |
||||
left: 0; |
||||
font-size: 1rem; |
||||
} |
||||
} |
||||
.dropdown-menu { |
||||
a { |
||||
color: $dropdown-color; |
||||
} |
||||
} |
||||
} |
||||
|
||||
i, |
||||
svg { |
||||
height: 12px; |
||||
width: 11px; |
||||
font-size: 12px; |
||||
stroke-width: 3; |
||||
vertical-align: top; |
||||
} |
||||
|
||||
// square badge |
||||
&.badge-square { |
||||
border-radius: 0; |
||||
} |
||||
|
||||
// badge-up |
||||
// to align badge over any element |
||||
&.badge-up { |
||||
position: absolute; |
||||
top: -11px; |
||||
right: -9px; |
||||
min-width: 1.429rem; |
||||
min-height: 1.429rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
font-size: 0.786rem; |
||||
line-height: 0.786; |
||||
padding-left: 0.25rem; |
||||
padding-right: 0.25rem; |
||||
&.badge-sm { |
||||
top: -0.5rem; |
||||
right: -0.5rem; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// For fullscreen search |
||||
.badge-icon { |
||||
i, |
||||
svg { |
||||
font-size: 100%; |
||||
margin-right: 5px; |
||||
} |
||||
} |
||||
|
||||
// badge dropup pointer |
||||
.dropup { |
||||
.badge { |
||||
cursor: pointer; |
||||
} |
||||
} |
@ -0,0 +1,80 @@
|
||||
.breadcrumb { |
||||
&:not([class*='breadcrumb-']) { |
||||
.breadcrumb-item + .breadcrumb-item { |
||||
&:before { |
||||
content: ' '; |
||||
background-image: url(str-replace(str-replace($chevron-right, 'currentColor', $body-color), '#', '%23')); |
||||
background-repeat: no-repeat; |
||||
background-position: center; |
||||
color: $body-color; |
||||
margin-right: $breadcrumb-item-padding-x; |
||||
background-size: 14px; |
||||
} |
||||
} |
||||
} |
||||
.breadcrumb-item + .breadcrumb-item { |
||||
&:before { |
||||
height: 20px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Component Specific */ |
||||
.breadcrumb-slash { |
||||
&.breadcrumb { |
||||
.breadcrumb-item + .breadcrumb-item:before { |
||||
content: '/'; |
||||
} |
||||
} |
||||
} |
||||
.breadcrumb-dots { |
||||
&.breadcrumb { |
||||
.breadcrumb-item + .breadcrumb-item:before { |
||||
content: '.'; |
||||
position: relative; |
||||
top: -4px; |
||||
} |
||||
} |
||||
} |
||||
.breadcrumb-dashes { |
||||
&.breadcrumb { |
||||
.breadcrumb-item + .breadcrumb-item:before { |
||||
content: '-'; |
||||
} |
||||
} |
||||
} |
||||
.breadcrumb-pipes { |
||||
&.breadcrumb { |
||||
.breadcrumb-item + .breadcrumb-item:before { |
||||
content: '|'; |
||||
} |
||||
} |
||||
} |
||||
.breadcrumb-chevron { |
||||
&.breadcrumb { |
||||
.breadcrumb-item + .breadcrumb-item:before { |
||||
content: ' '; |
||||
background-image: url(str-replace(str-replace($chevron-right, 'currentColor', $body-color), '#', '%23')); |
||||
background-repeat: no-repeat; |
||||
background-position: center; |
||||
color: $body-color; |
||||
margin-right: $breadcrumb-item-padding-x; |
||||
background-size: 14px; |
||||
} |
||||
} |
||||
} |
||||
// padding left for header area breadcrumbs |
||||
.content-header .breadcrumb { |
||||
padding-left: 1rem; |
||||
} |
||||
|
||||
@media (max-width: 648px) { |
||||
.content-header .breadcrumb { |
||||
display: none; |
||||
} |
||||
.breadcrumbs-top { |
||||
.content-header-title { |
||||
display: contents !important; |
||||
} |
||||
} |
||||
} |