feat list product and category
This commit is contained in:
parent
fffa2ead5c
commit
0fde122ac4
@ -1,33 +1,29 @@
|
|||||||
// MUI Imports
|
import { Typography, Pagination } from '@mui/material'
|
||||||
import Pagination from '@mui/material/Pagination'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
|
|
||||||
// Third Party Imports
|
const TablePaginationComponent = ({
|
||||||
import type { useReactTable } from '@tanstack/react-table'
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
totalCount,
|
||||||
|
onPageChange
|
||||||
|
}: {
|
||||||
|
pageIndex: number
|
||||||
|
pageSize: number
|
||||||
|
totalCount: number
|
||||||
|
onPageChange: (event: React.ChangeEvent<unknown>, page: number) => void
|
||||||
|
}) => {
|
||||||
|
const start = pageIndex === 1 ? pageIndex : (pageIndex - 1) * pageSize + 1
|
||||||
|
const end = Math.min((pageIndex) * pageSize, totalCount)
|
||||||
|
|
||||||
const TablePaginationComponent = ({ table }: { table: ReturnType<typeof useReactTable> }) => {
|
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-between items-center flex-wrap pli-6 border-bs bs-auto plb-[12.5px] gap-2'>
|
<div className='flex justify-between items-center flex-wrap pli-6 border-bs bs-auto plb-[12.5px] gap-2'>
|
||||||
<Typography color='text.disabled'>
|
<Typography color='text.disabled'>{`Showing ${start} to ${end} of ${totalCount} entries`}</Typography>
|
||||||
{`Showing ${
|
|
||||||
table.getFilteredRowModel().rows.length === 0
|
|
||||||
? 0
|
|
||||||
: table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1
|
|
||||||
}
|
|
||||||
to ${Math.min(
|
|
||||||
(table.getState().pagination.pageIndex + 1) * table.getState().pagination.pageSize,
|
|
||||||
table.getFilteredRowModel().rows.length
|
|
||||||
)} of ${table.getFilteredRowModel().rows.length} entries`}
|
|
||||||
</Typography>
|
|
||||||
<Pagination
|
<Pagination
|
||||||
shape='rounded'
|
shape='rounded'
|
||||||
color='primary'
|
color='primary'
|
||||||
variant='tonal'
|
variant='tonal'
|
||||||
count={Math.ceil(table.getFilteredRowModel().rows.length / table.getState().pagination.pageSize)}
|
count={Math.ceil(totalCount / pageSize)}
|
||||||
page={table.getState().pagination.pageIndex + 1}
|
page={pageIndex}
|
||||||
onChange={(_, page) => {
|
onChange={onPageChange}
|
||||||
table.setPageIndex(page - 1)
|
|
||||||
}}
|
|
||||||
showFirstButton
|
showFirstButton
|
||||||
showLastButton
|
showLastButton
|
||||||
/>
|
/>
|
||||||
|
|||||||
41
src/services/queries/categories.ts
Normal file
41
src/services/queries/categories.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { Categories } from '../../types/services/category'
|
||||||
|
import { api } from '../api'
|
||||||
|
|
||||||
|
interface CategoriesQueryParams {
|
||||||
|
page?: number
|
||||||
|
limit?: number
|
||||||
|
search?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCategoriesQuery = {
|
||||||
|
getCategories: (params: CategoriesQueryParams = {}) => {
|
||||||
|
const { page = 1, limit = 10, search = '', ...filters } = params
|
||||||
|
|
||||||
|
return useQuery<Categories>({
|
||||||
|
queryKey: ['categories', { page, limit, search, ...filters }],
|
||||||
|
queryFn: async () => {
|
||||||
|
const queryParams = new URLSearchParams()
|
||||||
|
|
||||||
|
queryParams.append('page', page.toString())
|
||||||
|
queryParams.append('limit', limit.toString())
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
queryParams.append('search', search)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add other filters
|
||||||
|
Object.entries(filters).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== null && value !== '') {
|
||||||
|
queryParams.append(key, value.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await api.get(`/categories?${queryParams.toString()}`)
|
||||||
|
return res.data.data
|
||||||
|
},
|
||||||
|
// Cache for 5 minutes
|
||||||
|
staleTime: 5 * 60 * 1000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,44 @@
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { Products } from '../../types/services/products'
|
import { Products } from '../../types/services/product'
|
||||||
import { api } from '../api'
|
import { api } from '../api'
|
||||||
|
|
||||||
|
interface ProductsQueryParams {
|
||||||
|
page?: number
|
||||||
|
limit?: number
|
||||||
|
search?: string
|
||||||
|
// Add other filter parameters as needed
|
||||||
|
category_id?: string
|
||||||
|
is_active?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export const useProductsQuery = {
|
export const useProductsQuery = {
|
||||||
getProducts: () => {
|
getProducts: (params: ProductsQueryParams = {}) => {
|
||||||
|
const { page = 1, limit = 10, search = '', ...filters } = params
|
||||||
|
|
||||||
return useQuery<Products>({
|
return useQuery<Products>({
|
||||||
queryKey: ['products'],
|
queryKey: ['products', { page, limit, search, ...filters }],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const res = await api.get('/products')
|
const queryParams = new URLSearchParams()
|
||||||
|
|
||||||
|
queryParams.append('page', page.toString())
|
||||||
|
queryParams.append('limit', limit.toString())
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
queryParams.append('search', search)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add other filters
|
||||||
|
Object.entries(filters).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== null && value !== '') {
|
||||||
|
queryParams.append(key, value.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await api.get(`/products?${queryParams.toString()}`)
|
||||||
return res.data.data
|
return res.data.data
|
||||||
}
|
},
|
||||||
|
// Cache for 5 minutes
|
||||||
|
staleTime: 5 * 60 * 1000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/types/services/category.ts
Normal file
18
src/types/services/category.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export interface Category {
|
||||||
|
id: string;
|
||||||
|
organization_id: string;
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
business_type: string;
|
||||||
|
metadata: Record<string, any>;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Categories {
|
||||||
|
categories: Category[];
|
||||||
|
total_count: number;
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
total_pages: number;
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
// React Imports
|
// React Imports
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
// MUI Imports
|
// MUI Imports
|
||||||
import Card from '@mui/material/Card'
|
import Card from '@mui/material/Card'
|
||||||
@ -39,6 +39,10 @@ import TablePaginationComponent from '@components/TablePaginationComponent'
|
|||||||
|
|
||||||
// Style Imports
|
// Style Imports
|
||||||
import tableStyles from '@core/styles/table.module.css'
|
import tableStyles from '@core/styles/table.module.css'
|
||||||
|
import { useCategoriesQuery } from '../../../../../services/queries/categories'
|
||||||
|
import { Category } from '../../../../../types/services/category'
|
||||||
|
import { Box, CircularProgress } from '@mui/material'
|
||||||
|
import Loading from '../../../../../components/layout/shared/Loading'
|
||||||
|
|
||||||
declare module '@tanstack/table-core' {
|
declare module '@tanstack/table-core' {
|
||||||
interface FilterFns {
|
interface FilterFns {
|
||||||
@ -49,16 +53,7 @@ declare module '@tanstack/table-core' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type categoryType = {
|
type CategoryWithActionsType = Category & {
|
||||||
id: number
|
|
||||||
categoryTitle: string
|
|
||||||
description: string
|
|
||||||
totalProduct: number
|
|
||||||
totalEarning: number
|
|
||||||
image: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CategoryWithActionsType = categoryType & {
|
|
||||||
actions?: string
|
actions?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,106 +99,6 @@ const DebouncedInput = ({
|
|||||||
return <CustomTextField {...props} value={value} onChange={e => setValue(e.target.value)} />
|
return <CustomTextField {...props} value={value} onChange={e => setValue(e.target.value)} />
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vars
|
|
||||||
const categoryData: categoryType[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
categoryTitle: 'Smart Phone',
|
|
||||||
description: 'Choose from wide range of smartphones online at best prices.',
|
|
||||||
totalProduct: 12548,
|
|
||||||
totalEarning: 98784,
|
|
||||||
image: '/images/apps/ecommerce/product-1.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
categoryTitle: 'Clothing, Shoes, and jewellery',
|
|
||||||
description: 'Fashion for a wide selection of clothing, shoes, jewellery and watches.',
|
|
||||||
totalProduct: 4689,
|
|
||||||
totalEarning: 45627,
|
|
||||||
image: '/images/apps/ecommerce/product-9.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
categoryTitle: 'Home and Kitchen',
|
|
||||||
description: 'Browse through the wide range of Home and kitchen products.',
|
|
||||||
totalProduct: 11297,
|
|
||||||
totalEarning: 51097,
|
|
||||||
image: '/images/apps/ecommerce/product-10.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
categoryTitle: 'Beauty and Personal Care',
|
|
||||||
description: 'Explore beauty and personal care products, shop makeup and etc.',
|
|
||||||
totalProduct: 9474,
|
|
||||||
totalEarning: 74829,
|
|
||||||
image: '/images/apps/ecommerce/product-19.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
categoryTitle: 'Books',
|
|
||||||
description: 'Over 25 million titles across categories such as business and etc.',
|
|
||||||
totalProduct: 10257,
|
|
||||||
totalEarning: 63618,
|
|
||||||
image: '/images/apps/ecommerce/product-25.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
categoryTitle: 'Games',
|
|
||||||
description: 'Every month, get exclusive in-game loot, free games, a free subscription.',
|
|
||||||
totalProduct: 14501,
|
|
||||||
totalEarning: 65920,
|
|
||||||
image: '/images/apps/ecommerce/product-12.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
categoryTitle: 'Baby Products',
|
|
||||||
description: 'Buy baby products across different categories from top brands.',
|
|
||||||
totalProduct: 8624,
|
|
||||||
totalEarning: 38838,
|
|
||||||
image: '/images/apps/ecommerce/product-14.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
categoryTitle: 'Growsari',
|
|
||||||
description: 'Shop grocery Items through at best prices in India.',
|
|
||||||
totalProduct: 7389,
|
|
||||||
totalEarning: 72652,
|
|
||||||
image: '/images/apps/ecommerce/product-26.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 9,
|
|
||||||
categoryTitle: 'Computer Accessories',
|
|
||||||
description: 'Enhance your computing experience with our range of computer accessories.',
|
|
||||||
totalProduct: 9876,
|
|
||||||
totalEarning: 65421,
|
|
||||||
image: '/images/apps/ecommerce/product-17.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 10,
|
|
||||||
categoryTitle: 'Fitness Tracker',
|
|
||||||
description: 'Monitor your health and fitness goals with our range of advanced fitness trackers.',
|
|
||||||
totalProduct: 1987,
|
|
||||||
totalEarning: 32067,
|
|
||||||
image: '/images/apps/ecommerce/product-10.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 11,
|
|
||||||
categoryTitle: 'Smart Home Devices',
|
|
||||||
description: 'Transform your home into a smart home with our innovative smart home devices.',
|
|
||||||
totalProduct: 2345,
|
|
||||||
totalEarning: 87654,
|
|
||||||
image: '/images/apps/ecommerce/product-11.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 12,
|
|
||||||
categoryTitle: 'Audio Speakers',
|
|
||||||
description: 'Immerse yourself in rich audio quality with our wide range of speakers.',
|
|
||||||
totalProduct: 5678,
|
|
||||||
totalEarning: 32145,
|
|
||||||
image: '/images/apps/ecommerce/product-2.png'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// Column Definitions
|
// Column Definitions
|
||||||
const columnHelper = createColumnHelper<CategoryWithActionsType>()
|
const columnHelper = createColumnHelper<CategoryWithActionsType>()
|
||||||
|
|
||||||
@ -211,8 +106,28 @@ const ProductCategoryTable = () => {
|
|||||||
// States
|
// States
|
||||||
const [addCategoryOpen, setAddCategoryOpen] = useState(false)
|
const [addCategoryOpen, setAddCategoryOpen] = useState(false)
|
||||||
const [rowSelection, setRowSelection] = useState({})
|
const [rowSelection, setRowSelection] = useState({})
|
||||||
const [data, setData] = useState(...[categoryData])
|
const [currentPage, setCurrentPage] = useState(1)
|
||||||
const [globalFilter, setGlobalFilter] = useState('')
|
const [pageSize, setPageSize] = useState(10)
|
||||||
|
|
||||||
|
// Fetch products with pagination and search
|
||||||
|
const { data, isLoading, error, isFetching } = useCategoriesQuery.getCategories({
|
||||||
|
page: currentPage,
|
||||||
|
limit: pageSize
|
||||||
|
})
|
||||||
|
|
||||||
|
const categories = data?.categories ?? []
|
||||||
|
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<HTMLInputElement>) => {
|
||||||
|
const newPageSize = parseInt(event.target.value, 10)
|
||||||
|
setPageSize(newPageSize)
|
||||||
|
setCurrentPage(0) // Reset to first page
|
||||||
|
}, [])
|
||||||
|
|
||||||
const columns = useMemo<ColumnDef<CategoryWithActionsType, any>[]>(
|
const columns = useMemo<ColumnDef<CategoryWithActionsType, any>[]>(
|
||||||
() => [
|
() => [
|
||||||
@ -238,31 +153,27 @@ const ProductCategoryTable = () => {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
columnHelper.accessor('categoryTitle', {
|
columnHelper.accessor('name', {
|
||||||
header: 'Categories',
|
header: 'Name',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className='flex items-center gap-3'>
|
<div className='flex items-center gap-3'>
|
||||||
<img src={row.original.image} width={38} height={38} className='rounded bg-actionHover' />
|
{/* <img src={row.original.image} width={38} height={38} className='rounded bg-actionHover' /> */}
|
||||||
<div className='flex flex-col items-start'>
|
<div className='flex flex-col items-start'>
|
||||||
<Typography className='font-medium' color='text.primary'>
|
<Typography className='font-medium' color='text.primary'>
|
||||||
{row.original.categoryTitle}
|
{row.original.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant='body2'>{row.original.description}</Typography>
|
<Typography variant='body2'>{row.original.description}</Typography>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('totalProduct', {
|
columnHelper.accessor('description', {
|
||||||
header: 'Total Products',
|
header: 'Decription',
|
||||||
cell: ({ row }) => <Typography>{row.original.totalProduct.toLocaleString()}</Typography>
|
cell: ({ row }) => <Typography>{row.original.description || '-'}</Typography>
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('totalEarning', {
|
columnHelper.accessor('business_type', {
|
||||||
header: 'Total Earning',
|
header: 'Business Type',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => <Typography>{row.original.business_type}</Typography>
|
||||||
<Typography>
|
|
||||||
{row.original.totalEarning.toLocaleString('en-IN', { style: 'currency', currency: 'USD' })}
|
|
||||||
</Typography>
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('actions', {
|
columnHelper.accessor('actions', {
|
||||||
header: 'Actions',
|
header: 'Actions',
|
||||||
@ -279,7 +190,7 @@ const ProductCategoryTable = () => {
|
|||||||
{
|
{
|
||||||
text: 'Delete',
|
text: 'Delete',
|
||||||
icon: 'tabler-trash',
|
icon: 'tabler-trash',
|
||||||
menuItemProps: { onClick: () => setData(data.filter(category => category.id !== row.original.id)) }
|
menuItemProps: { onClick: () => console.log('click') }
|
||||||
},
|
},
|
||||||
{ text: 'Duplicate', icon: 'tabler-copy' }
|
{ text: 'Duplicate', icon: 'tabler-copy' }
|
||||||
]}
|
]}
|
||||||
@ -294,32 +205,24 @@ const ProductCategoryTable = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: data as categoryType[],
|
data: categories as Category[],
|
||||||
columns,
|
columns,
|
||||||
filterFns: {
|
filterFns: {
|
||||||
fuzzy: fuzzyFilter
|
fuzzy: fuzzyFilter
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
rowSelection,
|
rowSelection,
|
||||||
globalFilter
|
|
||||||
},
|
|
||||||
initialState: {
|
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize: 10
|
pageIndex: currentPage, // <= penting!
|
||||||
|
pageSize
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enableRowSelection: true, //enable row selection for all rows
|
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,
|
onRowSelectionChange: setRowSelection,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
onGlobalFilterChange: setGlobalFilter,
|
// Disable client-side pagination since we're handling it server-side
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
manualPagination: true,
|
||||||
getSortedRowModel: getSortedRowModel(),
|
pageCount: Math.ceil(totalCount / pageSize)
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getFacetedRowModel: getFacetedRowModel(),
|
|
||||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
||||||
getFacetedMinMaxValues: getFacetedMinMaxValues()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -327,9 +230,9 @@ const ProductCategoryTable = () => {
|
|||||||
<Card>
|
<Card>
|
||||||
<div className='flex flex-wrap justify-between gap-4 p-6'>
|
<div className='flex flex-wrap justify-between gap-4 p-6'>
|
||||||
<DebouncedInput
|
<DebouncedInput
|
||||||
value={globalFilter ?? ''}
|
value={'search'}
|
||||||
onChange={value => setGlobalFilter(String(value))}
|
onChange={value => console.log(value)}
|
||||||
placeholder='Search'
|
placeholder='Search Product'
|
||||||
className='max-sm:is-full'
|
className='max-sm:is-full'
|
||||||
/>
|
/>
|
||||||
<div className='flex max-sm:flex-col items-start sm:items-center gap-4 max-sm:is-full'>
|
<div className='flex max-sm:flex-col items-start sm:items-center gap-4 max-sm:is-full'>
|
||||||
@ -354,74 +257,103 @@ const ProductCategoryTable = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='overflow-x-auto'>
|
<div className='overflow-x-auto'>
|
||||||
<table className={tableStyles.table}>
|
{isLoading ? (
|
||||||
<thead>
|
<Loading />
|
||||||
{table.getHeaderGroups().map(headerGroup => (
|
) : (
|
||||||
<tr key={headerGroup.id}>
|
<table className={tableStyles.table}>
|
||||||
{headerGroup.headers.map(header => (
|
<thead>
|
||||||
<th key={header.id}>
|
{table.getHeaderGroups().map(headerGroup => (
|
||||||
{header.isPlaceholder ? null : (
|
<tr key={headerGroup.id}>
|
||||||
<>
|
{headerGroup.headers.map(header => (
|
||||||
<div
|
<th key={header.id}>
|
||||||
className={classnames({
|
{header.isPlaceholder ? null : (
|
||||||
'flex items-center': header.column.getIsSorted(),
|
<>
|
||||||
'cursor-pointer select-none': header.column.getCanSort()
|
<div
|
||||||
})}
|
className={classnames({
|
||||||
onClick={header.column.getToggleSortingHandler()}
|
'flex items-center': header.column.getIsSorted(),
|
||||||
>
|
'cursor-pointer select-none': header.column.getCanSort()
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
})}
|
||||||
{{
|
onClick={header.column.getToggleSortingHandler()}
|
||||||
asc: <i className='tabler-chevron-up text-xl' />,
|
>
|
||||||
desc: <i className='tabler-chevron-down text-xl' />
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
}[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
|
{{
|
||||||
</div>
|
asc: <i className='tabler-chevron-up text-xl' />,
|
||||||
</>
|
desc: <i className='tabler-chevron-down text-xl' />
|
||||||
)}
|
}[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
|
||||||
</th>
|
</div>
|
||||||
))}
|
</>
|
||||||
</tr>
|
)}
|
||||||
))}
|
</th>
|
||||||
</thead>
|
))}
|
||||||
{table.getFilteredRowModel().rows.length === 0 ? (
|
</tr>
|
||||||
<tbody>
|
))}
|
||||||
<tr>
|
</thead>
|
||||||
<td colSpan={table.getVisibleFlatColumns().length} className='text-center'>
|
{table.getFilteredRowModel().rows.length === 0 ? (
|
||||||
No data available
|
<tbody>
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td colSpan={table.getVisibleFlatColumns().length} className='text-center'>
|
||||||
</tbody>
|
No data available
|
||||||
) : (
|
</td>
|
||||||
<tbody>
|
</tr>
|
||||||
{table
|
</tbody>
|
||||||
.getRowModel()
|
) : (
|
||||||
.rows.slice(0, table.getState().pagination.pageSize)
|
<tbody>
|
||||||
.map(row => {
|
{table
|
||||||
return (
|
.getRowModel()
|
||||||
<tr key={row.id} className={classnames({ selected: row.getIsSelected() })}>
|
.rows.slice(0, table.getState().pagination.pageSize)
|
||||||
{row.getVisibleCells().map(cell => (
|
.map(row => {
|
||||||
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
|
return (
|
||||||
))}
|
<tr key={row.id} className={classnames({ selected: row.getIsSelected() })}>
|
||||||
</tr>
|
{row.getVisibleCells().map(cell => (
|
||||||
)
|
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
|
||||||
})}
|
))}
|
||||||
</tbody>
|
</tr>
|
||||||
)}
|
)
|
||||||
</table>
|
})}
|
||||||
|
</tbody>
|
||||||
|
)}
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isFetching && !isLoading && (
|
||||||
|
<Box
|
||||||
|
position='absolute'
|
||||||
|
top={0}
|
||||||
|
left={0}
|
||||||
|
right={0}
|
||||||
|
bottom={0}
|
||||||
|
display='flex'
|
||||||
|
alignItems='center'
|
||||||
|
justifyContent='center'
|
||||||
|
bgcolor='rgba(255,255,255,0.7)'
|
||||||
|
zIndex={1}
|
||||||
|
>
|
||||||
|
<CircularProgress size={24} />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
component={() => <TablePaginationComponent table={table} />}
|
component={() => (
|
||||||
count={table.getFilteredRowModel().rows.length}
|
<TablePaginationComponent
|
||||||
rowsPerPage={table.getState().pagination.pageSize}
|
pageIndex={currentPage}
|
||||||
page={table.getState().pagination.pageIndex}
|
pageSize={pageSize}
|
||||||
onPageChange={(_, page) => {
|
totalCount={totalCount}
|
||||||
table.setPageIndex(page)
|
onPageChange={handlePageChange}
|
||||||
}}
|
/>
|
||||||
|
)}
|
||||||
|
count={totalCount}
|
||||||
|
rowsPerPage={pageSize}
|
||||||
|
page={currentPage}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
onRowsPerPageChange={handlePageSizeChange}
|
||||||
|
rowsPerPageOptions={[10, 25, 50]}
|
||||||
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
<AddCategoryDrawer
|
<AddCategoryDrawer
|
||||||
open={addCategoryOpen}
|
open={addCategoryOpen}
|
||||||
categoryData={data}
|
categoryData={categories}
|
||||||
setData={setData}
|
setData={() => {}}
|
||||||
handleClose={() => setAddCategoryOpen(!addCategoryOpen)}
|
handleClose={() => setAddCategoryOpen(!addCategoryOpen)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,63 +1,50 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
// React Imports
|
// React Imports
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
// Next Imports
|
// Next Imports
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useParams } from 'next/navigation'
|
import { useParams } from 'next/navigation'
|
||||||
|
|
||||||
// MUI Imports
|
// MUI Imports
|
||||||
|
import Button from '@mui/material/Button'
|
||||||
import Card from '@mui/material/Card'
|
import Card from '@mui/material/Card'
|
||||||
import CardHeader from '@mui/material/CardHeader'
|
import CardHeader from '@mui/material/CardHeader'
|
||||||
import Button from '@mui/material/Button'
|
|
||||||
import Chip from '@mui/material/Chip'
|
|
||||||
import Checkbox from '@mui/material/Checkbox'
|
import Checkbox from '@mui/material/Checkbox'
|
||||||
|
import Chip from '@mui/material/Chip'
|
||||||
import Divider from '@mui/material/Divider'
|
import Divider from '@mui/material/Divider'
|
||||||
import IconButton from '@mui/material/IconButton'
|
import IconButton from '@mui/material/IconButton'
|
||||||
import Switch from '@mui/material/Switch'
|
|
||||||
import MenuItem from '@mui/material/MenuItem'
|
import MenuItem from '@mui/material/MenuItem'
|
||||||
import TablePagination from '@mui/material/TablePagination'
|
import TablePagination from '@mui/material/TablePagination'
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
import type { TextFieldProps } from '@mui/material/TextField'
|
import type { TextFieldProps } from '@mui/material/TextField'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
|
||||||
// Third-party Imports
|
// Third-party Imports
|
||||||
import classnames from 'classnames'
|
|
||||||
import { rankItem } from '@tanstack/match-sorter-utils'
|
|
||||||
import {
|
|
||||||
createColumnHelper,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
useReactTable,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getFacetedRowModel,
|
|
||||||
getFacetedUniqueValues,
|
|
||||||
getFacetedMinMaxValues,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel
|
|
||||||
} from '@tanstack/react-table'
|
|
||||||
import type { ColumnDef, FilterFn } from '@tanstack/react-table'
|
|
||||||
import type { RankingInfo } from '@tanstack/match-sorter-utils'
|
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
|
// Type Imports
|
||||||
import type { ThemeColor } from '@core/types'
|
|
||||||
import type { Locale } from '@configs/i18n'
|
import type { Locale } from '@configs/i18n'
|
||||||
import type { ProductType } from '@/types/apps/ecommerceTypes'
|
|
||||||
|
|
||||||
// Component Imports
|
// Component Imports
|
||||||
import TableFilters from './TableFilters'
|
import TablePaginationComponent from '@components/TablePaginationComponent'
|
||||||
import CustomAvatar from '@core/components/mui/Avatar'
|
|
||||||
import CustomTextField from '@core/components/mui/TextField'
|
import CustomTextField from '@core/components/mui/TextField'
|
||||||
import OptionMenu from '@core/components/option-menu'
|
import OptionMenu from '@core/components/option-menu'
|
||||||
import TablePaginationComponent from '@components/TablePaginationComponent'
|
import TableFilters from './TableFilters'
|
||||||
|
|
||||||
// Util Imports
|
// Util Imports
|
||||||
import { getLocalizedUrl } from '@/utils/i18n'
|
import { getLocalizedUrl } from '@/utils/i18n'
|
||||||
|
|
||||||
// Style Imports
|
// Style Imports
|
||||||
import tableStyles from '@core/styles/table.module.css'
|
import tableStyles from '@core/styles/table.module.css'
|
||||||
|
import { Box, CircularProgress } from '@mui/material'
|
||||||
|
import Loading from '../../../../../components/layout/shared/Loading'
|
||||||
import { useProductsQuery } from '../../../../../services/queries/products'
|
import { useProductsQuery } from '../../../../../services/queries/products'
|
||||||
import { Product } from '../../../../../types/services/products'
|
import { Product } from '../../../../../types/services/product'
|
||||||
|
|
||||||
declare module '@tanstack/table-core' {
|
declare module '@tanstack/table-core' {
|
||||||
interface FilterFns {
|
interface FilterFns {
|
||||||
@ -72,20 +59,6 @@ type ProductWithActionsType = Product & {
|
|||||||
actions?: string
|
actions?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductCategoryType = {
|
|
||||||
[key: string]: {
|
|
||||||
icon: string
|
|
||||||
color: ThemeColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type productStatusType = {
|
|
||||||
[key: string]: {
|
|
||||||
title: string
|
|
||||||
color: ThemeColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
|
const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
|
||||||
// Rank the item
|
// Rank the item
|
||||||
const itemRank = rankItem(row.getValue(columnId), value)
|
const itemRank = rankItem(row.getValue(columnId), value)
|
||||||
@ -128,36 +101,37 @@ const DebouncedInput = ({
|
|||||||
return <CustomTextField {...props} value={value} onChange={e => setValue(e.target.value)} />
|
return <CustomTextField {...props} value={value} onChange={e => setValue(e.target.value)} />
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vars
|
|
||||||
const productCategoryObj: ProductCategoryType = {
|
|
||||||
Accessories: { icon: 'tabler-headphones', color: 'error' },
|
|
||||||
'Home Decor': { icon: 'tabler-smart-home', color: 'info' },
|
|
||||||
Electronics: { icon: 'tabler-device-laptop', color: 'primary' },
|
|
||||||
Shoes: { icon: 'tabler-shoe', color: 'success' },
|
|
||||||
Office: { icon: 'tabler-briefcase', color: 'warning' },
|
|
||||||
Games: { icon: 'tabler-device-gamepad-2', color: 'secondary' }
|
|
||||||
}
|
|
||||||
|
|
||||||
const productStatusObj: productStatusType = {
|
|
||||||
Scheduled: { title: 'Scheduled', color: 'warning' },
|
|
||||||
Published: { title: 'Publish', color: 'success' },
|
|
||||||
Inactive: { title: 'Inactive', color: 'error' }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column Definitions
|
// Column Definitions
|
||||||
const columnHelper = createColumnHelper<ProductWithActionsType>()
|
const columnHelper = createColumnHelper<ProductWithActionsType>()
|
||||||
|
|
||||||
const ProductListTable = () => {
|
const ProductListTable = () => {
|
||||||
// States
|
|
||||||
const { data } = useProductsQuery.getProducts()
|
|
||||||
const [rowSelection, setRowSelection] = useState({})
|
const [rowSelection, setRowSelection] = useState({})
|
||||||
const [filteredData, setFilteredData] = useState(data?.products ?? [])
|
const [currentPage, setCurrentPage] = useState(1)
|
||||||
const [globalFilter, setGlobalFilter] = useState('')
|
const [pageSize, setPageSize] = useState(10)
|
||||||
|
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
const { lang: locale } = useParams()
|
const { lang: locale } = useParams()
|
||||||
|
|
||||||
|
// Fetch products with pagination and search
|
||||||
|
const { data, isLoading, error, isFetching } = useProductsQuery.getProducts({
|
||||||
|
page: currentPage,
|
||||||
|
limit: pageSize
|
||||||
|
})
|
||||||
|
|
||||||
|
const products = data?.products ?? []
|
||||||
|
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<HTMLInputElement>) => {
|
||||||
|
const newPageSize = parseInt(event.target.value, 10)
|
||||||
|
setPageSize(newPageSize)
|
||||||
|
setCurrentPage(0) // Reset to first page
|
||||||
|
}, [])
|
||||||
|
|
||||||
const columns = useMemo<ColumnDef<ProductWithActionsType, any>[]>(
|
const columns = useMemo<ColumnDef<ProductWithActionsType, any>[]>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@ -226,7 +200,7 @@ const ProductListTable = () => {
|
|||||||
}),
|
}),
|
||||||
columnHelper.accessor('is_active', {
|
columnHelper.accessor('is_active', {
|
||||||
header: 'Status',
|
header: 'Status',
|
||||||
cell: ({row}) => (
|
cell: ({ row }) => (
|
||||||
<Chip
|
<Chip
|
||||||
label={row.original.is_active ? 'Active' : 'Inactive'}
|
label={row.original.is_active ? 'Active' : 'Inactive'}
|
||||||
variant='tonal'
|
variant='tonal'
|
||||||
@ -261,48 +235,40 @@ const ProductListTable = () => {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[data, filteredData]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: filteredData as Product[],
|
data: products as Product[],
|
||||||
columns,
|
columns,
|
||||||
filterFns: {
|
filterFns: {
|
||||||
fuzzy: fuzzyFilter
|
fuzzy: fuzzyFilter
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
rowSelection,
|
rowSelection,
|
||||||
globalFilter
|
|
||||||
},
|
|
||||||
initialState: {
|
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize: 10
|
pageIndex: currentPage, // <= penting!
|
||||||
|
pageSize
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enableRowSelection: true, //enable row selection for all rows
|
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,
|
onRowSelectionChange: setRowSelection,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
onGlobalFilterChange: setGlobalFilter,
|
// Disable client-side pagination since we're handling it server-side
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
manualPagination: true,
|
||||||
getSortedRowModel: getSortedRowModel(),
|
pageCount: Math.ceil(totalCount / pageSize)
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getFacetedRowModel: getFacetedRowModel(),
|
|
||||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
||||||
getFacetedMinMaxValues: getFacetedMinMaxValues()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader title='Filters' />
|
<CardHeader title='Filters' />
|
||||||
<TableFilters setData={setFilteredData} productData={data?.products} />
|
<TableFilters setData={() => {}} productData={[]} />
|
||||||
<Divider />
|
<Divider />
|
||||||
<div className='flex flex-wrap justify-between gap-4 p-6'>
|
<div className='flex flex-wrap justify-between gap-4 p-6'>
|
||||||
<DebouncedInput
|
<DebouncedInput
|
||||||
value={globalFilter ?? ''}
|
value={'search'}
|
||||||
onChange={value => setGlobalFilter(String(value))}
|
onChange={value => console.log(value)}
|
||||||
placeholder='Search Product'
|
placeholder='Search Product'
|
||||||
className='max-sm:is-full'
|
className='max-sm:is-full'
|
||||||
/>
|
/>
|
||||||
@ -337,68 +303,98 @@ const ProductListTable = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='overflow-x-auto'>
|
<div className='overflow-x-auto'>
|
||||||
<table className={tableStyles.table}>
|
{isLoading ? (
|
||||||
<thead>
|
<Loading />
|
||||||
{table.getHeaderGroups().map(headerGroup => (
|
) : (
|
||||||
<tr key={headerGroup.id}>
|
<table className={tableStyles.table}>
|
||||||
{headerGroup.headers.map(header => (
|
<thead>
|
||||||
<th key={header.id}>
|
{table.getHeaderGroups().map(headerGroup => (
|
||||||
{header.isPlaceholder ? null : (
|
<tr key={headerGroup.id}>
|
||||||
<>
|
{headerGroup.headers.map(header => (
|
||||||
<div
|
<th key={header.id}>
|
||||||
className={classnames({
|
{header.isPlaceholder ? null : (
|
||||||
'flex items-center': header.column.getIsSorted(),
|
<>
|
||||||
'cursor-pointer select-none': header.column.getCanSort()
|
<div
|
||||||
})}
|
className={classnames({
|
||||||
onClick={header.column.getToggleSortingHandler()}
|
'flex items-center': header.column.getIsSorted(),
|
||||||
>
|
'cursor-pointer select-none': header.column.getCanSort()
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
})}
|
||||||
{{
|
onClick={header.column.getToggleSortingHandler()}
|
||||||
asc: <i className='tabler-chevron-up text-xl' />,
|
>
|
||||||
desc: <i className='tabler-chevron-down text-xl' />
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
}[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
|
{{
|
||||||
</div>
|
asc: <i className='tabler-chevron-up text-xl' />,
|
||||||
</>
|
desc: <i className='tabler-chevron-down text-xl' />
|
||||||
)}
|
}[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
|
||||||
</th>
|
</div>
|
||||||
))}
|
</>
|
||||||
</tr>
|
)}
|
||||||
))}
|
</th>
|
||||||
</thead>
|
))}
|
||||||
{table.getFilteredRowModel().rows.length === 0 ? (
|
</tr>
|
||||||
<tbody>
|
))}
|
||||||
<tr>
|
</thead>
|
||||||
<td colSpan={table.getVisibleFlatColumns().length} className='text-center'>
|
{table.getFilteredRowModel().rows.length === 0 ? (
|
||||||
No data available
|
<tbody>
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td colSpan={table.getVisibleFlatColumns().length} className='text-center'>
|
||||||
</tbody>
|
No data available
|
||||||
) : (
|
</td>
|
||||||
<tbody>
|
</tr>
|
||||||
{table
|
</tbody>
|
||||||
.getRowModel()
|
) : (
|
||||||
.rows.slice(0, table.getState().pagination.pageSize)
|
<tbody>
|
||||||
.map(row => {
|
{table
|
||||||
return (
|
.getRowModel()
|
||||||
<tr key={row.id} className={classnames({ selected: row.getIsSelected() })}>
|
.rows.slice(0, table.getState().pagination.pageSize)
|
||||||
{row.getVisibleCells().map(cell => (
|
.map(row => {
|
||||||
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
|
return (
|
||||||
))}
|
<tr key={row.id} className={classnames({ selected: row.getIsSelected() })}>
|
||||||
</tr>
|
{row.getVisibleCells().map(cell => (
|
||||||
)
|
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
|
||||||
})}
|
))}
|
||||||
</tbody>
|
</tr>
|
||||||
)}
|
)
|
||||||
</table>
|
})}
|
||||||
|
</tbody>
|
||||||
|
)}
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isFetching && !isLoading && (
|
||||||
|
<Box
|
||||||
|
position='absolute'
|
||||||
|
top={0}
|
||||||
|
left={0}
|
||||||
|
right={0}
|
||||||
|
bottom={0}
|
||||||
|
display='flex'
|
||||||
|
alignItems='center'
|
||||||
|
justifyContent='center'
|
||||||
|
bgcolor='rgba(255,255,255,0.7)'
|
||||||
|
zIndex={1}
|
||||||
|
>
|
||||||
|
<CircularProgress size={24} />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TablePagination
|
<TablePagination
|
||||||
component={() => <TablePaginationComponent table={table} />}
|
component={() => (
|
||||||
count={table.getFilteredRowModel().rows.length}
|
<TablePaginationComponent
|
||||||
rowsPerPage={table.getState().pagination.pageSize}
|
pageIndex={currentPage}
|
||||||
page={table.getState().pagination.pageIndex}
|
pageSize={pageSize}
|
||||||
onPageChange={(_, page) => {
|
totalCount={totalCount}
|
||||||
table.setPageIndex(page)
|
onPageChange={handlePageChange}
|
||||||
}}
|
/>
|
||||||
|
)}
|
||||||
|
count={totalCount}
|
||||||
|
rowsPerPage={pageSize}
|
||||||
|
page={currentPage}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
onRowsPerPageChange={handlePageSizeChange}
|
||||||
|
rowsPerPageOptions={[10, 25, 50]}
|
||||||
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import type { ProductType } from '@/types/apps/ecommerceTypes'
|
|||||||
|
|
||||||
// Component Imports
|
// Component Imports
|
||||||
import CustomTextField from '@core/components/mui/TextField'
|
import CustomTextField from '@core/components/mui/TextField'
|
||||||
import { Product } from '../../../../../types/services/products'
|
import { Product } from '../../../../../types/services/product'
|
||||||
|
|
||||||
type ProductStockType = { [key: string]: boolean }
|
type ProductStockType = { [key: string]: boolean }
|
||||||
|
|
||||||
@ -21,13 +21,7 @@ const productStockObj: ProductStockType = {
|
|||||||
'Out of Stock': false
|
'Out of Stock': false
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableFilters = ({
|
const TableFilters = ({ setData, productData }: { setData: (data: Product[]) => void; productData?: Product[] }) => {
|
||||||
setData,
|
|
||||||
productData
|
|
||||||
}: {
|
|
||||||
setData: (data: Product[]) => void
|
|
||||||
productData?: Product[]
|
|
||||||
}) => {
|
|
||||||
// States
|
// States
|
||||||
const [category, setCategory] = useState<Product['category_id']>('')
|
const [category, setCategory] = useState<Product['category_id']>('')
|
||||||
const [stock, setStock] = useState('')
|
const [stock, setStock] = useState('')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user