- {getAvatar({ avatar: row.original.avatar, customer: row.original.customer })}
-
-
- {row.original.customer}
-
- {row.original.email}
-
+
+ )
+ }),
+ columnHelper.accessor('actions', {
+ header: 'Actions',
+ cell: ({ row }) => (
+
+ {
+ dispatch(setCustomer(row.original))
+ setCustomerUserOpen(true)
+ }}>
+
+
+ {
+ setOpenConfirm(true)
+ setCustomerId(row.original.id)
+ }
+ }
+ },
+ { text: 'Duplicate', icon: 'tabler-copy' }
+ ]}
+ />
- )
- }),
- columnHelper.accessor('customerId', {
- header: 'Customer Id',
- cell: ({ row }) =>
#{row.original.customerId}
- }),
- columnHelper.accessor('country', {
- header: 'Country',
- cell: ({ row }) => (
-
-

-
{row.original.country}
-
- )
- }),
- columnHelper.accessor('order', {
- header: 'Orders',
- cell: ({ row }) =>
{row.original.order}
- }),
- columnHelper.accessor('totalSpent', {
- header: 'Total Spent',
- cell: ({ row }) => (
-
- ${row.original.totalSpent.toLocaleString()}
-
- )
+ ),
+ enableSorting: false
})
],
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -216,63 +234,41 @@ const CustomerListTable = ({ customerData }: { customerData?: Customer[] }) => {
)
const table = useReactTable({
- data: data as Customer[],
+ data: customers as Customer[],
columns,
filterFns: {
fuzzy: fuzzyFilter
},
state: {
rowSelection,
- globalFilter
- },
- initialState: {
pagination: {
- pageSize: 10
+ pageIndex: currentPage,
+ pageSize
}
},
enableRowSelection: true, //enable row selection for all rows
- // enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row
- globalFilterFn: fuzzyFilter,
onRowSelectionChange: setRowSelection,
getCoreRowModel: getCoreRowModel(),
- onGlobalFilterChange: setGlobalFilter,
- getFilteredRowModel: getFilteredRowModel(),
- getSortedRowModel: getSortedRowModel(),
- getPaginationRowModel: getPaginationRowModel(),
- getFacetedRowModel: getFacetedRowModel(),
- getFacetedUniqueValues: getFacetedUniqueValues(),
- getFacetedMinMaxValues: getFacetedMinMaxValues()
+ // Disable client-side pagination since we're handling it server-side
+ manualPagination: true,
+ pageCount: Math.ceil(totalCount / pageSize)
})
- const getAvatar = (params: Pick
) => {
- const { avatar, customer } = params
-
- if (avatar) {
- return
- } else {
- return (
-
- {getInitials(customer as string)}
-
- )
- }
- }
-
return (
<>
setGlobalFilter(String(value))}
+ value={search}
+ onChange={value => setSearch(value as string)}
placeholder='Search'
className='max-sm:is-full'
/>
table.setPageSize(Number(e.target.value))}
+ value={pageSize}
+ onChange={handlePageSizeChange}
className='is-full sm:is-[70px]'
>
@@ -300,75 +296,110 @@ const CustomerListTable = ({ customerData }: { customerData?: Customer[] }) => {
-
-
- {table.getHeaderGroups().map(headerGroup => (
-
- {headerGroup.headers.map(header => (
- |
- {header.isPlaceholder ? null : (
- <>
-
- {flexRender(header.column.columnDef.header, header.getContext())}
- {{
- asc: ,
- desc:
- }[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
-
- >
- )}
- |
- ))}
-
- ))}
-
- {table.getFilteredRowModel().rows.length === 0 ? (
-
-
- |
- No data available
- |
-
-
- ) : (
-
- {table
- .getRowModel()
- .rows.slice(0, table.getState().pagination.pageSize)
- .map(row => {
- return (
-
- {row.getVisibleCells().map(cell => (
- | {flexRender(cell.column.columnDef.cell, cell.getContext())} |
- ))}
-
- )
- })}
-
- )}
-
+ {isLoading ? (
+
+ ) : (
+
+
+ {table.getHeaderGroups().map(headerGroup => (
+
+ {headerGroup.headers.map(header => (
+ |
+ {header.isPlaceholder ? null : (
+ <>
+
+ {flexRender(header.column.columnDef.header, header.getContext())}
+ {{
+ asc: ,
+ desc:
+ }[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
+
+ >
+ )}
+ |
+ ))}
+
+ ))}
+
+ {table.getFilteredRowModel().rows.length === 0 ? (
+
+
+ |
+ No data available
+ |
+
+
+ ) : (
+
+ {table
+ .getRowModel()
+ .rows.slice(0, table.getState().pagination.pageSize)
+ .map(row => {
+ return (
+
+ {row.getVisibleCells().map(cell => (
+ | {flexRender(cell.column.columnDef.cell, cell.getContext())} |
+ ))}
+
+ )
+ })}
+
+ )}
+
+ )}
+
+ {isFetching && !isLoading && (
+
+
+
+ )}
- {/* }
- count={table.getFilteredRowModel().rows.length}
- rowsPerPage={table.getState().pagination.pageSize}
- page={table.getState().pagination.pageIndex}
- onPageChange={(_, page) => {
- table.setPageIndex(page)
- }}
- /> */}
+
+ (
+
+ )}
+ count={totalCount}
+ rowsPerPage={pageSize}
+ page={currentPage}
+ onPageChange={handlePageChange}
+ onRowsPerPageChange={handlePageSizeChange}
+ rowsPerPageOptions={[10, 25, 50]}
+ disabled={isLoading}
+ />
- setCustomerUserOpen(!customerUserOpen)}
- setData={setData}
- customerData={data}
+
+ setCustomerUserOpen(!customerUserOpen)} />
+
+ setOpenConfirm(false)}
+ onConfirm={handleDelete}
+ isLoading={deleteCustomer.isPending}
+ title='Delete Customer'
+ message='Are you sure you want to delete this customer? This action cannot be undone.'
/>
>
)
diff --git a/src/views/apps/ecommerce/orders/list/OrderListTable.tsx b/src/views/apps/ecommerce/orders/list/OrderListTable.tsx
index 267a301..6393944 100644
--- a/src/views/apps/ecommerce/orders/list/OrderListTable.tsx
+++ b/src/views/apps/ecommerce/orders/list/OrderListTable.tsx
@@ -44,6 +44,7 @@ import { Box, CircularProgress } from '@mui/material'
import Loading from '../../../../../components/layout/shared/Loading'
import { useOrders } from '../../../../../services/queries/orders'
import { Order } from '../../../../../types/services/order'
+import { formatCurrency } from '../../../../../utils/transform'
declare module '@tanstack/table-core' {
interface FilterFns {
@@ -54,30 +55,6 @@ declare module '@tanstack/table-core' {
}
}
-type PayementStatusType = {
- text: string
- color: ThemeColor
- colorClassName: string
-}
-
-type StatusChipColorType = {
- color: ThemeColor
-}
-
-export const paymentStatus: { [key: number]: PayementStatusType } = {
- 1: { text: 'Paid', color: 'success', colorClassName: 'text-success' },
- 2: { text: 'Pending', color: 'warning', colorClassName: 'text-warning' },
- 3: { text: 'Cancelled', color: 'secondary', colorClassName: 'text-secondary' },
- 4: { text: 'Failed', color: 'error', colorClassName: 'text-error' }
-}
-
-export const statusChipColor: { [key: string]: StatusChipColorType } = {
- Delivered: { color: 'success' },
- 'Out for Delivery': { color: 'primary' },
- 'Ready to Pickup': { color: 'info' },
- Dispatched: { color: 'warning' }
-}
-
type ECommerceOrderTypeWithAction = Order & {
action?: string
}
@@ -132,10 +109,12 @@ const OrderListTable = () => {
const [rowSelection, setRowSelection] = useState({})
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(10)
+ const [search, setSearch] = useState('')
const { data, isLoading, error, isFetching } = useOrders({
page: currentPage,
- limit: pageSize
+ limit: pageSize,
+ search
})
// Hooks
@@ -152,13 +131,9 @@ const OrderListTable = () => {
const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
const newPageSize = parseInt(event.target.value, 10)
setPageSize(newPageSize)
- setCurrentPage(0) // Reset to first page
+ setCurrentPage(1) // Reset to first page
}, [])
- // Vars
- const paypal = '/images/apps/ecommerce/paypal.png'
- const mastercard = '/images/apps/ecommerce/mastercard.png'
-
const columns = useMemo[]>(
() => [
{
@@ -195,83 +170,39 @@ const OrderListTable = () => {
}),
columnHelper.accessor('table_number', {
header: 'Table',
- cell: ({ row }) => {row.original.table_number}
+ cell: ({ row }) => {row.original.table_number || '-'}
}),
columnHelper.accessor('order_type', {
header: 'Order Type',
cell: ({ row }) => {row.original.order_type}
}),
- // columnHelper.accessor('order_type', {
- // header: 'Customers',
- // cell: ({ row }) => (
- //
- // {getAvatar({ avatar: row.original.avatar, customer: row.original.customer })}
- //
- //
- // {row.original.customer}
- //
- // {row.original.email}
- //
- //
- // )
- // }),
- // columnHelper.accessor('payment', {
- // header: 'Payment',
- // cell: ({ row }) => (
- //
- //
- //
- // {paymentStatus[row.original.payment].text}
- //
- //
- // )
- // }),
columnHelper.accessor('status', {
header: 'Status',
- cell: ({ row }) =>
+ cell: ({ row }) => (
+
+ )
}),
columnHelper.accessor('subtotal', {
header: 'SubTotal',
- cell: ({ row }) => {row.original.subtotal}
+ cell: ({ row }) => {formatCurrency(row.original.subtotal)}
}),
columnHelper.accessor('total_amount', {
header: 'Total',
- cell: ({ row }) => {row.original.total_amount}
+ cell: ({ row }) => {formatCurrency(row.original.total_amount)}
}),
columnHelper.accessor('tax_amount', {
header: 'Tax',
- cell: ({ row }) => {row.original.tax_amount}
+ cell: ({ row }) => {formatCurrency(row.original.tax_amount)}
}),
columnHelper.accessor('discount_amount', {
header: 'Discount',
- cell: ({ row }) => {row.original.discount_amount}
+ cell: ({ row }) => {formatCurrency(row.original.discount_amount)}
}),
- // columnHelper.accessor('method', {
- // header: 'Method',
- // cell: ({ row }) => (
- //
- //
- //

- //
- //
- // {`...${row.original.method === 'mastercard' ? row.original.methodNumber : '@gmail.com'}`}
- //
- //
- // )
- // }),
columnHelper.accessor('action', {
header: 'Action',
cell: ({ row }) => (
@@ -329,34 +260,20 @@ const OrderListTable = () => {
pageCount: Math.ceil(totalCount / pageSize)
})
- const getAvatar = (params: Pick) => {
- const { avatar, customer } = params
-
- if (avatar) {
- return
- } else {
- return (
-
- {getInitials(customer as string)}
-
- )
- }
- }
-
return (
console.log('click')}
+ value={search}
+ onChange={value => setSearch(value as string)}
placeholder='Search Order'
className='sm:is-auto'
/>
table.setPageSize(Number(e.target.value))}
+ value={pageSize}
+ onChange={handlePageSizeChange}
className='is-[70px] max-sm:is-full'
>
diff --git a/src/views/apps/ecommerce/products/add/ProductInformation.tsx b/src/views/apps/ecommerce/products/add/ProductInformation.tsx
index d50a1e8..fc4cd37 100644
--- a/src/views/apps/ecommerce/products/add/ProductInformation.tsx
+++ b/src/views/apps/ecommerce/products/add/ProductInformation.tsx
@@ -154,7 +154,7 @@ const ProductInformation = () => {
immediatelyRender: false,
content: `
- ${description}
+ ${description || ''}
`
})
diff --git a/src/views/apps/ecommerce/products/add/ProductVariants.tsx b/src/views/apps/ecommerce/products/add/ProductVariants.tsx
index dfa6995..787c0ea 100644
--- a/src/views/apps/ecommerce/products/add/ProductVariants.tsx
+++ b/src/views/apps/ecommerce/products/add/ProductVariants.tsx
@@ -21,7 +21,11 @@ const ProductVariants = () => {
const { variants } = useSelector((state: RootState) => state.productReducer.productRequest)
const handleAddVariant = () => {
- dispatch(setProductField({ field: 'variants', value: [...variants, { name: '', cost: 0, price_modifier: 0 }] }))
+ if (!variants) {
+ dispatch(setProductField({ field: 'variants', value: [{ name: '', cost: 0, price_modifier: 0 }] }))
+ } else {
+ dispatch(setProductField({ field: 'variants', value: [...variants, { name: '', cost: 0, price_modifier: 0 }] }))
+ }
}
const handleRemoveVariant = (index: number) => {
@@ -44,47 +48,48 @@ const ProductVariants = () => {
- {variants && variants.map((variant, index) => (
-
-
-
- handleInputChange(index, e)}
- />
-
-
- handleInputChange(index, e)}
- />
-
-
-
+ {variants &&
+ variants.map((variant, index) => (
+
+
+
handleInputChange(index, e)}
/>
- handleRemoveVariant(index)} className='min-is-fit'>
-
-
-
+
+
+ handleInputChange(index, e)}
+ />
+
+
+
+ handleInputChange(index, e)}
+ />
+ handleRemoveVariant(index)} className='min-is-fit'>
+
+
+
+
-
- ))}
+ ))}
}>
Add Another Option
diff --git a/src/views/apps/ecommerce/products/category/ProductCategoryTable.tsx b/src/views/apps/ecommerce/products/category/ProductCategoryTable.tsx
index 6677cdd..e32db39 100644
--- a/src/views/apps/ecommerce/products/category/ProductCategoryTable.tsx
+++ b/src/views/apps/ecommerce/products/category/ProductCategoryTable.tsx
@@ -35,6 +35,7 @@ import { useCategoriesMutation } from '../../../../../services/mutations/categor
import { useCategories } from '../../../../../services/queries/categories'
import { Category } from '../../../../../types/services/category'
import EditCategoryDrawer from './EditCategoryDrawer'
+import { formatDate } from '../../../../../utils/transform'
declare module '@tanstack/table-core' {
interface FilterFns {
@@ -104,6 +105,7 @@ const ProductCategoryTable = () => {
const [categoryId, setCategoryId] = useState('')
const [openConfirm, setOpenConfirm] = useState(false)
const [currentCategory, setCurrentCategory] = useState()
+ const [search, setSearch] = useState('')
// Fetch products with pagination and search
const { data, isLoading, error, isFetching } = useCategories({
@@ -124,7 +126,7 @@ const ProductCategoryTable = () => {
const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
const newPageSize = parseInt(event.target.value, 10)
setPageSize(newPageSize)
- setCurrentPage(0) // Reset to first page
+ setCurrentPage(1) // Reset to first page
}, [])
const handleDelete = () => {
@@ -180,7 +182,7 @@ const ProductCategoryTable = () => {
}),
columnHelper.accessor('created_at', {
header: 'Created At',
- cell: ({ row }) => {row.original.created_at}
+ cell: ({ row }) => {formatDate(row.original.created_at)}
}),
columnHelper.accessor('actions', {
header: 'Actions',
@@ -247,16 +249,16 @@ const ProductCategoryTable = () => {
console.log(value)}
+ value={search}
+ onChange={value => setSearch(value as string)}
placeholder='Search Product'
className='max-sm:is-full'
/>
table.setPageSize(Number(e.target.value))}
+ value={pageSize}
+ onChange={handlePageSizeChange}
className='flex-auto max-sm:is-full sm:is-[70px]'
>
diff --git a/src/views/apps/ecommerce/products/detail/ProductDetail.tsx b/src/views/apps/ecommerce/products/detail/ProductDetail.tsx
index d78a096..fb5dfeb 100644
--- a/src/views/apps/ecommerce/products/detail/ProductDetail.tsx
+++ b/src/views/apps/ecommerce/products/detail/ProductDetail.tsx
@@ -1,19 +1,19 @@
'use client'
import {
- Avatar,
- Badge,
- Card,
- CardContent,
- CardMedia,
- Chip,
- Divider,
- Grid,
- List,
- ListItem,
- ListItemIcon,
- ListItemText,
- Typography
+ Avatar,
+ Badge,
+ Card,
+ CardContent,
+ CardMedia,
+ Chip,
+ Divider,
+ Grid,
+ List,
+ ListItem,
+ ListItemIcon,
+ ListItemText,
+ Typography
} from '@mui/material'
import { useParams } from 'next/navigation'
import React, { useEffect } from 'react'
@@ -22,6 +22,7 @@ import Loading from '../../../../../components/layout/shared/Loading'
import { setProduct } from '../../../../../redux-store/slices/product'
import { useProductById } from '../../../../../services/queries/products'
import { ProductVariant } from '../../../../../types/services/product'
+import { formatCurrency, formatDate } from '../../../../../utils/transform'
// Tabler icons (using class names)
const TablerIcon = ({ name, className = '' }: { name: string; className?: string }) => (
@@ -39,24 +40,6 @@ const ProductDetail = () => {
}
}, [product, dispatch])
- const formatCurrency = (amount: number) => {
- return new Intl.NumberFormat('id-ID', {
- style: 'currency',
- currency: 'IDR',
- minimumFractionDigits: 0
- }).format(amount)
- }
-
- const formatDate = (dateString: string) => {
- return new Date(dateString).toLocaleDateString('id-ID', {
- year: 'numeric',
- month: 'long',
- day: 'numeric',
- hour: '2-digit',
- minute: '2-digit'
- })
- }
-
const getBusinessTypeColor = (type: string) => {
switch (type.toLowerCase()) {
case 'restaurant':
@@ -83,6 +66,11 @@ const ProductDetail = () => {
}
}
+ const getPlainText = (html: string) => {
+ const doc = new DOMParser().parseFromString(html, 'text/html')
+ return doc.body.textContent || ''
+ }
+
if (isLoading) return
return (
@@ -126,7 +114,7 @@ const ProductDetail = () => {
{product.description && (
- {product.description}
+ {getPlainText(product.description)}
)}
diff --git a/src/views/apps/ecommerce/products/ingredient/ProductIngredientTable.tsx b/src/views/apps/ecommerce/products/ingredient/ProductIngredientTable.tsx
new file mode 100644
index 0000000..2cc908d
--- /dev/null
+++ b/src/views/apps/ecommerce/products/ingredient/ProductIngredientTable.tsx
@@ -0,0 +1,393 @@
+'use client'
+
+// React Imports
+import { useCallback, useEffect, useMemo, useState } from 'react'
+
+// MUI Imports
+import Button from '@mui/material/Button'
+import Card from '@mui/material/Card'
+import Checkbox from '@mui/material/Checkbox'
+import IconButton from '@mui/material/IconButton'
+import MenuItem from '@mui/material/MenuItem'
+import TablePagination from '@mui/material/TablePagination'
+import type { TextFieldProps } from '@mui/material/TextField'
+import Typography from '@mui/material/Typography'
+
+// Third-party Imports
+import type { RankingInfo } from '@tanstack/match-sorter-utils'
+import { rankItem } from '@tanstack/match-sorter-utils'
+import type { ColumnDef, FilterFn } from '@tanstack/react-table'
+import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'
+import classnames from 'classnames'
+
+// Component Imports
+import TablePaginationComponent from '@components/TablePaginationComponent'
+import CustomTextField from '@core/components/mui/TextField'
+import OptionMenu from '@core/components/option-menu'
+
+// Style Imports
+import tableStyles from '@core/styles/table.module.css'
+import { Box, Chip, CircularProgress } from '@mui/material'
+import ConfirmDeleteDialog from '../../../../../components/dialogs/confirm-delete'
+import Loading from '../../../../../components/layout/shared/Loading'
+import { useUnitsMutation } from '../../../../../services/mutations/units'
+import { useUnits } from '../../../../../services/queries/units'
+import { Unit } from '../../../../../types/services/unit'
+import { formatDate } from '../../../../../utils/transform'
+
+declare module '@tanstack/table-core' {
+ interface FilterFns {
+ fuzzy: FilterFn
+ }
+ interface FilterMeta {
+ itemRank: RankingInfo
+ }
+}
+
+type UnitWithActionsType = Unit & {
+ actions?: string
+}
+
+const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => {
+ // Rank the item
+ const itemRank = rankItem(row.getValue(columnId), value)
+
+ // Store the itemRank info
+ addMeta({
+ itemRank
+ })
+
+ // Return if the item should be filtered in/out
+ return itemRank.passed
+}
+
+const DebouncedInput = ({
+ value: initialValue,
+ onChange,
+ debounce = 500,
+ ...props
+}: {
+ value: string | number
+ onChange: (value: string | number) => void
+ debounce?: number
+} & Omit) => {
+ // States
+ const [value, setValue] = useState(initialValue)
+
+ useEffect(() => {
+ setValue(initialValue)
+ }, [initialValue])
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ onChange(value)
+ }, debounce)
+
+ return () => clearTimeout(timeout)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [value])
+
+ return setValue(e.target.value)} />
+}
+
+// Column Definitions
+const columnHelper = createColumnHelper()
+
+const ProductIngredientTable = () => {
+ // States
+ const [addUnitOpen, setAddUnitOpen] = useState(false)
+ const [editUnitOpen, setEditUnitOpen] = useState(false)
+ const [rowSelection, setRowSelection] = useState({})
+ const [currentPage, setCurrentPage] = useState(1)
+ const [pageSize, setPageSize] = useState(10)
+ const [unitId, setUnitId] = useState('')
+ const [openConfirm, setOpenConfirm] = useState(false)
+ const [currentUnit, setCurrentUnit] = useState()
+
+ // Fetch products with pagination and search
+ const { data, isLoading, error, isFetching } = useUnits({
+ page: currentPage,
+ limit: pageSize
+ })
+
+ const { mutate: deleteUnit, isPending: isDeleting } = useUnitsMutation().deleteUnit
+
+ const units = data?.data ?? []
+ const totalCount = data?.pagination.total_count ?? 0
+
+ const handlePageChange = useCallback((event: unknown, newPage: number) => {
+ setCurrentPage(newPage)
+ }, [])
+
+ // Handle page size change
+ const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
+ const newPageSize = parseInt(event.target.value, 10)
+ setPageSize(newPageSize)
+ setCurrentPage(1) // Reset to first page
+ }, [])
+
+ const handleDelete = () => {
+ deleteUnit(unitId, {
+ onSuccess: () => setOpenConfirm(false)
+ })
+ }
+
+ const columns = useMemo[]>(
+ () => [
+ {
+ id: 'select',
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ )
+ },
+ columnHelper.accessor('name', {
+ header: 'Name',
+ cell: ({ row }) => (
+
+ {/*

*/}
+
+
+ {row.original.name || '-'}
+
+
+
+ )
+ }),
+ columnHelper.accessor('abbreviation', {
+ header: 'Abbreviation',
+ cell: ({ row }) => {row.original.abbreviation || '-'}
+ }),
+ columnHelper.accessor('is_active', {
+ header: 'Status',
+ cell: ({ row }) => (
+
+ )
+ }),
+ columnHelper.accessor('created_at', {
+ header: 'Created Date',
+ cell: ({ row }) => {formatDate(row.original.created_at)}
+ }),
+ columnHelper.accessor('actions', {
+ header: 'Actions',
+ cell: ({ row }) => (
+
+ {
+ setCurrentUnit(row.original)
+ setEditUnitOpen(!editUnitOpen)
+ }}
+ >
+
+
+ {
+ setUnitId(row.original.id)
+ setOpenConfirm(true)
+ }
+ }
+ },
+ { text: 'Duplicate', icon: 'tabler-copy' }
+ ]}
+ />
+
+ ),
+ enableSorting: false
+ })
+ ],
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [data]
+ )
+
+ const table = useReactTable({
+ data: units as Unit[],
+ columns,
+ filterFns: {
+ fuzzy: fuzzyFilter
+ },
+ state: {
+ rowSelection,
+ pagination: {
+ pageIndex: currentPage, // <= penting!
+ pageSize
+ }
+ },
+ enableRowSelection: true, //enable row selection for all rows
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ // Disable client-side pagination since we're handling it server-side
+ manualPagination: true,
+ pageCount: Math.ceil(totalCount / pageSize)
+ })
+
+ return (
+ <>
+
+
+
console.log(value)}
+ placeholder='Search Product'
+ className='max-sm:is-full'
+ />
+
+ table.setPageSize(Number(e.target.value))}
+ className='flex-auto max-sm:is-full sm:is-[70px]'
+ >
+
+
+
+
+
+
+
+
+ {isLoading ? (
+
+ ) : (
+
+
+ {table.getHeaderGroups().map(headerGroup => (
+
+ {headerGroup.headers.map(header => (
+ |
+ {header.isPlaceholder ? null : (
+ <>
+
+ {flexRender(header.column.columnDef.header, header.getContext())}
+ {{
+ asc: ,
+ desc:
+ }[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
+
+ >
+ )}
+ |
+ ))}
+
+ ))}
+
+ {table.getFilteredRowModel().rows.length === 0 ? (
+
+
+ |
+ No data available
+ |
+
+
+ ) : (
+
+ {table
+ .getRowModel()
+ .rows.slice(0, table.getState().pagination.pageSize)
+ .map(row => {
+ return (
+
+ {row.getVisibleCells().map(cell => (
+ | {flexRender(cell.column.columnDef.cell, cell.getContext())} |
+ ))}
+
+ )
+ })}
+
+ )}
+
+ )}
+
+ {isFetching && !isLoading && (
+
+
+
+ )}
+
+ (
+
+ )}
+ count={totalCount}
+ rowsPerPage={pageSize}
+ page={currentPage}
+ onPageChange={handlePageChange}
+ onRowsPerPageChange={handlePageSizeChange}
+ rowsPerPageOptions={[10, 25, 50]}
+ disabled={isLoading}
+ />
+
+
+ {/* setAddUnitOpen(!addUnitOpen)} />
+
+ setEditUnitOpen(!editUnitOpen)} data={currentUnit!} /> */}
+
+ setOpenConfirm(false)}
+ onConfirm={handleDelete}
+ isLoading={isDeleting}
+ title='Delete Unit'
+ message='Are you sure you want to delete this Unit? This action cannot be undone.'
+ />
+ >
+ )
+}
+
+export default ProductIngredientTable
diff --git a/src/views/apps/ecommerce/products/list/ProductListTable.tsx b/src/views/apps/ecommerce/products/list/ProductListTable.tsx
index 84d8571..cb8745e 100644
--- a/src/views/apps/ecommerce/products/list/ProductListTable.tsx
+++ b/src/views/apps/ecommerce/products/list/ProductListTable.tsx
@@ -47,6 +47,7 @@ import { useProducts } from '../../../../../services/queries/products'
import { Product } from '../../../../../types/services/product'
import ConfirmDeleteDialog from '../../../../../components/dialogs/confirm-delete'
import { useProductsMutation } from '../../../../../services/mutations/products'
+import { formatCurrency } from '../../../../../utils/transform'
declare module '@tanstack/table-core' {
interface FilterFns {
@@ -112,6 +113,7 @@ const ProductListTable = () => {
const [pageSize, setPageSize] = useState(10)
const [openConfirm, setOpenConfirm] = useState(false)
const [productId, setProductId] = useState('')
+ const [search, setSearch] = useState('')
// Hooks
const { lang: locale } = useParams()
@@ -119,7 +121,8 @@ const ProductListTable = () => {
// Fetch products with pagination and search
const { data, isLoading, error, isFetching } = useProducts({
page: currentPage,
- limit: pageSize
+ limit: pageSize,
+ search
})
const { mutate: deleteProduct, isPending: isDeleting } = useProductsMutation().deleteProduct
@@ -135,7 +138,7 @@ const ProductListTable = () => {
const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
const newPageSize = parseInt(event.target.value, 10)
setPageSize(newPageSize)
- setCurrentPage(0) // Reset to first page
+ setCurrentPage(1) // Reset to first page
}, [])
const handleDelete = () => {
@@ -182,33 +185,17 @@ const ProductListTable = () => {
)
}),
- // columnHelper.accessor('category_id', {
- // header: 'Category',
- // cell: ({ row }) => (
- //
- //
- //
- //
- // {row.original.category_id || '-'}
- //
- // )
- // }),
- // columnHelper.accessor('stock', {
- // header: 'Stock',
- // cell: ({ row }) => ,
- // enableSorting: false
- // }),
columnHelper.accessor('sku', {
header: 'SKU',
cell: ({ row }) => {row.original.sku}
}),
columnHelper.accessor('price', {
header: 'Price',
- cell: ({ row }) => {row.original.price}
+ cell: ({ row }) => {formatCurrency(row.original.price)}
}),
columnHelper.accessor('cost', {
header: 'Cost',
- cell: ({ row }) => {row.original.cost}
+ cell: ({ row }) => {formatCurrency(row.original.cost)}
}),
columnHelper.accessor('is_active', {
header: 'Status',
@@ -273,7 +260,7 @@ const ProductListTable = () => {
state: {
rowSelection,
pagination: {
- pageIndex: currentPage, // <= penting!
+ pageIndex: currentPage,
pageSize
}
},
@@ -293,8 +280,8 @@ const ProductListTable = () => {
console.log(value)}
+ value={search}
+ onChange={value => setSearch(value as string)}
placeholder='Search Product'
className='max-sm:is-full'
/>
diff --git a/src/views/apps/ecommerce/products/units/ProductUnitTable.tsx b/src/views/apps/ecommerce/products/units/ProductUnitTable.tsx
index aa6c875..cffc85c 100644
--- a/src/views/apps/ecommerce/products/units/ProductUnitTable.tsx
+++ b/src/views/apps/ecommerce/products/units/ProductUnitTable.tsx
@@ -27,7 +27,7 @@ import OptionMenu from '@core/components/option-menu'
// Style Imports
import tableStyles from '@core/styles/table.module.css'
-import { Box, CircularProgress } from '@mui/material'
+import { Box, Chip, CircularProgress } from '@mui/material'
import ConfirmDeleteDialog from '../../../../../components/dialogs/confirm-delete'
import Loading from '../../../../../components/layout/shared/Loading'
import { useUnitsMutation } from '../../../../../services/mutations/units'
@@ -35,6 +35,7 @@ import { useUnits } from '../../../../../services/queries/units'
import { Unit } from '../../../../../types/services/unit'
import AddUnitDrawer from './AddUnitDrawer'
import EditUnitDrawer from './EditUnitDrawer'
+import { formatDate } from '../../../../../utils/transform'
declare module '@tanstack/table-core' {
interface FilterFns {
@@ -124,7 +125,7 @@ const ProductUnitTable = () => {
const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
const newPageSize = parseInt(event.target.value, 10)
setPageSize(newPageSize)
- setCurrentPage(0) // Reset to first page
+ setCurrentPage(1) // Reset to first page
}, [])
const handleDelete = () => {
@@ -176,11 +177,18 @@ const ProductUnitTable = () => {
}),
columnHelper.accessor('is_active', {
header: 'Status',
- cell: ({ row }) => {row.original.is_active ? 'Active' : 'Inactive'}
+ cell: ({ row }) => (
+
+ )
}),
columnHelper.accessor('created_at', {
header: 'Created Date',
- cell: ({ row }) => {row.original.created_at}
+ cell: ({ row }) => {formatDate(row.original.created_at)}
}),
columnHelper.accessor('actions', {
header: 'Actions',
diff --git a/src/views/apps/stock/adjustment/AdjustmentStockDrawer.tsx b/src/views/apps/ecommerce/stock/adjustment/AdjustmentStockDrawer.tsx
similarity index 94%
rename from src/views/apps/stock/adjustment/AdjustmentStockDrawer.tsx
rename to src/views/apps/ecommerce/stock/adjustment/AdjustmentStockDrawer.tsx
index 06932c4..f3e7a46 100644
--- a/src/views/apps/stock/adjustment/AdjustmentStockDrawer.tsx
+++ b/src/views/apps/ecommerce/stock/adjustment/AdjustmentStockDrawer.tsx
@@ -16,10 +16,10 @@ import Typography from '@mui/material/Typography'
import CustomTextField from '@core/components/mui/TextField'
import { Autocomplete, CircularProgress } from '@mui/material'
import { useDebounce } from 'use-debounce'
-import { useInventoriesMutation } from '../../../../services/mutations/inventories'
-import { useOutlets } from '../../../../services/queries/outlets'
-import { useProducts } from '../../../../services/queries/products'
-import { InventoryAdjustRequest } from '../../../../types/services/inventory'
+import { useInventoriesMutation } from '../../../../../services/mutations/inventories'
+import { useOutlets } from '../../../../../services/queries/outlets'
+import { useProducts } from '../../../../../services/queries/products'
+import { InventoryAdjustRequest } from '../../../../../types/services/inventory'
type Props = {
open: boolean
diff --git a/src/views/apps/stock/adjustment/StockListTable.tsx b/src/views/apps/ecommerce/stock/adjustment/StockListTable.tsx
similarity index 97%
rename from src/views/apps/stock/adjustment/StockListTable.tsx
rename to src/views/apps/ecommerce/stock/adjustment/StockListTable.tsx
index 2cc82b5..ca4e04f 100644
--- a/src/views/apps/stock/adjustment/StockListTable.tsx
+++ b/src/views/apps/ecommerce/stock/adjustment/StockListTable.tsx
@@ -35,10 +35,10 @@ import CustomTextField from '@core/components/mui/TextField'
// Style Imports
import tableStyles from '@core/styles/table.module.css'
import { Box, CircularProgress } from '@mui/material'
-import Loading from '../../../../components/layout/shared/Loading'
-import { useInventories } from '../../../../services/queries/inventories'
-import { Inventory } from '../../../../types/services/inventory'
import AdjustmentStockDrawer from './AdjustmentStockDrawer'
+import { Inventory } from '../../../../../types/services/inventory'
+import Loading from '../../../../../components/layout/shared/Loading'
+import { useInventories } from '../../../../../services/queries/inventories'
declare module '@tanstack/table-core' {
interface FilterFns {
@@ -121,7 +121,7 @@ const StockListTable = () => {
const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
const newPageSize = parseInt(event.target.value, 10)
setPageSize(newPageSize)
- setCurrentPage(0) // Reset to first page
+ setCurrentPage(1) // Reset to first page
}, [])
const columns = useMemo[]>(
diff --git a/src/views/apps/stock/list/AddStockDrawer.tsx b/src/views/apps/ecommerce/stock/list/AddStockDrawer.tsx
similarity index 94%
rename from src/views/apps/stock/list/AddStockDrawer.tsx
rename to src/views/apps/ecommerce/stock/list/AddStockDrawer.tsx
index 8609af1..44e37f2 100644
--- a/src/views/apps/stock/list/AddStockDrawer.tsx
+++ b/src/views/apps/ecommerce/stock/list/AddStockDrawer.tsx
@@ -16,10 +16,10 @@ import Typography from '@mui/material/Typography'
import CustomTextField from '@core/components/mui/TextField'
import { Autocomplete, CircularProgress } from '@mui/material'
import { useDebounce } from 'use-debounce'
-import { useInventoriesMutation } from '../../../../services/mutations/inventories'
-import { useOutlets } from '../../../../services/queries/outlets'
-import { useProducts } from '../../../../services/queries/products'
-import { InventoryRequest } from '../../../../types/services/inventory'
+import { useInventoriesMutation } from '../../../../../services/mutations/inventories'
+import { useOutlets } from '../../../../../services/queries/outlets'
+import { useProducts } from '../../../../../services/queries/products'
+import { InventoryRequest } from '../../../../../types/services/inventory'
type Props = {
open: boolean
diff --git a/src/views/apps/stock/list/StockListTable.tsx b/src/views/apps/ecommerce/stock/list/StockListTable.tsx
similarity index 95%
rename from src/views/apps/stock/list/StockListTable.tsx
rename to src/views/apps/ecommerce/stock/list/StockListTable.tsx
index 3372590..d56eaac 100644
--- a/src/views/apps/stock/list/StockListTable.tsx
+++ b/src/views/apps/ecommerce/stock/list/StockListTable.tsx
@@ -36,12 +36,12 @@ import OptionMenu from '@core/components/option-menu'
// Style Imports
import tableStyles from '@core/styles/table.module.css'
import { Box, CircularProgress } from '@mui/material'
-import ConfirmDeleteDialog from '../../../../components/dialogs/confirm-delete'
-import Loading from '../../../../components/layout/shared/Loading'
-import { useInventoriesMutation } from '../../../../services/mutations/inventories'
-import { useInventories } from '../../../../services/queries/inventories'
-import { Inventory } from '../../../../types/services/inventory'
import AddStockDrawer from './AddStockDrawer'
+import ConfirmDeleteDialog from '../../../../../components/dialogs/confirm-delete'
+import Loading from '../../../../../components/layout/shared/Loading'
+import { useInventoriesMutation } from '../../../../../services/mutations/inventories'
+import { useInventories } from '../../../../../services/queries/inventories'
+import { Inventory } from '../../../../../types/services/inventory'
declare module '@tanstack/table-core' {
interface FilterFns {
@@ -128,7 +128,7 @@ const StockListTable = () => {
const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
const newPageSize = parseInt(event.target.value, 10)
setPageSize(newPageSize)
- setCurrentPage(0) // Reset to first page
+ setCurrentPage(1) // Reset to first page
}, [])
const handleDelete = () => {
@@ -252,8 +252,8 @@ const StockListTable = () => {
table.setPageSize(Number(e.target.value))}
+ value={pageSize}
+ onChange={handlePageSizeChange}
className='flex-auto is-[70px] max-sm:is-full'
>
diff --git a/src/views/apps/stock/list/TableFilters.tsx b/src/views/apps/ecommerce/stock/list/TableFilters.tsx
similarity index 95%
rename from src/views/apps/stock/list/TableFilters.tsx
rename to src/views/apps/ecommerce/stock/list/TableFilters.tsx
index fec459d..cc1dccf 100644
--- a/src/views/apps/stock/list/TableFilters.tsx
+++ b/src/views/apps/ecommerce/stock/list/TableFilters.tsx
@@ -1,17 +1,16 @@
// React Imports
-import { useState, useEffect } from 'react'
+import { useEffect, useState } from 'react'
// MUI Imports
-import Grid from '@mui/material/Grid2'
import CardContent from '@mui/material/CardContent'
+import Grid from '@mui/material/Grid2'
import MenuItem from '@mui/material/MenuItem'
// Type Imports
-import type { ProductType } from '@/types/apps/ecommerceTypes'
// Component Imports
import CustomTextField from '@core/components/mui/TextField'
-import { Product } from '../../../../types/services/product'
+import { Product } from '../../../../../types/services/product'
type ProductStockType = { [key: string]: boolean }
diff --git a/src/views/apps/finance/payment-methods/list/AddPaymentMethodDrawer.tsx b/src/views/apps/finance/payment-methods/list/AddPaymentMethodDrawer.tsx
new file mode 100644
index 0000000..4e9675a
--- /dev/null
+++ b/src/views/apps/finance/payment-methods/list/AddPaymentMethodDrawer.tsx
@@ -0,0 +1,167 @@
+// React Imports
+import { useEffect, useState } from 'react'
+
+// MUI Imports
+import Button from '@mui/material/Button'
+import Divider from '@mui/material/Divider'
+import Drawer from '@mui/material/Drawer'
+import IconButton from '@mui/material/IconButton'
+import Switch from '@mui/material/Switch'
+import Typography from '@mui/material/Typography'
+
+// Third-party Imports
+import PerfectScrollbar from 'react-perfect-scrollbar'
+
+// Type Imports
+
+// Component Imports
+import CustomTextField from '@core/components/mui/TextField'
+import { MenuItem } from '@mui/material'
+import { useDispatch, useSelector } from 'react-redux'
+import { RootState } from '../../../../../redux-store'
+import { usePaymentMethodsMutation } from '../../../../../services/mutations/paymentMethods'
+import { PaymentMethodRequest } from '../../../../../types/services/paymentMethod'
+import { resetPaymentMethod } from '../../../../../redux-store/slices/paymentMethod'
+
+type Props = {
+ open: boolean
+ handleClose: () => void
+}
+
+// Vars
+const initialData = {
+ name: '',
+ type: '',
+ is_active: true
+}
+
+const AddPaymentMethodDrawer = (props: Props) => {
+ const dispatch = useDispatch()
+ // Props
+ const { open, handleClose } = props
+
+ const { createPaymentMethod, updatePaymentMethod } = usePaymentMethodsMutation()
+ const { currentPaymentMethod } = useSelector((state: RootState) => state.paymentMethodReducer)
+
+ // States
+ const [formData, setFormData] = useState(initialData)
+
+ useEffect(() => {
+ if (currentPaymentMethod.id) {
+ setFormData(currentPaymentMethod)
+ }
+ }, [currentPaymentMethod])
+
+ const handleSubmit = (e: any) => {
+ e.preventDefault()
+
+ if (currentPaymentMethod.id) {
+ updatePaymentMethod.mutate(
+ { id: currentPaymentMethod.id, payload: formData },
+ {
+ onSuccess: () => {
+ handleReset()
+ }
+ }
+ )
+ } else {
+ createPaymentMethod.mutate(formData, {
+ onSuccess: () => {
+ handleReset()
+ }
+ })
+ }
+ }
+
+ const handleReset = () => {
+ handleClose()
+ dispatch(resetPaymentMethod())
+ setFormData(initialData)
+ }
+
+ const handleInputChange = (e: any) => {
+ setFormData({
+ ...formData,
+ [e.target.name]: e.target.value
+ })
+ }
+
+ return (
+
+
+ {currentPaymentMethod.id ? 'Edit' : 'Add'} Payment Method
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default AddPaymentMethodDrawer
diff --git a/src/views/apps/finance/payment-methods/list/PaymentMethodListTable.tsx b/src/views/apps/finance/payment-methods/list/PaymentMethodListTable.tsx
new file mode 100644
index 0000000..3a14f61
--- /dev/null
+++ b/src/views/apps/finance/payment-methods/list/PaymentMethodListTable.tsx
@@ -0,0 +1,401 @@
+'use client'
+
+// React Imports
+import { useCallback, useEffect, useMemo, useState } from 'react'
+
+// Next Imports
+
+// MUI Imports
+import Button from '@mui/material/Button'
+import Card from '@mui/material/Card'
+import CardContent from '@mui/material/CardContent'
+import Checkbox from '@mui/material/Checkbox'
+import MenuItem from '@mui/material/MenuItem'
+import type { TextFieldProps } from '@mui/material/TextField'
+import Typography from '@mui/material/Typography'
+
+// Third-party Imports
+import type { RankingInfo } from '@tanstack/match-sorter-utils'
+import { rankItem } from '@tanstack/match-sorter-utils'
+import type { ColumnDef, FilterFn } from '@tanstack/react-table'
+import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'
+import classnames from 'classnames'
+
+// Type Imports
+
+// Component Imports
+import CustomTextField from '@core/components/mui/TextField'
+
+// Util Imports
+
+// Style Imports
+import tableStyles from '@core/styles/table.module.css'
+import { Box, Chip, CircularProgress, IconButton, TablePagination } from '@mui/material'
+import { useDispatch } from 'react-redux'
+import OptionMenu from '../../../../../@core/components/option-menu'
+import ConfirmDeleteDialog from '../../../../../components/dialogs/confirm-delete'
+import Loading from '../../../../../components/layout/shared/Loading'
+import TablePaginationComponent from '../../../../../components/TablePaginationComponent'
+import { setPaymentMethod } from '../../../../../redux-store/slices/paymentMethod'
+import { usePaymentMethodsMutation } from '../../../../../services/mutations/paymentMethods'
+import { usePaymentMethods } from '../../../../../services/queries/paymentMethods'
+import { PaymentMethod } from '../../../../../types/services/paymentMethod'
+import AddPaymentMethodDrawer from './AddPaymentMethodDrawer'
+
+declare module '@tanstack/table-core' {
+ interface FilterFns {
+ fuzzy: FilterFn
+ }
+ interface FilterMeta {
+ itemRank: RankingInfo
+ }
+}
+
+type FinancePaymentMethodTypeWithAction = PaymentMethod & {
+ actions?: string
+}
+
+const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => {
+ // Rank the item
+ const itemRank = rankItem(row.getValue(columnId), value)
+
+ // Store the itemRank info
+ addMeta({
+ itemRank
+ })
+
+ // Return if the item should be filtered in/out
+ return itemRank.passed
+}
+
+const DebouncedInput = ({
+ value: initialValue,
+ onChange,
+ debounce = 500,
+ ...props
+}: {
+ value: string | number
+ onChange: (value: string | number) => void
+ debounce?: number
+} & Omit) => {
+ // States
+ const [value, setValue] = useState(initialValue)
+
+ useEffect(() => {
+ setValue(initialValue)
+ }, [initialValue])
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ onChange(value)
+ }, debounce)
+
+ return () => clearTimeout(timeout)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [value])
+
+ return setValue(e.target.value)} />
+}
+
+// Column Definitions
+const columnHelper = createColumnHelper()
+
+const PaymentMethodListTable = () => {
+ const dispatch = useDispatch()
+
+ // States
+ const [paymentMethodOpen, setPaymentMethodOpen] = useState(false)
+ const [rowSelection, setRowSelection] = useState({})
+ const [currentPage, setCurrentPage] = useState(1)
+ const [pageSize, setPageSize] = useState(10)
+ const [openConfirm, setOpenConfirm] = useState(false)
+ const [paymentMethodId, setPaymentMethodId] = useState('')
+ const [search, setSearch] = useState('')
+
+ const { data, isLoading, error, isFetching } = usePaymentMethods({
+ page: currentPage,
+ limit: pageSize,
+ search
+ })
+
+ const { deletePaymentMethod } = usePaymentMethodsMutation()
+
+ const paymentMethods = data?.payment_methods ?? []
+ const totalCount = data?.total_count ?? 0
+
+ const handlePageChange = useCallback((event: unknown, newPage: number) => {
+ setCurrentPage(newPage)
+ }, [])
+
+ // Handle page size change
+ const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
+ const newPageSize = parseInt(event.target.value, 10)
+ setPageSize(newPageSize)
+ setCurrentPage(1) // Reset to first page
+ }, [])
+
+ const handleDelete = () => {
+ deletePaymentMethod.mutate(paymentMethodId, {
+ onSuccess: () => setOpenConfirm(false)
+ })
+ }
+
+ const columns = useMemo[]>(
+ () => [
+ {
+ id: 'select',
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ )
+ },
+ columnHelper.accessor('name', {
+ header: 'Name',
+ cell: ({ row }) => {row.original.name || '-'}
+ }),
+ columnHelper.accessor('type', {
+ header: 'Type',
+ cell: ({ row }) => {row.original.type || '-'}
+ }),
+ columnHelper.accessor('is_active', {
+ header: 'Status',
+ cell: ({ row }) => (
+
+ )
+ }),
+ columnHelper.accessor('created_at', {
+ header: 'Created Date',
+ cell: ({ row }) => {row.original.created_at || '-'}
+ }),
+ columnHelper.accessor('actions', {
+ header: 'Actions',
+ cell: ({ row }) => (
+
+ {
+ dispatch(setPaymentMethod(row.original))
+ setPaymentMethodOpen(true)
+ }}
+ >
+
+
+ {
+ setOpenConfirm(true)
+ setPaymentMethodId(row.original.id)
+ }
+ }
+ },
+ { text: 'Duplicate', icon: 'tabler-copy' }
+ ]}
+ />
+
+ ),
+ enableSorting: false
+ })
+ ],
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ )
+
+ const table = useReactTable({
+ data: paymentMethods as PaymentMethod[],
+ columns,
+ filterFns: {
+ fuzzy: fuzzyFilter
+ },
+ state: {
+ rowSelection,
+ pagination: {
+ pageIndex: currentPage,
+ pageSize
+ }
+ },
+ enableRowSelection: true, //enable row selection for all rows
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ // Disable client-side pagination since we're handling it server-side
+ manualPagination: true,
+ pageCount: Math.ceil(totalCount / pageSize)
+ })
+
+ return (
+ <>
+
+
+ setSearch(value as string)}
+ placeholder='Search'
+ className='max-sm:is-full'
+ />
+
+
+
+
+
+
+
+ }
+ >
+ Export
+
+ }
+ onClick={() => setPaymentMethodOpen(!paymentMethodOpen)}
+ >
+ Add Payment Method
+
+
+
+
+ {isLoading ? (
+
+ ) : (
+
+
+ {table.getHeaderGroups().map(headerGroup => (
+
+ {headerGroup.headers.map(header => (
+ |
+ {header.isPlaceholder ? null : (
+ <>
+
+ {flexRender(header.column.columnDef.header, header.getContext())}
+ {{
+ asc: ,
+ desc:
+ }[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
+
+ >
+ )}
+ |
+ ))}
+
+ ))}
+
+ {table.getFilteredRowModel().rows.length === 0 ? (
+
+
+ |
+ No data available
+ |
+
+
+ ) : (
+
+ {table
+ .getRowModel()
+ .rows.slice(0, table.getState().pagination.pageSize)
+ .map(row => {
+ return (
+
+ {row.getVisibleCells().map(cell => (
+ | {flexRender(cell.column.columnDef.cell, cell.getContext())} |
+ ))}
+
+ )
+ })}
+
+ )}
+
+ )}
+
+ {isFetching && !isLoading && (
+
+
+
+ )}
+
+
+ (
+
+ )}
+ count={totalCount}
+ rowsPerPage={pageSize}
+ page={currentPage}
+ onPageChange={handlePageChange}
+ onRowsPerPageChange={handlePageSizeChange}
+ rowsPerPageOptions={[10, 25, 50]}
+ disabled={isLoading}
+ />
+
+
+ setPaymentMethodOpen(!paymentMethodOpen)} />
+
+ setOpenConfirm(false)}
+ onConfirm={handleDelete}
+ isLoading={deletePaymentMethod.isPending}
+ title='Delete paymentMethod'
+ message='Are you sure you want to delete this paymentMethod? This action cannot be undone.'
+ />
+ >
+ )
+}
+
+export default PaymentMethodListTable
diff --git a/src/views/apps/organization/outlets/list/OrganizationOutletListTable.tsx b/src/views/apps/organization/outlets/list/OrganizationOutletListTable.tsx
new file mode 100644
index 0000000..c7f5031
--- /dev/null
+++ b/src/views/apps/organization/outlets/list/OrganizationOutletListTable.tsx
@@ -0,0 +1,395 @@
+'use client'
+
+// React Imports
+import { useCallback, useEffect, useMemo, useState } from 'react'
+
+// Next Imports
+
+// MUI Imports
+import Button from '@mui/material/Button'
+import Card from '@mui/material/Card'
+import CardContent from '@mui/material/CardContent'
+import Checkbox from '@mui/material/Checkbox'
+import MenuItem from '@mui/material/MenuItem'
+import type { TextFieldProps } from '@mui/material/TextField'
+import Typography from '@mui/material/Typography'
+
+// Third-party Imports
+import type { RankingInfo } from '@tanstack/match-sorter-utils'
+import { rankItem } from '@tanstack/match-sorter-utils'
+import type { ColumnDef, FilterFn } from '@tanstack/react-table'
+import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'
+import classnames from 'classnames'
+
+// Type Imports
+
+// Component Imports
+import CustomTextField from '@core/components/mui/TextField'
+
+// Util Imports
+
+// Style Imports
+import tableStyles from '@core/styles/table.module.css'
+import { Box, Chip, CircularProgress, IconButton, TablePagination } from '@mui/material'
+import { useDispatch } from 'react-redux'
+import OptionMenu from '../../../../../@core/components/option-menu'
+import ConfirmDeleteDialog from '../../../../../components/dialogs/confirm-delete'
+import Loading from '../../../../../components/layout/shared/Loading'
+import TablePaginationComponent from '../../../../../components/TablePaginationComponent'
+import { usePaymentMethodsMutation } from '../../../../../services/mutations/paymentMethods'
+import { useOutlets } from '../../../../../services/queries/outlets'
+import { Outlet } from '../../../../../types/services/outlet'
+
+declare module '@tanstack/table-core' {
+ interface FilterFns {
+ fuzzy: FilterFn
+ }
+ interface FilterMeta {
+ itemRank: RankingInfo
+ }
+}
+
+type OrganizationOutletTypeWithAction = Outlet & {
+ actions?: string
+}
+
+const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => {
+ // Rank the item
+ const itemRank = rankItem(row.getValue(columnId), value)
+
+ // Store the itemRank info
+ addMeta({
+ itemRank
+ })
+
+ // Return if the item should be filtered in/out
+ return itemRank.passed
+}
+
+const DebouncedInput = ({
+ value: initialValue,
+ onChange,
+ debounce = 500,
+ ...props
+}: {
+ value: string | number
+ onChange: (value: string | number) => void
+ debounce?: number
+} & Omit) => {
+ // States
+ const [value, setValue] = useState(initialValue)
+
+ useEffect(() => {
+ setValue(initialValue)
+ }, [initialValue])
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ onChange(value)
+ }, debounce)
+
+ return () => clearTimeout(timeout)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [value])
+
+ return setValue(e.target.value)} />
+}
+
+// Column Definitions
+const columnHelper = createColumnHelper()
+
+const OrganizationOutletListTable = () => {
+ const dispatch = useDispatch()
+
+ // States
+ const [paymentMethodOpen, setPaymentMethodOpen] = useState(false)
+ const [rowSelection, setRowSelection] = useState({})
+ const [currentPage, setCurrentPage] = useState(1)
+ const [pageSize, setPageSize] = useState(10)
+ const [openConfirm, setOpenConfirm] = useState(false)
+ const [paymentMethodId, setPaymentMethodId] = useState('')
+ const [search, setSearch] = useState('')
+
+ const { data, isLoading, error, isFetching } = useOutlets({
+ page: currentPage,
+ limit: pageSize,
+ search
+ })
+
+ const { deletePaymentMethod } = usePaymentMethodsMutation()
+
+ const outlets = data?.outlets ?? []
+ const totalCount = data?.total_count ?? 0
+
+ const handlePageChange = useCallback((event: unknown, newPage: number) => {
+ setCurrentPage(newPage)
+ }, [])
+
+ // Handle page size change
+ const handlePageSizeChange = useCallback((event: React.ChangeEvent) => {
+ const newPageSize = parseInt(event.target.value, 10)
+ setPageSize(newPageSize)
+ setCurrentPage(1) // Reset to first page
+ }, [])
+
+ const handleDelete = () => {
+ deletePaymentMethod.mutate(paymentMethodId, {
+ onSuccess: () => setOpenConfirm(false)
+ })
+ }
+
+ const columns = useMemo[]>(
+ () => [
+ {
+ id: 'select',
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ )
+ },
+ columnHelper.accessor('name', {
+ header: 'Name',
+ cell: ({ row }) => {row.original.name || '-'}
+ }),
+ columnHelper.accessor('address', {
+ header: 'Address',
+ cell: ({ row }) => {row.original.address || '-'}
+ }),
+ columnHelper.accessor('phone_number', {
+ header: 'Phone',
+ cell: ({ row }) => {row.original.phone_number || '-'}
+ }),
+ columnHelper.accessor('business_type', {
+ header: 'Business',
+ cell: ({ row }) => {row.original.business_type || '-'}
+ }),
+ columnHelper.accessor('is_active', {
+ header: 'Status',
+ cell: ({ row }) => (
+
+ )
+ }),
+ columnHelper.accessor('currency', {
+ header: 'Currency',
+ cell: ({ row }) => {row.original.currency || '-'}
+ }),
+ columnHelper.accessor('tax_rate', {
+ header: 'Tax',
+ cell: ({ row }) => {row.original.tax_rate || '-'}
+ }),
+ columnHelper.accessor('actions', {
+ header: 'Actions',
+ cell: ({ row }) => (
+
+
+
+
+ {
+ setOpenConfirm(true)
+ setPaymentMethodId(row.original.id)
+ }
+ }
+ },
+ { text: 'Duplicate', icon: 'tabler-copy' }
+ ]}
+ />
+
+ ),
+ enableSorting: false
+ })
+ ],
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ )
+
+ const table = useReactTable({
+ data: outlets as Outlet[],
+ columns,
+ filterFns: {
+ fuzzy: fuzzyFilter
+ },
+ state: {
+ rowSelection,
+ pagination: {
+ pageIndex: currentPage,
+ pageSize
+ }
+ },
+ enableRowSelection: true, //enable row selection for all rows
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ // Disable client-side pagination since we're handling it server-side
+ manualPagination: true,
+ pageCount: Math.ceil(totalCount / pageSize)
+ })
+
+ return (
+ <>
+
+
+ setSearch(value as string)}
+ placeholder='Search'
+ className='max-sm:is-full'
+ />
+
+
+
+
+
+
+
+ }
+ >
+ Export
+
+
+
+
+ {isLoading ? (
+
+ ) : (
+
+
+ {table.getHeaderGroups().map(headerGroup => (
+
+ {headerGroup.headers.map(header => (
+ |
+ {header.isPlaceholder ? null : (
+ <>
+
+ {flexRender(header.column.columnDef.header, header.getContext())}
+ {{
+ asc: ,
+ desc:
+ }[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
+
+ >
+ )}
+ |
+ ))}
+
+ ))}
+
+ {table.getFilteredRowModel().rows.length === 0 ? (
+
+
+ |
+ No data available
+ |
+
+
+ ) : (
+
+ {table
+ .getRowModel()
+ .rows.slice(0, table.getState().pagination.pageSize)
+ .map(row => {
+ return (
+
+ {row.getVisibleCells().map(cell => (
+ | {flexRender(cell.column.columnDef.cell, cell.getContext())} |
+ ))}
+
+ )
+ })}
+
+ )}
+
+ )}
+
+ {isFetching && !isLoading && (
+
+
+
+ )}
+
+
+ (
+
+ )}
+ count={totalCount}
+ rowsPerPage={pageSize}
+ page={currentPage}
+ onPageChange={handlePageChange}
+ onRowsPerPageChange={handlePageSizeChange}
+ rowsPerPageOptions={[10, 25, 50]}
+ disabled={isLoading}
+ />
+
+
+ setOpenConfirm(false)}
+ onConfirm={handleDelete}
+ isLoading={deletePaymentMethod.isPending}
+ title='Delete paymentMethod'
+ message='Are you sure you want to delete this paymentMethod? This action cannot be undone.'
+ />
+ >
+ )
+}
+
+export default OrganizationOutletListTable