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 jquery 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 = '/engineN/' 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) jquery.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 = location.protocol + '//' + location.hostname + '/engineN/' 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: 'TGL_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 className="f-14"> <Col> <span> Berdasarkan kompilasi data KPD Mobile dan pengolahan data-data perpajakan lainnya (KPD Lainnya), daftar Wajib Pajak yang <b>bayar tidak wajar </b> agar segera dilakukan dinamisasi/kegiatan intensifikasi pajak (PPM dan/atau PKM) melalui mekanisme komite kepatuhan (DSP4 dan/atau WRA), agar sesuai dengan kondisi kegiatan usaha yang sebenarnya, oleh: </span> <ul> <li> AR yang <b>mengampu WP</b> tersebut; </li> <li> AR yang <b>mengampu wilayah</b> tersebut dengan mengirimkan LHKPD (data ICALEP) ke unit kerja yang mengadministrasikan kegiatan usaha yang diawasi; </li> <li>Input di DRM</li> </ul> </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