You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

350 lines
10 KiB

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