From aadf10b8b9deb82ac54a1576363ff1717efc666d Mon Sep 17 00:00:00 2001 From: rstubryan Date: Mon, 2 Mar 2026 11:17:54 +0700 Subject: [PATCH] refactor(FE): Refactor master-data pages to simplify component rendering --- src/app/master-data/area/page.tsx | 6 +- src/app/master-data/bank/page.tsx | 6 +- src/app/master-data/customer/page.tsx | 6 +- src/app/master-data/flock/page.tsx | 6 +- src/app/master-data/kandang/page.tsx | 6 +- src/app/master-data/location/page.tsx | 6 +- src/app/master-data/nonstock/page.tsx | 6 +- src/app/master-data/product-category/page.tsx | 6 +- .../master-data/production-standard/page.tsx | 6 +- src/app/master-data/supplier/page.tsx | 6 +- src/app/master-data/uom/page.tsx | 6 +- src/app/master-data/warehouse/page.tsx | 6 +- .../pages/master-data/area/AreasTable.tsx | 354 +++++++------- .../pages/master-data/bank/BanksTable.tsx | 378 ++++++++------- .../master-data/customer/CustomersTable.tsx | 408 ++++++++-------- .../pages/master-data/flock/FlocksTable.tsx | 398 +++++++-------- .../master-data/kandang/KandangsTable.tsx | 406 +++++++--------- .../master-data/location/LocationsTable.tsx | 394 +++++++-------- .../master-data/nonstock/NonstocksTable.tsx | 2 +- .../product-category/ProductCategoryTable.tsx | 395 +++++++-------- .../master-data/product/ProductTable.tsx | 2 +- .../ProductionStandardTable.tsx | 338 +++++++------ .../master-data/supplier/SupplierTable.tsx | 438 ++++++++--------- .../pages/master-data/uom/UomsTable.tsx | 350 +++++++------- .../master-data/warehouse/WarehousesTable.tsx | 452 ++++++++---------- 25 files changed, 2118 insertions(+), 2269 deletions(-) diff --git a/src/app/master-data/area/page.tsx b/src/app/master-data/area/page.tsx index f8789af2..2c3cef14 100644 --- a/src/app/master-data/area/page.tsx +++ b/src/app/master-data/area/page.tsx @@ -1,11 +1,7 @@ import AreasTable from '@/components/pages/master-data/area/AreasTable'; const Nonstock = () => { - return ( -
- -
- ); + return ; }; export default Nonstock; diff --git a/src/app/master-data/bank/page.tsx b/src/app/master-data/bank/page.tsx index 3f913c55..371cc3bf 100644 --- a/src/app/master-data/bank/page.tsx +++ b/src/app/master-data/bank/page.tsx @@ -1,11 +1,7 @@ import BanksTable from '@/components/pages/master-data/bank/BanksTable'; const Bank = () => { - return ( -
- -
- ); + return ; }; export default Bank; diff --git a/src/app/master-data/customer/page.tsx b/src/app/master-data/customer/page.tsx index 8aec1088..05c0e1e8 100644 --- a/src/app/master-data/customer/page.tsx +++ b/src/app/master-data/customer/page.tsx @@ -1,11 +1,7 @@ import CustomersTable from '@/components/pages/master-data/customer/CustomersTable'; const Customer = () => { - return ( -
- -
- ); + return ; }; export default Customer; diff --git a/src/app/master-data/flock/page.tsx b/src/app/master-data/flock/page.tsx index 76cc32c1..418018ab 100644 --- a/src/app/master-data/flock/page.tsx +++ b/src/app/master-data/flock/page.tsx @@ -1,11 +1,7 @@ import FlockTable from '@/components/pages/master-data/flock/FlocksTable'; const Flock = () => { - return ( -
- -
- ); + return ; }; export default Flock; diff --git a/src/app/master-data/kandang/page.tsx b/src/app/master-data/kandang/page.tsx index 293eb0da..e281e82c 100644 --- a/src/app/master-data/kandang/page.tsx +++ b/src/app/master-data/kandang/page.tsx @@ -1,11 +1,7 @@ import KandangsTable from '@/components/pages/master-data/kandang/KandangsTable'; const Nonstock = () => { - return ( -
- -
- ); + return ; }; export default Nonstock; diff --git a/src/app/master-data/location/page.tsx b/src/app/master-data/location/page.tsx index 338fdbff..af65761f 100644 --- a/src/app/master-data/location/page.tsx +++ b/src/app/master-data/location/page.tsx @@ -1,11 +1,7 @@ import LocationsTable from '@/components/pages/master-data/location/LocationsTable'; const Nonstock = () => { - return ( -
- -
- ); + return ; }; export default Nonstock; diff --git a/src/app/master-data/nonstock/page.tsx b/src/app/master-data/nonstock/page.tsx index 0812a5e2..02ed2e1e 100644 --- a/src/app/master-data/nonstock/page.tsx +++ b/src/app/master-data/nonstock/page.tsx @@ -1,11 +1,7 @@ import NonstocksTable from '@/components/pages/master-data/nonstock/NonstocksTable'; const Nonstock = () => { - return ( -
- -
- ); + return ; }; export default Nonstock; diff --git a/src/app/master-data/product-category/page.tsx b/src/app/master-data/product-category/page.tsx index 78a4fda3..7c0a6656 100644 --- a/src/app/master-data/product-category/page.tsx +++ b/src/app/master-data/product-category/page.tsx @@ -1,11 +1,7 @@ import ProductCategoryTable from '@/components/pages/master-data/product-category/ProductCategoryTable'; const ProductCategory = () => { - return ( -
- -
- ); + return ; }; export default ProductCategory; diff --git a/src/app/master-data/production-standard/page.tsx b/src/app/master-data/production-standard/page.tsx index ed1107cd..17944ebe 100644 --- a/src/app/master-data/production-standard/page.tsx +++ b/src/app/master-data/production-standard/page.tsx @@ -1,11 +1,7 @@ import ProductionStandardTable from '@/components/pages/master-data/production-standard/ProductionStandardTable'; const ProductionStandardPage = () => { - return ( -
- -
- ); + return ; }; export default ProductionStandardPage; diff --git a/src/app/master-data/supplier/page.tsx b/src/app/master-data/supplier/page.tsx index 8000be0a..169fd071 100644 --- a/src/app/master-data/supplier/page.tsx +++ b/src/app/master-data/supplier/page.tsx @@ -1,11 +1,7 @@ import SuppliersTable from '@/components/pages/master-data/supplier/SupplierTable'; const Supplier = () => { - return ( -
- -
- ); + return ; }; export default Supplier; diff --git a/src/app/master-data/uom/page.tsx b/src/app/master-data/uom/page.tsx index 689b9d0d..b5ba52b8 100644 --- a/src/app/master-data/uom/page.tsx +++ b/src/app/master-data/uom/page.tsx @@ -1,11 +1,7 @@ import UomsTable from '@/components/pages/master-data/uom/UomsTable'; const Nonstock = () => { - return ( -
- -
- ); + return ; }; export default Nonstock; diff --git a/src/app/master-data/warehouse/page.tsx b/src/app/master-data/warehouse/page.tsx index eb5ae416..7119283e 100644 --- a/src/app/master-data/warehouse/page.tsx +++ b/src/app/master-data/warehouse/page.tsx @@ -1,11 +1,7 @@ import WarehousesTable from '@/components/pages/master-data/warehouse/WarehousesTable'; const Warehouse = () => { - return ( -
- -
- ); + return ; }; export default Warehouse; diff --git a/src/components/pages/master-data/area/AreasTable.tsx b/src/components/pages/master-data/area/AreasTable.tsx index d92c7840..cc8084d8 100644 --- a/src/components/pages/master-data/area/AreasTable.tsx +++ b/src/components/pages/master-data/area/AreasTable.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ChangeEventHandler, useEffect, useState } from 'react'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -11,71 +11,92 @@ import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import Button from '@/components/Button'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; -import SelectInput, { OptionType } from '@/components/input/SelectInput'; -import RowDropdownOptions from '@/components/table/RowDropdownOptions'; -import RowCollapseOptions from '@/components/table/RowCollapseOptions'; -import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RequirePermission from '@/components/helper/RequirePermission'; +import PopoverButton from '@/components/popover/PopoverButton'; +import PopoverContent from '@/components/popover/PopoverContent'; import { Area } from '@/types/api/master-data/area'; import { AreaApi } from '@/services/api/master-data'; import { cn } from '@/lib/helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { ROWS_OPTIONS } from '@/config/constant'; const RowOptionsMenu = ({ - type = 'dropdown', + popoverPosition = 'bottom', props, deleteClickHandler, }: { - type: 'dropdown' | 'collapse'; + popoverPosition: 'bottom' | 'top'; props: CellContext; deleteClickHandler: () => void; }) => { + const popoverId = `area#${props.row.original.id}`; + const popoverAnchorName = `--anchor-area#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - +
+ + + - - - - - - - - + +
+ + + + + + + + + +
+
+
); }; @@ -87,10 +108,17 @@ const AreasTable = () => { setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ - initial: { search: '', nameSort: '' }, - paramMap: { page: 'page', pageSize: 'limit', nameSort: 'sort_name' }, + initial: { + search: '', + }, + paramMap: { + page: 'page', + pageSize: 'limit', + }, }); + const [sorting, setSorting] = useState([]); + const { data: areas, isLoading, @@ -101,65 +129,12 @@ const AreasTable = () => { ); const deleteModal = useModal(); - const [selectedArea, setSelectedArea] = useState(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [sorting, setSorting] = useState([]); - - const areasColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedArea(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -179,95 +154,114 @@ const AreasTable = () => { setIsDeleteLoading(false); }; - const searchChangeHandler: ChangeEventHandler = (e) => { - updateFilter('search', e.target.value); - }; + const areasColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - setPageSize(newVal.value as number); - }; + const deleteClickHandler = () => { + setSelectedArea(props.row.original); + deleteModal.openModal(); + }; - // track sorting - useEffect(() => { - const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name'); - - if (!isNameSorted) { - updateFilter('nameSort', ''); - } else { - updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc'); - } - }, [sorting, updateFilter]); + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] + ); return ( <> -
-
-
-
-
- - - -
-
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ {/* Search */} +
-
- -
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(areas) ? areas?.data : []} - columns={areasColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(areas) ? areas?.meta?.page : 0} - totalItems={isResponseSuccess(areas) ? areas?.meta?.total_results : 0} - onPageChange={setPage} - isLoading={isLoading} - sorting={sorting} - setSorting={setSorting} - className={{ - containerClassName: cn({ - 'mb-20': isResponseSuccess(areas) && areas?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(areas) ? areas?.data : []} + columns={areasColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(areas) ? areas?.meta?.page : 0} + totalItems={ + isResponseSuccess(areas) ? areas?.meta?.total_results : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': isResponseSuccess(areas) && areas?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
; deleteClickHandler: () => void; }) => { + const popoverId = `bank#${props.row.original.id}`; + const popoverAnchorName = `--anchor-bank#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - +
+ + + - - - - - - - - + +
+ + + + + + + + + +
+
+
); }; @@ -87,10 +108,17 @@ const BanksTable = () => { setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ - initial: { search: '', nameSort: '' }, - paramMap: { page: 'page', pageSize: 'limit', nameSort: 'sort_name' }, + initial: { + search: '', + }, + paramMap: { + page: 'page', + pageSize: 'limit', + }, }); + const [sorting, setSorting] = useState([]); + const { data: banks, isLoading, @@ -101,78 +129,12 @@ const BanksTable = () => { ); const deleteModal = useModal(); - const [selectedBank, setSelectedBank] = useState(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [sorting, setSorting] = useState([]); - - const banksColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - accessorKey: 'alias', - header: 'Alias', - }, - { - accessorKey: 'account_number', - header: 'No. Rekening', - }, - { - accessorKey: 'owner', - header: 'Pemilik', - cell: (props) => (props.getValue() ? props.getValue() : '-'), - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedBank(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -192,93 +154,127 @@ const BanksTable = () => { setIsDeleteLoading(false); }; - const searchChangeHandler: ChangeEventHandler = (e) => { - updateFilter('search', e.target.value); - }; + const banksColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorKey: 'alias', + header: 'Alias', + }, + { + accessorKey: 'account_number', + header: 'No. Rekening', + }, + { + accessorKey: 'owner', + header: 'Pemilik', + cell: (props) => props.getValue() || '-', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - setPageSize(newVal.value as number); - }; + const deleteClickHandler = () => { + setSelectedBank(props.row.original); + deleteModal.openModal(); + }; - // track sorting - useEffect(() => { - const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name'); - - if (!isNameSorted) { - updateFilter('nameSort', ''); - } else { - updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc'); - } - }, [sorting]); + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] + ); return ( <> -
-
-
-
- - - -
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ {/* Search */} +
-
- -
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(banks) ? banks?.data : []} - columns={banksColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(banks) ? banks?.meta?.page : 0} - totalItems={isResponseSuccess(banks) ? banks?.meta?.total_results : 0} - onPageChange={setPage} - isLoading={isLoading} - sorting={sorting} - setSorting={setSorting} - className={{ - containerClassName: cn({ - 'mb-20': isResponseSuccess(banks) && banks?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(banks) ? banks?.data : []} + columns={banksColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(banks) ? banks?.meta?.page : 0} + totalItems={ + isResponseSuccess(banks) ? banks?.meta?.total_results : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': isResponseSuccess(banks) && banks?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
; deleteClickHandler: () => void; }) => { + const popoverId = `customer#${props.row.original.id}`; + const popoverAnchorName = `--anchor-customer#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - - - - - - - - +
+ + + + + +
+ + + + + + + + + +
+
+
); }; @@ -83,16 +108,17 @@ const CustomersTable = () => { setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ - initial: { search: '', nameSort: '', picSort: '' }, + initial: { + search: '', + }, paramMap: { page: 'page', pageSize: 'limit', - nameSort: 'sort_name', - picSort: 'sort_pic', }, }); - // Fetch Data + const [sorting, setSorting] = useState([]); + const { data: customers, isLoading, @@ -102,87 +128,16 @@ const CustomersTable = () => { CustomerApi.getAllFetcher ); - // State const deleteModal = useModal(); const [selectedCustomer, setSelectedCustomer] = useState< Customer | undefined >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - // Columns Definition - const customersColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - accessorKey: 'pic', - header: 'PIC', - cell: (props) => props.row.original.pic.name, - }, - { - accessorKey: 'type', - header: 'Type', - cell: (props) => props.row.original.type, - }, - { - accessorKey: 'phone', - header: 'Phone', - }, - { - accessorKey: 'email', - header: 'Email', - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedCustomer(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; - - // Handler const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -202,81 +157,132 @@ const CustomersTable = () => { toast.success('Successfully delete Customer!'); setIsDeleteLoading(false); }; - const searchChangeHandler = (e: React.ChangeEvent) => { - updateFilter('search', e.target.value); - }; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; - setPageSize(newVal.value as number); - }; + + const customersColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorFn: (row) => row.pic?.name ?? '-', + header: 'PIC', + }, + { + accessorKey: 'type', + header: 'Type', + }, + { + accessorKey: 'phone', + header: 'Phone', + }, + { + accessorKey: 'email', + header: 'Email', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; + + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; + + const deleteClickHandler = () => { + setSelectedCustomer(props.row.original); + deleteModal.openModal(); + }; + + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] + ); return ( <> -
-
-
-
- - - -
- - +
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + +
-
- + + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(customers) ? customers?.data : []} - columns={customersColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(customers) ? customers?.meta?.page : 0} - totalItems={ - isResponseSuccess(customers) ? customers?.meta?.total_results : 0 - } - onPageChange={setPage} - isLoading={isLoading} - className={{ - containerClassName: cn({ - 'mb-20': - isResponseSuccess(customers) && customers?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(customers) ? customers?.data : []} + columns={customersColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(customers) ? customers?.meta?.page : 0} + totalItems={ + isResponseSuccess(customers) ? customers?.meta?.total_results : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': + isResponseSuccess(customers) && customers?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
; deleteClickHandler: () => void; }) => { + const popoverId = `flock#${props.row.original.id}`; + const popoverAnchorName = `--anchor-flock#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - - - - - - - - +
+ + + + + +
+ + + + + + + + + +
+
+
); }; @@ -93,15 +108,17 @@ const FlockTable = () => { setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ - initial: { search: '', nameSort: '' }, + initial: { + search: '', + }, paramMap: { page: 'page', pageSize: 'limit', - nameSort: 'sort_name', }, }); - // Fetch Data + const [sorting, setSorting] = useState([]); + const { data: flocks, isLoading, @@ -111,74 +128,16 @@ const FlockTable = () => { FlockApi.getAllFetcher ); - // State const deleteModal = useModal(); const [selectedFlock, setSelectedFlock] = useState( undefined ); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - // Columns Definition - const flocksColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - accessorKey: 'created_at', - header: 'Dibuat pada', - cell: (props) => - new Date(props.row.original.created_at).toLocaleDateString(), - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedFlock(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; - - // Handler const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -196,85 +155,128 @@ const FlockTable = () => { toast.success('Successfully delete Flock!'); setIsDeleteLoading(false); }; - const searchChangeHandler = (e: React.ChangeEvent) => { - updateFilter('search', e.target.value); - }; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; - setPageSize(newVal.value as number); - }; + + const flocksColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorKey: 'created_at', + header: 'Dibuat pada', + cell: (props) => + new Date(props.row.original.created_at).toLocaleDateString('id-ID'), + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; + + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; + + const deleteClickHandler = () => { + setSelectedFlock(props.row.original); + deleteModal.openModal(); + }; + + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] + ); return ( <> -
-
-
-
- - - -
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ {/* Search */} +
-
- -
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(flocks) ? flocks?.data : []} - columns={flocksColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(flocks) ? flocks?.meta?.page : 0} - totalItems={ - isResponseSuccess(flocks) ? flocks?.meta?.total_results : 0 - } - onPageChange={setPage} - isLoading={isLoading} - className={{ - containerClassName: cn({ - 'mb-20': isResponseSuccess(flocks) && flocks?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(flocks) ? flocks?.data : []} + columns={flocksColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(flocks) ? flocks?.meta?.page : 0} + totalItems={ + isResponseSuccess(flocks) ? flocks?.meta?.total_results : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': + isResponseSuccess(flocks) && flocks?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
+ ; deleteClickHandler: () => void; }) => { + const popoverId = `kandang#${props.row.original.id}`; + const popoverAnchorName = `--anchor-kandang#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - +
+ + + - - - - - - - - + +
+ + + + + + + + + +
+
+
); }; @@ -94,21 +110,15 @@ const KandangsTable = () => { } = useTableFilter({ initial: { search: '', - nameSort: '', - locationSort: '', - capacitySort: '', - picSort: '', }, paramMap: { page: 'page', pageSize: 'limit', - nameSort: 'sort_name', - locationSort: 'sort_location', - capacitySort: 'sort_capacity', - picSort: ' sort_pic', }, }); + const [sorting, setSorting] = useState([]); + const { data: kandangs, isLoading, @@ -119,82 +129,14 @@ const KandangsTable = () => { ); const deleteModal = useModal(); - const [selectedKandang, setSelectedKandang] = useState( undefined ); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [sorting, setSorting] = useState([]); - - const kandangsColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - accessorKey: 'location', - header: 'Lokasi', - cell: (props) => props.row.original.location.name, - }, - { - accessorKey: 'capacity', - header: 'Kapasitas', - cell: (props) => formatNumber(props.row.original.capacity ?? 0), - }, - { - accessorKey: 'pic', - header: 'PIC', - cell: (props) => props.row.original.pic.name, - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedKandang(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -216,114 +158,128 @@ const KandangsTable = () => { setIsDeleteLoading(false); }; - const searchChangeHandler: ChangeEventHandler = (e) => { - updateFilter('search', e.target.value); - }; + const kandangsColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorFn: (row) => row.location?.name ?? '-', + header: 'Lokasi', + }, + { + accessorKey: 'capacity', + header: 'Kapasitas', + cell: (props) => formatNumber(props.row.original.capacity ?? 0), + }, + { + accessorFn: (row) => row.pic?.name ?? '-', + header: 'PIC', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - setPageSize(newVal.value as number); - }; + const deleteClickHandler = () => { + setSelectedKandang(props.row.original); + deleteModal.openModal(); + }; - const updateSortingFilter = useCallback( - ( - sortName: Exclude, - sortFilter: ColumnSort | undefined - ) => { - if (!sortFilter) { - updateFilter(sortName, ''); - } else { - updateFilter(sortName, sortFilter.desc ? 'desc' : 'asc'); - } - }, - [updateFilter] + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] ); - // track sorting - useEffect(() => { - const nameSortFilter = sorting.find((sortItem) => sortItem.id === 'name'); - const locationSortFilter = sorting.find( - (sortItem) => sortItem.id === 'location' - ); - const picSortFilter = sorting.find((sortItem) => sortItem.id === 'pic'); - - updateSortingFilter('nameSort', nameSortFilter); - updateSortingFilter('locationSort', locationSortFilter); - updateSortingFilter('picSort', picSortFilter); - }, [sorting, updateSortingFilter]); - return ( <> -
-
-
-
-
- - - -
-
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ {/* Search */} +
-
- -
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(kandangs) ? kandangs?.data : []} - columns={kandangsColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(kandangs) ? kandangs?.meta?.page : 0} - totalItems={ - isResponseSuccess(kandangs) ? kandangs?.meta?.total_results : 0 - } - onPageChange={setPage} - isLoading={isLoading} - sorting={sorting} - setSorting={setSorting} - className={{ - containerClassName: cn({ - 'mb-20': - isResponseSuccess(kandangs) && kandangs?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(kandangs) ? kandangs?.data : []} + columns={kandangsColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(kandangs) ? kandangs?.meta?.page : 0} + totalItems={ + isResponseSuccess(kandangs) ? kandangs?.meta?.total_results : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': + isResponseSuccess(kandangs) && kandangs?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
; deleteClickHandler: () => void; }) => { + const popoverId = `location#${props.row.original.id}`; + const popoverAnchorName = `--anchor-location#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - +
+ + + - - - - - - - - + +
+ + + + + + + + + +
+
+
); }; @@ -92,16 +108,17 @@ const LocationsTable = () => { setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ - initial: { search: '', nameSort: '', addressSort: '', areaSort: '' }, + initial: { + search: '', + }, paramMap: { page: 'page', pageSize: 'limit', - nameSort: 'sort_name', - addressSort: 'sort_address', - areaSort: ' sort_area', }, }); + const [sorting, setSorting] = useState([]); + const { data: locations, isLoading, @@ -112,76 +129,14 @@ const LocationsTable = () => { ); const deleteModal = useModal(); - const [selectedLocation, setSelectedLocation] = useState< Location | undefined >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [sorting, setSorting] = useState([]); - - const locationsColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - accessorKey: 'address', - header: 'Alamat', - }, - { - accessorKey: 'area', - header: 'Area', - cell: (props) => props.row.original.area.name, - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedLocation(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -203,114 +158,123 @@ const LocationsTable = () => { setIsDeleteLoading(false); }; - const searchChangeHandler: ChangeEventHandler = (e) => { - updateFilter('search', e.target.value); - }; + const locationsColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorKey: 'address', + header: 'Alamat', + }, + { + accessorFn: (row) => row.area?.name ?? '-', + header: 'Area', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - setPageSize(newVal.value as number); - }; + const deleteClickHandler = () => { + setSelectedLocation(props.row.original); + deleteModal.openModal(); + }; - const updateSortingFilter = useCallback( - ( - sortName: Exclude, - sortFilter: ColumnSort | undefined - ) => { - if (!sortFilter) { - updateFilter(sortName, ''); - } else { - updateFilter(sortName, sortFilter.desc ? 'desc' : 'asc'); - } - }, - [updateFilter] + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] ); - // track sorting - useEffect(() => { - const nameSortFilter = sorting.find((sortItem) => sortItem.id === 'name'); - const addressSortFilter = sorting.find( - (sortItem) => sortItem.id === 'address' - ); - const areaSortFilter = sorting.find((sortItem) => sortItem.id === 'area'); - - updateSortingFilter('nameSort', nameSortFilter); - updateSortingFilter('addressSort', addressSortFilter); - updateSortingFilter('areaSort', areaSortFilter); - }, [sorting, updateSortingFilter]); - return ( <> -
-
-
-
-
- - - -
-
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ {/* Search */} +
-
- -
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(locations) ? locations?.data : []} - columns={locationsColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(locations) ? locations?.meta?.page : 0} - totalItems={ - isResponseSuccess(locations) ? locations?.meta?.total_results : 0 - } - onPageChange={setPage} - isLoading={isLoading} - sorting={sorting} - setSorting={setSorting} - className={{ - containerClassName: cn({ - 'mb-20': - isResponseSuccess(locations) && locations?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(locations) ? locations?.data : []} + columns={locationsColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(locations) ? locations?.meta?.page : 0} + totalItems={ + isResponseSuccess(locations) ? locations?.meta?.total_results : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': + isResponseSuccess(locations) && locations?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
{ className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm' > - Tambah Nonstock + Add Nonstock
diff --git a/src/components/pages/master-data/product-category/ProductCategoryTable.tsx b/src/components/pages/master-data/product-category/ProductCategoryTable.tsx index 11199c73..3a872b7f 100644 --- a/src/components/pages/master-data/product-category/ProductCategoryTable.tsx +++ b/src/components/pages/master-data/product-category/ProductCategoryTable.tsx @@ -1,6 +1,12 @@ 'use client'; -import { ChangeEventHandler, useEffect, useRef, useState } from 'react'; +import { + ChangeEventHandler, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -11,11 +17,9 @@ import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import Button from '@/components/Button'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; -import SelectInput, { OptionType } from '@/components/input/SelectInput'; -import RowDropdownOptions from '@/components/table/RowDropdownOptions'; -import RowCollapseOptions from '@/components/table/RowCollapseOptions'; -import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RequirePermission from '@/components/helper/RequirePermission'; +import PopoverButton from '@/components/popover/PopoverButton'; +import PopoverContent from '@/components/popover/PopoverContent'; import { ProductCategory } from '@/types/api/master-data/product-category'; import { ProductCategoryApi } from '@/services/api/master-data'; @@ -23,60 +27,83 @@ import { cn } from '@/lib/helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { useUiStore } from '@/stores/ui/ui.store'; -import { ROWS_OPTIONS } from '@/config/constant'; const RowOptionsMenu = ({ - type = 'dropdown', + popoverPosition = 'bottom', props, deleteClickHandler, }: { - type: 'dropdown' | 'collapse'; + popoverPosition: 'bottom' | 'top'; props: CellContext; deleteClickHandler: () => void; }) => { + const popoverId = `product-category#${props.row.original.id}`; + const popoverAnchorName = `--anchor-product-category#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - +
+ + + - - - - - - - - + +
+ + + + + + + + + +
+
+
); }; @@ -91,10 +118,17 @@ const ProductCategoryTable = () => { setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ - initial: { search: searchValue, nameSort: '' }, - paramMap: { page: 'page', pageSize: 'limit', nameSort: 'sort_name' }, + initial: { + search: searchValue, + }, + paramMap: { + page: 'page', + pageSize: 'limit', + }, }); + const [sorting, setSorting] = useState([]); + const { data: productCategories, isLoading, @@ -105,71 +139,15 @@ const ProductCategoryTable = () => { ); const deleteModal = useModal(); - const [selectedProductCategory, setSelectedProductCategory] = useState< ProductCategory | undefined >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [sorting, setSorting] = useState([]); - - const productCategoryColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'code', - header: 'Code', - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedProductCategory(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; + const searchChangeHandler: ChangeEventHandler = (e) => { + setSearchValue(e.target.value); + updateFilter('search', e.target.value); + }; const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -191,15 +169,51 @@ const ProductCategoryTable = () => { setIsDeleteLoading(false); }; - const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); - }; + const productCategoryColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'code', + header: 'Code', + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; - setPageSize(newVal.value as number); - }; + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; + + const deleteClickHandler = () => { + setSelectedProductCategory(props.row.original); + deleteModal.openModal(); + }; + + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] + ); useEffect(() => { // Store current path on mount @@ -223,91 +237,86 @@ const ProductCategoryTable = () => { }; }, [resetSearchValue]); - useEffect(() => { - const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name'); - if (!isNameSorted) { - updateFilter('nameSort', ''); - } else { - updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc'); - } - }, [sorting, updateFilter]); - return ( <> -
-
-
-
- - - -
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ + {/* Search */} +
-
-
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={ - isResponseSuccess(productCategories) ? productCategories?.data : [] - } - columns={productCategoryColumns} - pageSize={tableFilterState.pageSize} - page={ - isResponseSuccess(productCategories) - ? productCategories?.meta?.page - : 0 - } - totalItems={ - isResponseSuccess(productCategories) - ? productCategories?.meta?.total_results - : 0 - } - onPageChange={setPage} - isLoading={isLoading} - sorting={sorting} - setSorting={setSorting} - className={{ - containerClassName: cn({ - 'mb-20': - isResponseSuccess(productCategories) && - productCategories?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + + {/* Table Section */} +
+ + data={ + isResponseSuccess(productCategories) + ? productCategories?.data + : [] + } + columns={productCategoryColumns} + pageSize={tableFilterState.pageSize} + page={ + isResponseSuccess(productCategories) + ? productCategories?.meta?.page + : 0 + } + totalItems={ + isResponseSuccess(productCategories) + ? productCategories?.meta?.total_results + : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': + isResponseSuccess(productCategories) && + productCategories?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
+ { className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm' > - Tambah Produk + Add Product
diff --git a/src/components/pages/master-data/production-standard/ProductionStandardTable.tsx b/src/components/pages/master-data/production-standard/ProductionStandardTable.tsx index a8df6ae8..09e83fd1 100644 --- a/src/components/pages/master-data/production-standard/ProductionStandardTable.tsx +++ b/src/components/pages/master-data/production-standard/ProductionStandardTable.tsx @@ -1,92 +1,121 @@ 'use client'; -import Button from '@/components/Button'; -import Table, { TABLE_DEFAULT_STYLING } from '@/components/Table'; -import { ProductionStandard } from '@/types/api/master-data/production-standard'; -import { Icon } from '@iconify/react'; +import { useMemo, useState } from 'react'; import useSWR from 'swr'; -import { ProductionStandardApi } from '@/services/api/master-data'; -import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; -import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; -import { CellContext } from '@tanstack/react-table'; -import { useModal } from '@/components/Modal'; -import { useState } from 'react'; -import RowDropdownOptions from '@/components/table/RowDropdownOptions'; -import RowCollapseOptions from '@/components/table/RowCollapseOptions'; -import ConfirmationModal from '@/components/modal/ConfirmationModal'; +import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; -import { cn } from '@/lib/helper'; + +import { Icon } from '@iconify/react'; +import Table from '@/components/Table'; +import Button from '@/components/Button'; +import { useModal } from '@/components/Modal'; +import ConfirmationModal from '@/components/modal/ConfirmationModal'; import RequirePermission from '@/components/helper/RequirePermission'; +import PopoverButton from '@/components/popover/PopoverButton'; +import PopoverContent from '@/components/popover/PopoverContent'; + +import { ProductionStandard } from '@/types/api/master-data/production-standard'; +import { ProductionStandardApi } from '@/services/api/master-data'; +import { cn } from '@/lib/helper'; +import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; const RowOptionsMenu = ({ - type = 'dropdown', + popoverPosition = 'bottom', props, deleteClickHandler, }: { - type: 'dropdown' | 'collapse'; + popoverPosition: 'bottom' | 'top'; props: CellContext; deleteClickHandler: () => void; }) => { + const popoverId = `production-standard#${props.row.original.id}`; + const popoverAnchorName = `--anchor-production-standard#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - +
+ + + - - - - - - - - + +
+ + + + + + + + + +
+
+
); }; const ProductionStandardTable = () => { - const deleteModal = useModal(); + const [sorting, setSorting] = useState([]); + const { + data: productionStandards, + isLoading, + mutate: refreshProductionStandards, + } = useSWR( + `${ProductionStandardApi.basePath}`, + ProductionStandardApi.getAllFetcher + ); + + const deleteModal = useModal(); const [selectedProductionStandard, setSelectedProductionStandard] = useState< ProductionStandard | undefined >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const { data: productionStandards, mutate: refreshProductionStandards } = - useSWR( - `${ProductionStandardApi.basePath}`, - ProductionStandardApi.getAllFetcher - ); - const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -107,112 +136,107 @@ const ProductionStandardTable = () => { setIsDeleteLoading(false); }; + const productionStandardColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => props.row.index + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorFn: (row) => row.project_category ?? '-', + header: 'Kategori', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; + + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; + + const deleteClickHandler = () => { + setSelectedProductionStandard(props.row.original); + deleteModal.openModal(); + }; + + return ( + + ); + }, + }, + ], + [deleteModal] + ); + return ( <> -
-
- - - +
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
- + + {/* Table Section */} +
data={ isResponseSuccess(productionStandards) ? productionStandards.data : [] } - columns={[ - { - header: 'No', - accessorFn: (row, index) => index + 1, - }, - { - header: 'Nama', - accessorKey: 'name', - }, - { - header: 'Kategori', - accessorFn: (row) => row.project_category, - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = - props.table.getPaginationRowModel().rows.length; - const currentPageRows = - props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = - currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedProductionStandard(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]} + columns={productionStandardColumns} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} className={{ - headerColumnClassName: cn( - TABLE_DEFAULT_STYLING.headerColumnClassName, - 'last:flex last:flex-row last:justify-end' - ), - bodyColumnClassName: cn( - TABLE_DEFAULT_STYLING.bodyColumnClassName, - 'last:flex last:flex-row last:justify-end' - ), + containerClassName: cn('p-3 mb-0', { + 'w-full': + isResponseSuccess(productionStandards) && + productionStandards?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', }} /> - +
- - - + + ); }; diff --git a/src/components/pages/master-data/supplier/SupplierTable.tsx b/src/components/pages/master-data/supplier/SupplierTable.tsx index 2620c9e6..e5b225ec 100644 --- a/src/components/pages/master-data/supplier/SupplierTable.tsx +++ b/src/components/pages/master-data/supplier/SupplierTable.tsx @@ -1,87 +1,102 @@ 'use client'; -import Button from '@/components/Button'; +import { ChangeEventHandler, useMemo, useState } from 'react'; +import useSWR from 'swr'; +import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; +import toast from 'react-hot-toast'; + +import { Icon } from '@iconify/react'; +import Table from '@/components/Table'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; -import SelectInput, { OptionType } from '@/components/input/SelectInput'; +import Button from '@/components/Button'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; -import Table from '@/components/Table'; -import RowCollapseOptions from '@/components/table/RowCollapseOptions'; -import RowDropdownOptions from '@/components/table/RowDropdownOptions'; -import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RequirePermission from '@/components/helper/RequirePermission'; -import { ROWS_OPTIONS } from '@/config/constant'; -import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; -import { cn } from '@/lib/helper'; -import { SupplierApi } from '@/services/api/master-data'; -import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { Supplier } from '@/types/api/master-data/supplier'; -import { Icon } from '@iconify/react'; -import { CellContext, ColumnDef } from '@tanstack/react-table'; -import { useState } from 'react'; -import toast from 'react-hot-toast'; -import useSWR from 'swr'; +import PopoverButton from '@/components/popover/PopoverButton'; +import PopoverContent from '@/components/popover/PopoverContent'; -const RowOptions = ({ - type = 'dropdown', +import { Supplier } from '@/types/api/master-data/supplier'; +import { SupplierApi } from '@/services/api/master-data'; +import { cn } from '@/lib/helper'; +import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; +import { useTableFilter } from '@/services/hooks/useTableFilter'; + +const RowOptionsMenu = ({ + popoverPosition = 'bottom', props, deleteClickHandler, }: { - type: 'dropdown' | 'collapse'; + popoverPosition: 'bottom' | 'top'; props: CellContext; deleteClickHandler: () => void; }) => { + const popoverId = `supplier#${props.row.original.id}`; + const popoverAnchorName = `--anchor-supplier#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - - - - - - - - +
+ + + + + +
+ + + + + + + + + +
+
+
); }; @@ -93,15 +108,17 @@ const SuppliersTable = () => { setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ - initial: { search: '', nameSort: '' }, + initial: { + search: '', + }, paramMap: { page: 'page', pageSize: 'limit', - nameSort: 'sort_name', }, }); - // Fetch Data + const [sorting, setSorting] = useState([]); + const { data: suppliers, isLoading, @@ -111,97 +128,16 @@ const SuppliersTable = () => { SupplierApi.getAllFetcher ); - // State const deleteModal = useModal(); const [selectedSupplier, setSelectedSupplier] = useState< Supplier | undefined >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - // Columns Definition - const suppliersColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - accessorKey: 'alias', - header: 'Alias', - }, - { - accessorKey: 'pic', - header: 'Nama PIC', - }, - { - accessorKey: 'category', - header: 'Kategori', - }, - { - accessorKey: 'type', - header: 'Tipe', - }, - { - accessorKey: 'phone', - header: 'No. Telp', - }, - { - accessorKey: 'email', - header: 'Email', - }, - { - accessorKey: 'address', - header: 'Alamat', - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedSupplier(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; - - // Handler const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -221,82 +157,146 @@ const SuppliersTable = () => { toast.success('Successfully delete Supplier!'); setIsDeleteLoading(false); }; - const searchChangeHandler = (e: React.ChangeEvent) => { - updateFilter('search', e.target.value); - }; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; - setPageSize(newVal.value as number); - }; + + const suppliersColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorKey: 'alias', + header: 'Alias', + }, + { + accessorKey: 'pic', + header: 'Nama PIC', + }, + { + accessorKey: 'category', + header: 'Kategori', + }, + { + accessorKey: 'type', + header: 'Tipe', + }, + { + accessorKey: 'phone', + header: 'No. Telp', + }, + { + accessorKey: 'email', + header: 'Email', + }, + { + accessorKey: 'address', + header: 'Alamat', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; + + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; + + const deleteClickHandler = () => { + setSelectedSupplier(props.row.original); + deleteModal.openModal(); + }; + + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] + ); return ( <> -
-
-
-
- - - -
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ {/* Search */} +
-
- -
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(suppliers) ? suppliers?.data : []} - columns={suppliersColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(suppliers) ? suppliers?.meta?.page : 0} - totalItems={ - isResponseSuccess(suppliers) ? suppliers?.meta?.total_results : 0 - } - onPageChange={setPage} - isLoading={isLoading} - className={{ - containerClassName: cn({ - 'mb-20': - isResponseSuccess(suppliers) && suppliers?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(suppliers) ? suppliers?.data : []} + columns={suppliersColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(suppliers) ? suppliers?.meta?.page : 0} + totalItems={ + isResponseSuccess(suppliers) ? suppliers?.meta?.total_results : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': + isResponseSuccess(suppliers) && suppliers?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
+ ; deleteClickHandler: () => void; }) => { + const popoverId = `uom#${props.row.original.id}`; + const popoverAnchorName = `--anchor-uom#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - +
+ + + - - - - - - - - + +
+ + + + + + + + + +
+
+
); }; @@ -87,10 +108,17 @@ const UomsTable = () => { setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ - initial: { search: '', nameSort: '' }, - paramMap: { page: 'page', pageSize: 'limit', nameSort: 'sort_name' }, + initial: { + search: '', + }, + paramMap: { + page: 'page', + pageSize: 'limit', + }, }); + const [sorting, setSorting] = useState([]); + const { data: uoms, isLoading, @@ -101,65 +129,12 @@ const UomsTable = () => { ); const deleteModal = useModal(); - const [selectedUom, setSelectedUom] = useState(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [sorting, setSorting] = useState([]); - - const uomsColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedUom(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -179,93 +154,112 @@ const UomsTable = () => { setIsDeleteLoading(false); }; - const searchChangeHandler: ChangeEventHandler = (e) => { - updateFilter('search', e.target.value); - }; + const uomsColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - setPageSize(newVal.value as number); - }; + const deleteClickHandler = () => { + setSelectedUom(props.row.original); + deleteModal.openModal(); + }; - // track sorting - useEffect(() => { - const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name'); - - if (!isNameSorted) { - updateFilter('nameSort', ''); - } else { - updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc'); - } - }, [sorting, updateFilter]); + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] + ); return ( <> -
-
-
-
- - - -
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ {/* Search */} +
-
- -
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(uoms) ? uoms?.data : []} - columns={uomsColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(uoms) ? uoms?.meta?.page : 0} - totalItems={isResponseSuccess(uoms) ? uoms?.meta?.total_results : 0} - onPageChange={setPage} - isLoading={isLoading} - sorting={sorting} - setSorting={setSorting} - className={{ - containerClassName: cn({ - 'mb-20': isResponseSuccess(uoms) && uoms?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(uoms) ? uoms?.data : []} + columns={uomsColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(uoms) ? uoms?.meta?.page : 0} + totalItems={isResponseSuccess(uoms) ? uoms?.meta?.total_results : 0} + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': isResponseSuccess(uoms) && uoms?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +
; deleteClickHandler: () => void; }) => { + const popoverId = `warehouse#${props.row.original.id}`; + const popoverAnchorName = `--anchor-warehouse#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + return ( - - - - +
+ + + - - - - - - - - + +
+ + + + + + + + + +
+
+
); }; @@ -94,23 +110,15 @@ const WarehousesTable = () => { } = useTableFilter({ initial: { search: '', - nameSort: '', - typeSort: '', - areaSort: '', - locationSort: '', - kandangSort: '', }, paramMap: { page: 'page', pageSize: 'limit', - nameSort: 'sort_name', - typeSort: 'sort_type', - areaSort: ' sort_area', - locationSort: ' sort_location', - kandangSort: ' sort_kandang', }, }); + const [sorting, setSorting] = useState([]); + const { data: warehouses, isLoading, @@ -121,101 +129,14 @@ const WarehousesTable = () => { ); const deleteModal = useModal(); - const [selectedWarehouse, setSelectedWarehouse] = useState< Warehouse | undefined >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [sorting, setSorting] = useState([]); - - const warehousesColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - accessorKey: 'type', - header: 'Tipe', - }, - { - accessorKey: 'area', - header: 'Area', - cell: (props) => props.row.original.area.name, - }, - { - accessorKey: 'location', - header: 'Lokasi', - cell: (props) => { - if ( - props.row.original.type === 'LOKASI' || - props.row.original.type === 'KANDANG' - ) { - return props.row.original.location.name; - } else { - return '-'; - } - }, - }, - { - accessorKey: 'kandang', - header: 'Kandang', - cell: (props) => { - if (props.row.original.type === 'KANDANG') { - return props.row.original.kandang.name; - } else { - return '-'; - } - }, - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedWarehouse(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; + const searchChangeHandler: ChangeEventHandler = (e) => { + updateFilter('search', e.target.value); + }; const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -237,118 +158,149 @@ const WarehousesTable = () => { setIsDeleteLoading(false); }; - const searchChangeHandler: ChangeEventHandler = (e) => { - updateFilter('search', e.target.value); - }; + const warehousesColumns: ColumnDef[] = useMemo( + () => [ + { + header: 'No', + cell: (props) => + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorKey: 'type', + header: 'Tipe', + }, + { + accessorFn: (row) => row.area?.name ?? '-', + header: 'Area', + }, + { + accessorKey: 'location', + header: 'Lokasi', + cell: (props) => { + if ( + props.row.original.type === 'LOKASI' || + props.row.original.type === 'KANDANG' + ) { + return props.row.original.location?.name ?? '-'; + } + return '-'; + }, + }, + { + accessorKey: 'kandang', + header: 'Kandang', + cell: (props) => { + if (props.row.original.type === 'KANDANG') { + return props.row.original.kandang?.name ?? '-'; + } + return '-'; + }, + }, + { + header: 'Aksi', + cell: (props: CellContext) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - setPageSize(newVal.value as number); - }; + const deleteClickHandler = () => { + setSelectedWarehouse(props.row.original); + deleteModal.openModal(); + }; - const updateSortingFilter = useCallback( - ( - sortName: Exclude, - sortFilter: ColumnSort | undefined - ) => { - if (!sortFilter) { - updateFilter(sortName, ''); - } else { - updateFilter(sortName, sortFilter.desc ? 'desc' : 'asc'); - } - }, - [updateFilter] + return ( + + ); + }, + }, + ], + [tableFilterState.pageSize, tableFilterState.page, deleteModal] ); - // track sorting - useEffect(() => { - const nameSortFilter = sorting.find((sortItem) => sortItem.id === 'name'); - const typeSortFilter = sorting.find((sortItem) => sortItem.id === 'type'); - const areaSortFilter = sorting.find((sortItem) => sortItem.id === 'area'); - const locationSortFilter = sorting.find( - (sortItem) => sortItem.id === 'location' - ); - const kandangSortFilter = sorting.find( - (sortItem) => sortItem.id === 'kandang' - ); - - updateSortingFilter('nameSort', nameSortFilter); - updateSortingFilter('typeSort', typeSortFilter); - updateSortingFilter('areaSort', areaSortFilter); - updateSortingFilter('locationSort', locationSortFilter); - updateSortingFilter('kandangSort', kandangSortFilter); - }, [sorting, updateSortingFilter]); - return ( <> -
-
-
-
- - - -
+
+ {/* Header Section */} +
+ {/* Action Buttons */} +
+ + + +
+ {/* Search */} +
-
- -
- + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', }} - onChange={pageSizeChangeHandler} - className={{ wrapper: 'max-w-28' }} />
- - data={isResponseSuccess(warehouses) ? warehouses?.data : []} - columns={warehousesColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(warehouses) ? warehouses?.meta?.page : 0} - totalItems={ - isResponseSuccess(warehouses) ? warehouses?.meta?.total_results : 0 - } - onPageChange={setPage} - isLoading={isLoading} - sorting={sorting} - setSorting={setSorting} - className={{ - containerClassName: cn({ - 'mb-20': - isResponseSuccess(warehouses) && warehouses?.data?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - }} - /> + {/* Table Section */} +
+ + data={isResponseSuccess(warehouses) ? warehouses?.data : []} + columns={warehousesColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(warehouses) ? warehouses?.meta?.page : 0} + totalItems={ + isResponseSuccess(warehouses) + ? warehouses?.meta?.total_results + : 0 + } + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + className={{ + containerClassName: cn('p-3 mb-0', { + 'w-full': + isResponseSuccess(warehouses) && + warehouses?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> +