feat(FE): Add skeleton components for master data tables

This commit is contained in:
rstubryan
2026-03-02 13:50:25 +07:00
parent 1341b1ff53
commit f2b59ded3c
26 changed files with 967 additions and 294 deletions
@@ -11,13 +11,13 @@ import DebouncedTextInput from '@/components/input/DebouncedTextInput';
import Button from '@/components/Button';
import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import RequirePermission from '@/components/helper/RequirePermission';
import AreaTableSkeleton from '@/components/pages/master-data/area/skeleton/AreaTableSkeleton';
import { Area } from '@/types/api/master-data/area';
import { AreaApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -241,26 +241,44 @@ const AreasTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Area>
data={isResponseSuccess(areas) ? areas?.data : []}
columns={areasColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(areas) ? areas?.meta?.page : 0}
totalItems={
isResponseSuccess(areas) ? areas?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full': isResponseSuccess(areas) && areas?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(areas) || areas.data?.length === 0 ? (
<div className='p-3'>
<AreaTableSkeleton
columns={areasColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Area>
data={isResponseSuccess(areas) ? areas?.data : []}
columns={areasColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(areas) ? areas?.meta?.page : 0}
totalItems={
isResponseSuccess(areas) ? areas?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Area } from '@/types/api/master-data/area';
import { ColumnDef } from '@tanstack/react-table';
const AreaTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no area data displayed. Enter area data to get started.',
}: {
columns: ColumnDef<Area>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default AreaTableSkeleton;
@@ -14,10 +14,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import BankTableSkeleton from '@/components/pages/master-data/bank/skeleton/BankTableSkeleton';
import { Bank } from '@/types/api/master-data/bank';
import { BankApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -254,26 +254,44 @@ const BanksTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Bank>
data={isResponseSuccess(banks) ? banks?.data : []}
columns={banksColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(banks) ? banks?.meta?.page : 0}
totalItems={
isResponseSuccess(banks) ? banks?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full': isResponseSuccess(banks) && banks?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(banks) || banks.data?.length === 0 ? (
<div className='p-3'>
<BankTableSkeleton
columns={banksColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Bank>
data={isResponseSuccess(banks) ? banks?.data : []}
columns={banksColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(banks) ? banks?.meta?.page : 0}
totalItems={
isResponseSuccess(banks) ? banks?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Bank } from '@/types/api/master-data/bank';
import { ColumnDef } from '@tanstack/react-table';
const BankTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no bank data displayed. Enter bank data to get started.',
}: {
columns: ColumnDef<Bank>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default BankTableSkeleton;
@@ -14,10 +14,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import CustomerTableSkeleton from '@/components/pages/master-data/customer/skeleton/CustomerTableSkeleton';
import { Customer } from '@/types/api/master-data/customer';
import { CustomerApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -261,27 +261,42 @@ const CustomersTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Customer>
data={isResponseSuccess(customers) ? customers?.data : []}
columns={customersColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(customers) ? customers?.meta?.page : 0}
totalItems={
isResponseSuccess(customers) ? customers?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(customers) && customers?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(customers) || customers.data?.length === 0 ? (
<div className='p-3'>
<CustomerTableSkeleton
columns={customersColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Customer>
data={customers?.data}
columns={customersColumns}
pageSize={tableFilterState.pageSize}
page={customers?.meta?.page ?? 0}
totalItems={customers?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Customer } from '@/types/api/master-data/customer';
import { ColumnDef } from '@tanstack/react-table';
const CustomerTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no customer data displayed. Enter customer data to get started.',
}: {
columns: ColumnDef<Customer>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default CustomerTableSkeleton;
@@ -14,10 +14,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import FlockTableSkeleton from '@/components/pages/master-data/flock/skeleton/FlockTableSkeleton';
import { Flock } from '@/types/api/master-data/flock';
import { FlockApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -249,27 +249,42 @@ const FlockTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Flock>
data={isResponseSuccess(flocks) ? flocks?.data : []}
columns={flocksColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(flocks) ? flocks?.meta?.page : 0}
totalItems={
isResponseSuccess(flocks) ? flocks?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(flocks) && flocks?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(flocks) || flocks.data?.length === 0 ? (
<div className='p-3'>
<FlockTableSkeleton
columns={flocksColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Flock>
data={flocks?.data}
columns={flocksColumns}
pageSize={tableFilterState.pageSize}
page={flocks?.meta?.page ?? 0}
totalItems={flocks?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Flock } from '@/types/api/master-data/flock';
import { ColumnDef } from '@tanstack/react-table';
const FlockTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no flock data displayed. Enter flock data to get started.',
}: {
columns: ColumnDef<Flock>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default FlockTableSkeleton;
@@ -14,10 +14,11 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import KandangTableSkeleton from '@/components/pages/master-data/kandang/skeleton/KandangTableSkeleton';
import { Kandang } from '@/types/api/master-data/kandang';
import { KandangApi } from '@/services/api/master-data';
import { cn, formatNumber } from '@/lib/helper';
import { formatNumber } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -258,27 +259,42 @@ const KandangsTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Kandang>
data={isResponseSuccess(kandangs) ? kandangs?.data : []}
columns={kandangsColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(kandangs) ? kandangs?.meta?.page : 0}
totalItems={
isResponseSuccess(kandangs) ? kandangs?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(kandangs) && kandangs?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(kandangs) || kandangs.data?.length === 0 ? (
<div className='p-3'>
<KandangTableSkeleton
columns={kandangsColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Kandang>
data={kandangs?.data}
columns={kandangsColumns}
pageSize={tableFilterState.pageSize}
page={kandangs?.meta?.page ?? 0}
totalItems={kandangs?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Kandang } from '@/types/api/master-data/kandang';
import { ColumnDef } from '@tanstack/react-table';
const KandangTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no kandang data displayed. Enter kandang data to get started.',
}: {
columns: ColumnDef<Kandang>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default KandangTableSkeleton;
@@ -14,10 +14,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import LocationTableSkeleton from '@/components/pages/master-data/location/skeleton/LocationTableSkeleton';
import { Location } from '@/types/api/master-data/location';
import { LocationApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -253,27 +253,42 @@ const LocationsTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Location>
data={isResponseSuccess(locations) ? locations?.data : []}
columns={locationsColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(locations) ? locations?.meta?.page : 0}
totalItems={
isResponseSuccess(locations) ? locations?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(locations) && locations?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(locations) || locations.data?.length === 0 ? (
<div className='p-3'>
<LocationTableSkeleton
columns={locationsColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Location>
data={locations?.data}
columns={locationsColumns}
pageSize={tableFilterState.pageSize}
page={locations?.meta?.page ?? 0}
totalItems={locations?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Location } from '@/types/api/master-data/location';
import { ColumnDef } from '@tanstack/react-table';
const LocationTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no location data displayed. Enter location data to get started.',
}: {
columns: ColumnDef<Location>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default LocationTableSkeleton;
@@ -14,10 +14,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import NonstockTableSkeleton from '@/components/pages/master-data/nonstock/skeleton/NonstockTableSkeleton';
import { Nonstock } from '@/types/api/master-data/nonstock';
import { NonstockApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -258,27 +258,42 @@ const NonstocksTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Nonstock>
data={isResponseSuccess(nonstocks) ? nonstocks?.data : []}
columns={nonstocksColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(nonstocks) ? nonstocks?.meta?.page : 0}
totalItems={
isResponseSuccess(nonstocks) ? nonstocks?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(nonstocks) && nonstocks?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(nonstocks) || nonstocks.data?.length === 0 ? (
<div className='p-3'>
<NonstockTableSkeleton
columns={nonstocksColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Nonstock>
data={nonstocks?.data}
columns={nonstocksColumns}
pageSize={tableFilterState.pageSize}
page={nonstocks?.meta?.page ?? 0}
totalItems={nonstocks?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Nonstock } from '@/types/api/master-data/nonstock';
import { ColumnDef } from '@tanstack/react-table';
const NonstockTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no nonstock data displayed. Enter nonstock data to get started.',
}: {
columns: ColumnDef<Nonstock>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default NonstockTableSkeleton;
@@ -20,10 +20,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import ProductCategoryTableSkeleton from '@/components/pages/master-data/product-category/skeleton/ProductCategoryTableSkeleton';
import { ProductCategory } from '@/types/api/master-data/product-category';
import { ProductCategoryApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { useUiStore } from '@/stores/ui/ui.store';
@@ -282,38 +282,43 @@ const ProductCategoryTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<ProductCategory>
data={
isResponseSuccess(productCategories)
? productCategories?.data
: []
}
columns={productCategoryColumns}
pageSize={tableFilterState.pageSize}
page={
isResponseSuccess(productCategories)
? productCategories?.meta?.page
: 0
}
totalItems={
isResponseSuccess(productCategories)
? productCategories?.meta?.total_results
: 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(productCategories) &&
productCategories?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(productCategories) ||
productCategories.data?.length === 0 ? (
<div className='p-3'>
<ProductCategoryTableSkeleton
columns={productCategoryColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<ProductCategory>
data={productCategories?.data}
columns={productCategoryColumns}
pageSize={tableFilterState.pageSize}
page={productCategories?.meta?.page ?? 0}
totalItems={productCategories?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { ProductCategory } from '@/types/api/master-data/product-category';
import { ColumnDef } from '@tanstack/react-table';
const ProductCategoryTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no product category data displayed. Enter product category data to get started.',
}: {
columns: ColumnDef<ProductCategory>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default ProductCategoryTableSkeleton;
@@ -14,10 +14,11 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import ProductTableSkeleton from '@/components/pages/master-data/product/skeleton/ProductTableSkeleton';
import { Product } from '@/types/api/master-data/product';
import { ProductApi } from '@/services/api/master-data';
import { cn, formatCurrency } from '@/lib/helper';
import { formatCurrency } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -303,27 +304,42 @@ const ProductsTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Product>
data={isResponseSuccess(products) ? products?.data : []}
columns={productsColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(products) ? products?.meta?.page : 0}
totalItems={
isResponseSuccess(products) ? products?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(products) && products?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(products) || products.data?.length === 0 ? (
<div className='p-3'>
<ProductTableSkeleton
columns={productsColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Product>
data={products?.data}
columns={productsColumns}
pageSize={tableFilterState.pageSize}
page={products?.meta?.page ?? 0}
totalItems={products?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Product } from '@/types/api/master-data/product';
import { ColumnDef } from '@tanstack/react-table';
const ProductTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no product data displayed. Enter product data to get started.',
}: {
columns: ColumnDef<Product>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default ProductTableSkeleton;
@@ -13,10 +13,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import ProductionStandardTableSkeleton from '@/components/pages/master-data/production-standard/skeleton/ProductionStandardTableSkeleton';
import { ProductionStandard } from '@/types/api/master-data/production-standard';
import { ProductionStandardApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
const RowOptionsMenu = ({
@@ -201,25 +201,38 @@ const ProductionStandardTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<ProductionStandard>
data={
isResponseSuccess(productionStandards)
? productionStandards.data
: []
}
columns={productionStandardColumns}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(productionStandards) &&
productionStandards?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(productionStandards) ||
productionStandards.data?.length === 0 ? (
<div className='p-3'>
<ProductionStandardTableSkeleton
columns={productionStandardColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<ProductionStandard>
data={productionStandards.data}
columns={productionStandardColumns}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { ProductionStandard } from '@/types/api/master-data/production-standard';
import { ColumnDef } from '@tanstack/react-table';
const ProductionStandardTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no production standard data displayed. Enter production standard data to get started.',
}: {
columns: ColumnDef<ProductionStandard>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default ProductionStandardTableSkeleton;
@@ -14,10 +14,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import SupplierTableSkeleton from '@/components/pages/master-data/supplier/skeleton/SupplierTableSkeleton';
import { Supplier } from '@/types/api/master-data/supplier';
import { SupplierApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -273,27 +273,42 @@ const SuppliersTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Supplier>
data={isResponseSuccess(suppliers) ? suppliers?.data : []}
columns={suppliersColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(suppliers) ? suppliers?.meta?.page : 0}
totalItems={
isResponseSuccess(suppliers) ? suppliers?.meta?.total_results : 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(suppliers) && suppliers?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(suppliers) || suppliers.data?.length === 0 ? (
<div className='p-3'>
<SupplierTableSkeleton
columns={suppliersColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Supplier>
data={suppliers?.data}
columns={suppliersColumns}
pageSize={tableFilterState.pageSize}
page={suppliers?.meta?.page ?? 0}
totalItems={suppliers?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Supplier } from '@/types/api/master-data/supplier';
import { ColumnDef } from '@tanstack/react-table';
const SupplierTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no supplier data displayed. Enter supplier data to get started.',
}: {
columns: ColumnDef<Supplier>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default SupplierTableSkeleton;
@@ -14,10 +14,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import UomTableSkeleton from '@/components/pages/master-data/uom/skeleton/UomTableSkeleton';
import { Uom } from '@/types/api/master-data/uom';
import { UomApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -241,24 +241,42 @@ const UomsTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Uom>
data={isResponseSuccess(uoms) ? uoms?.data : []}
columns={uomsColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(uoms) ? uoms?.meta?.page : 0}
totalItems={isResponseSuccess(uoms) ? uoms?.meta?.total_results : 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full': isResponseSuccess(uoms) && uoms?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(uoms) || uoms.data?.length === 0 ? (
<div className='p-3'>
<UomTableSkeleton
columns={uomsColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Uom>
data={uoms?.data}
columns={uomsColumns}
pageSize={tableFilterState.pageSize}
page={uoms?.meta?.page ?? 0}
totalItems={uoms?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Uom } from '@/types/api/master-data/uom';
import { ColumnDef } from '@tanstack/react-table';
const UomTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no uom data displayed. Enter uom data to get started.',
}: {
columns: ColumnDef<Uom>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default UomTableSkeleton;
@@ -14,10 +14,10 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import WarehouseTableSkeleton from '@/components/pages/master-data/warehouse/skeleton/WarehouseTableSkeleton';
import { Warehouse } from '@/types/api/master-data/warehouse';
import { WarehouseApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
@@ -276,30 +276,43 @@ const WarehousesTable = () => {
{/* Table Section */}
<div className='flex flex-col mb-4'>
<Table<Warehouse>
data={isResponseSuccess(warehouses) ? warehouses?.data : []}
columns={warehousesColumns}
pageSize={tableFilterState.pageSize}
page={isResponseSuccess(warehouses) ? warehouses?.meta?.page : 0}
totalItems={
isResponseSuccess(warehouses)
? warehouses?.meta?.total_results
: 0
}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={isLoading}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: cn('p-3 mb-0', {
'w-full':
isResponseSuccess(warehouses) &&
warehouses?.data?.length === 0,
}),
headerColumnClassName: 'text-nowrap',
}}
/>
{isLoading ? (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
) : !isResponseSuccess(warehouses) ||
warehouses.data?.length === 0 ? (
<div className='p-3'>
<WarehouseTableSkeleton
columns={warehousesColumns}
icon={
<Icon
icon='heroicons:document-text'
className='text-white'
width={20}
height={20}
/>
}
/>
</div>
) : (
<Table<Warehouse>
data={warehouses?.data}
columns={warehousesColumns}
pageSize={tableFilterState.pageSize}
page={warehouses?.meta?.page ?? 0}
totalItems={warehouses?.meta?.total_results ?? 0}
onPageChange={setPage}
onPageSizeChange={setPageSize}
isLoading={false}
sorting={sorting}
setSorting={setSorting}
className={{
containerClassName: 'p-3 mb-0',
headerColumnClassName: 'text-nowrap',
}}
/>
)}
</div>
</div>
@@ -0,0 +1,37 @@
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
import Table from '@/components/Table';
import { Warehouse } from '@/types/api/master-data/warehouse';
import { ColumnDef } from '@tanstack/react-table';
const WarehouseTableSkeleton = ({
columns,
icon,
title = 'No Data Available',
subtitle = 'There is no warehouse data displayed. Enter warehouse data to get started.',
}: {
columns: ColumnDef<Warehouse>[];
icon: React.ReactNode;
title?: string;
subtitle?: string;
}) => {
return (
<div className='relative size-full'>
<Table
data={[]}
columns={columns}
isLoading={true}
className={{
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
headerColumnClassName: 'whitespace-nowrap',
containerClassName: 'mb-0 overflow-hidden',
tableWrapperClassName: 'overflow-hidden',
}}
/>
<div className='absolute inset-0 flex items-center justify-center'>
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
</div>
</div>
);
};
export default WarehouseTableSkeleton;