From fd7b49ab93c7168c585009cbe359307d21f8898a Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Tue, 19 May 2026 11:51:17 +0700 Subject: [PATCH 1/3] feat: implement server-side sorting in report expense --- .../report/expense/tab/ReportExpenseTab.tsx | 64 ++++++++++++++----- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/src/components/pages/report/expense/tab/ReportExpenseTab.tsx b/src/components/pages/report/expense/tab/ReportExpenseTab.tsx index edd5b725..8e6b49b5 100644 --- a/src/components/pages/report/expense/tab/ReportExpenseTab.tsx +++ b/src/components/pages/report/expense/tab/ReportExpenseTab.tsx @@ -39,7 +39,7 @@ import { } from '@/services/api/master-data'; import { Supplier } from '@/types/api/master-data/supplier'; import { Nonstock } from '@/types/api/master-data/nonstock'; -import { ColumnDef } from '@tanstack/react-table'; +import { ColumnDef, SortingState, Updater } from '@tanstack/react-table'; import { httpClient } from '@/services/http/client'; import { BaseApiResponse } from '@/types/api/api-general'; import ButtonFilter from '@/components/helper/ButtonFilter'; @@ -73,6 +73,25 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); + // ===== SORTING STATE ===== + const [sortBy, setSortBy] = useState(''); + const [orderBy, setOrderBy] = useState(''); + + const sorting: SortingState = sortBy + ? [{ id: sortBy, desc: orderBy === 'desc' }] + : []; + + const handleSortingChange = (updater: Updater) => { + const next = typeof updater === 'function' ? updater(sorting) : updater; + if (next.length > 0) { + setSortBy(next[0].id); + setOrderBy(next[0].desc ? 'desc' : 'asc'); + } else { + setSortBy(''); + setOrderBy(''); + } + }; + const handleFilterModalOpenRef = useRef(() => {}); const filterModal = useModal(); @@ -252,6 +271,8 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { if (filterParams.category) { params.append('category', filterParams.category); } + if (sortBy) params.append('sort_by', sortBy); + if (orderBy) params.append('sort_order', orderBy); Object.entries(extraParams ?? {}).forEach(([key, value]) => { params.set(key, value); @@ -259,7 +280,7 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { return params.toString(); }, - [filterParams] + [filterParams, sortBy, orderBy] ); // ===== DATA FETCHING ===== @@ -443,19 +464,23 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { return [ { header: 'No', + enableSorting: false, cell: (props) => (page - 1) * pageSize + props.row.index + 1, }, { header: 'No. PO', accessorKey: 'po_number', + enableSorting: true, }, { header: 'No. Referensi', accessorKey: 'reference_number', + enableSorting: true, }, { header: 'Tanggal Realisasi', accessorKey: 'realization_date', + enableSorting: true, cell: ({ row }) => { return formatDate(row.original?.realization_date, 'DD MMM, YYYY'); }, @@ -463,6 +488,7 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { { header: 'Tanggal Transaksi', accessorKey: 'transaction_date', + enableSorting: true, cell: ({ row }) => { return formatDate(row.original?.transaction_date, 'DD MMM, YYYY'); }, @@ -470,21 +496,30 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { { header: 'Kategori', accessorKey: 'category', + enableSorting: true, }, { header: 'Produk', + accessorKey: 'product', + enableSorting: true, accessorFn: (row) => row.pengajuan?.nonstock?.name, }, { header: 'Supplier', + accessorKey: 'supplier', + enableSorting: true, accessorFn: (row) => row.supplier?.name, }, { header: 'Lokasi', + accessorKey: 'location', + enableSorting: true, accessorFn: (row) => row.kandang?.location?.name, }, { header: 'Kandang', + accessorKey: 'kandang', + enableSorting: true, accessorFn: (row) => row.kandang?.name, }, { @@ -492,23 +527,19 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { columns: [ { header: 'Qty', - id: 'qty_pengajuan', - accessorFn: (row) => row.pengajuan?.qty, + accessorKey: 'qty_pengajuan', cell: ({ row }) => row.original.pengajuan?.qty?.toLocaleString('id-ID') || '0', }, { header: 'Harga', - id: 'harga_pengajuan', - accessorFn: (row) => row.pengajuan?.price, + accessorKey: 'price_pengajuan', cell: ({ row }) => formatCurrency(row.original.pengajuan?.price || 0), }, { header: 'Total', - id: 'total_pengajuan', - accessorFn: (row) => - (row.pengajuan?.qty || 0) * (row.pengajuan?.price || 0), + accessorKey: 'total_pengajuan', cell: ({ row }) => { const total = (row.original.pengajuan?.qty || 0) * @@ -523,23 +554,19 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { columns: [ { header: 'Qty', - id: 'qty_realisasi', - accessorFn: (row) => row.realisasi?.qty, + accessorKey: 'qty_realisasi', cell: ({ row }) => row.original.realisasi?.qty?.toLocaleString('id-ID') || '0', }, { header: 'Harga', - id: 'harga_realisasi', - accessorFn: (row) => row.realisasi?.price, + accessorKey: 'price_realisasi', cell: ({ row }) => formatCurrency(row.original.realisasi?.price || 0), }, { header: 'Total', - id: 'total_realisasi', - accessorFn: (row) => - (row.realisasi?.qty || 0) * (row.realisasi?.price || 0), + accessorKey: 'total_realisasi', cell: ({ row }) => { const total = (row.original.realisasi?.qty || 0) * @@ -550,6 +577,7 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { ], }, { + id: 'realization_status', header: 'Status Pencairan', cell: (props) => ( { ), }, { + id: 'bop_status', header: 'Status BOP', cell: (props) => ( @@ -602,6 +631,9 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { totalItems={meta?.total_results || 0} onPageChange={setPage} onPageSizeChange={setPageSize} + sorting={sorting} + setSorting={handleSortingChange} + manualSorting className={{ containerClassName: 'w-full mb-0!', tableWrapperClassName: 'overflow-x-auto', From 802bf77bc508fa16dd78d54b44fe4a58ded1a25a Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Tue, 19 May 2026 11:51:27 +0700 Subject: [PATCH 2/3] feat: add rtk instructions --- CLAUDE.md | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index d8f15df6..711d5a1c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -260,3 +260,155 @@ const handleExportExcel = useCallback(async () => { - Do **not** import `xlsx`, `@react-pdf/renderer`, `jspdf`, `exceljs` in page/tab components. **Reference implementation:** `MarketingReportApiService.exportDailyMarketingToExcel` / `exportDailyMarketingToPDF` in [src/services/api/report/marketing-report.ts](src/services/api/report/marketing-report.ts), consumed by [src/components/pages/report/marketing/tab/DailyMarketingTab.tsx](src/components/pages/report/marketing/tab/DailyMarketingTab.tsx). + + + +# RTK (Rust Token Killer) - Token-Optimized Commands + +## Golden Rule + +**Always prefix commands with `rtk`**. If RTK has a dedicated filter, it uses it. If not, it passes through unchanged. This means RTK is always safe to use. + +**Important**: Even in command chains with `&&`, use `rtk`: + +```bash +# ❌ Wrong +git add . && git commit -m "msg" && git push + +# ✅ Correct +rtk git add . && rtk git commit -m "msg" && rtk git push +``` + +## RTK Commands by Workflow + +### Build & Compile (80-90% savings) + +```bash +rtk cargo build # Cargo build output +rtk cargo check # Cargo check output +rtk cargo clippy # Clippy warnings grouped by file (80%) +rtk tsc # TypeScript errors grouped by file/code (83%) +rtk lint # ESLint/Biome violations grouped (84%) +rtk prettier --check # Files needing format only (70%) +rtk next build # Next.js build with route metrics (87%) +``` + +### Test (60-99% savings) + +```bash +rtk cargo test # Cargo test failures only (90%) +rtk go test # Go test failures only (90%) +rtk jest # Jest failures only (99.5%) +rtk vitest # Vitest failures only (99.5%) +rtk playwright test # Playwright failures only (94%) +rtk pytest # Python test failures only (90%) +rtk rake test # Ruby test failures only (90%) +rtk rspec # RSpec test failures only (60%) +rtk test # Generic test wrapper - failures only +``` + +### Git (59-80% savings) + +```bash +rtk git status # Compact status +rtk git log # Compact log (works with all git flags) +rtk git diff # Compact diff (80%) +rtk git show # Compact show (80%) +rtk git add # Ultra-compact confirmations (59%) +rtk git commit # Ultra-compact confirmations (59%) +rtk git push # Ultra-compact confirmations +rtk git pull # Ultra-compact confirmations +rtk git branch # Compact branch list +rtk git fetch # Compact fetch +rtk git stash # Compact stash +rtk git worktree # Compact worktree +``` + +Note: Git passthrough works for ALL subcommands, even those not explicitly listed. + +### GitHub (26-87% savings) + +```bash +rtk gh pr view # Compact PR view (87%) +rtk gh pr checks # Compact PR checks (79%) +rtk gh run list # Compact workflow runs (82%) +rtk gh issue list # Compact issue list (80%) +rtk gh api # Compact API responses (26%) +``` + +### JavaScript/TypeScript Tooling (70-90% savings) + +```bash +rtk pnpm list # Compact dependency tree (70%) +rtk pnpm outdated # Compact outdated packages (80%) +rtk pnpm install # Compact install output (90%) +rtk npm run