mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 15:25:46 +00:00
Merge branch 'development' of https://gitlab.com/mbugroup/lti-web-client into fix/debt-supplier
This commit is contained in:
@@ -72,8 +72,10 @@ const RequireAuth = ({ children }: RequireAuthProps) => {
|
|||||||
await AuthApi.refresh();
|
await AuthApi.refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
refreshUserSession();
|
if (user) {
|
||||||
}, []);
|
refreshUserSession();
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(isLoadingUserResponse && !userResponse && !userErrorResponse) ||
|
(isLoadingUserResponse && !userResponse && !userErrorResponse) ||
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ const DrawerHeader = ({
|
|||||||
if (leftIconOnClick) {
|
if (leftIconOnClick) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
type='button'
|
||||||
onClick={leftIconOnClick}
|
onClick={leftIconOnClick}
|
||||||
className='hover:text-gray-400 bg-transparent border-none p-0'
|
className='hover:text-gray-400 bg-transparent border-none p-0'
|
||||||
>
|
>
|
||||||
@@ -72,12 +73,12 @@ const DrawerHeader = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex flex-row justify-between items-center px-4 pt-4',
|
'flex flex-row justify-between items-center px-4 pt-4 pb-4 border-b border-base-content/10',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* Left Side */}
|
{/* Left Side */}
|
||||||
<div className='flex flex-row h-full gap-2 items-center'>
|
<div className='flex flex-row h-full gap-3 items-center'>
|
||||||
{renderLeftIcon()}
|
{renderLeftIcon()}
|
||||||
|
|
||||||
{showDivider && subtitle && (
|
{showDivider && subtitle && (
|
||||||
@@ -85,7 +86,12 @@ const DrawerHeader = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{subtitle && (
|
{subtitle && (
|
||||||
<div className={cn('text-sm text-neutral', subtitleClassName)}>
|
<div
|
||||||
|
className={cn(
|
||||||
|
'text-sm font-medium text-base-content/50',
|
||||||
|
subtitleClassName
|
||||||
|
)}
|
||||||
|
>
|
||||||
{subtitle}
|
{subtitle}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import UniformityGaugeChart from '@/components/pages/production/uniformity/chart
|
|||||||
import UniformityBarChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityBarChartSkeleton';
|
import UniformityBarChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityBarChartSkeleton';
|
||||||
import UniformityGaugeChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityGaugeChartSkeleton';
|
import UniformityGaugeChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityGaugeChartSkeleton';
|
||||||
import { Uniformity, type ChartData } from '@/types/api/production/uniformity';
|
import { Uniformity, type ChartData } from '@/types/api/production/uniformity';
|
||||||
|
import { Icon } from '@iconify/react';
|
||||||
|
|
||||||
interface UniformityChartProps {
|
interface UniformityChartProps {
|
||||||
uniformityData?: Uniformity | null;
|
uniformityData?: Uniformity | null;
|
||||||
@@ -101,15 +102,26 @@ const UniformityChart = ({
|
|||||||
const shouldShowEmptyState = !isFiltered;
|
const shouldShowEmptyState = !isFiltered;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className='w-full grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-4 gap-4'>
|
<section className='w-full grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-[1fr_350px] gap-3'>
|
||||||
<Card
|
<Card
|
||||||
title='Performance Overview ⓘ'
|
|
||||||
variant='bordered'
|
variant='bordered'
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'xl:col-span-1 2xl:col-span-3 w-full',
|
wrapper:
|
||||||
body: 'h-96',
|
'2xl:col-span-1 w-full rounded-xl border border-base-content/10',
|
||||||
|
body: 'h-96 p-4',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<div className='flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-3'>
|
||||||
|
<div className='text-base font-semibold leading-7 flex gap-3 items-center'>
|
||||||
|
Performance Overview{' '}
|
||||||
|
<Icon
|
||||||
|
icon='heroicons:information-circle'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className='inline text-neutral-500'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className='w-full h-full flex items-center justify-center'>
|
<div className='w-full h-full flex items-center justify-center'>
|
||||||
{shouldShowEmptyState ||
|
{shouldShowEmptyState ||
|
||||||
!uniformityData ||
|
!uniformityData ||
|
||||||
@@ -120,26 +132,31 @@ const UniformityChart = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
{shouldShowEmptyState || !uniformityData || !gaugeChartData ? (
|
<Card
|
||||||
<Card
|
variant='bordered'
|
||||||
variant='bordered'
|
className={{
|
||||||
title='Weekly Performance ⓘ'
|
wrapper:
|
||||||
className={{
|
'2xl:col-span-1 w-full rounded-xl border border-base-content/10',
|
||||||
wrapper: 'xl:col-span-1 2xl:col-span-1 w-full',
|
body:
|
||||||
body: 'h-110',
|
shouldShowEmptyState || !uniformityData || !gaugeChartData
|
||||||
}}
|
? 'h-110 p-4'
|
||||||
>
|
: 'p-4',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-3'>
|
||||||
|
<div className='text-base font-semibold leading-7 flex gap-3 items-center'>
|
||||||
|
Weekly Performance{' '}
|
||||||
|
<Icon
|
||||||
|
icon='heroicons:information-circle'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className='inline text-neutral-500'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{shouldShowEmptyState || !uniformityData || !gaugeChartData ? (
|
||||||
<UniformityGaugeChartSkeleton />
|
<UniformityGaugeChartSkeleton />
|
||||||
</Card>
|
) : (
|
||||||
) : (
|
|
||||||
<Card
|
|
||||||
variant='bordered'
|
|
||||||
title='Weekly Performance ⓘ'
|
|
||||||
className={{
|
|
||||||
wrapper: 'xl:col-span-1 2xl:col-span-1 w-full',
|
|
||||||
body: 'p-4',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<UniformityGaugeChart
|
<UniformityGaugeChart
|
||||||
value={gaugeChartData.value}
|
value={gaugeChartData.value}
|
||||||
label={gaugeChartData.label}
|
label={gaugeChartData.label}
|
||||||
@@ -150,8 +167,8 @@ const UniformityChart = ({
|
|||||||
hasPrevWeek={gaugeChartData.hasPrevWeek}
|
hasPrevWeek={gaugeChartData.hasPrevWeek}
|
||||||
hasNextWeek={gaugeChartData.hasNextWeek}
|
hasNextWeek={gaugeChartData.hasNextWeek}
|
||||||
/>
|
/>
|
||||||
</Card>
|
)}
|
||||||
)}
|
</Card>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import Drawer from '@/components/Drawer';
|
import Drawer from '@/components/Drawer';
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import UniformityTable from '@/components/pages/production/uniformity/UniformityTable';
|
import Uniformity from '@/app/production/uniformity/page';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
import { useUiStore } from '@/stores/ui/ui.store';
|
||||||
|
|
||||||
export default function UniformityPageWrapper({
|
export default function UniformityPageWrapper({
|
||||||
@@ -40,8 +40,8 @@ export default function UniformityPageWrapper({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='w-full p-4'>
|
<div className='w-full'>
|
||||||
<UniformityTable />
|
<Uniformity />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ import { isResponseSuccess } from '@/lib/api-helper';
|
|||||||
import { type BaseApiResponse } from '@/types/api/api-general';
|
import { type BaseApiResponse } from '@/types/api/api-general';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import Badge from '@/components/Badge';
|
import Badge from '@/components/Badge';
|
||||||
|
import StatusBadge from '@/components/helper/StatusBadge';
|
||||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import Card from '@/components/Card';
|
|
||||||
import UniformityTableSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityTableSkeleton';
|
import UniformityTableSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityTableSkeleton';
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
import { useUniformityStore } from '@/stores/uniformity/uniformity.store';
|
import { useUniformityStore } from '@/stores/uniformity/uniformity.store';
|
||||||
@@ -45,12 +45,11 @@ import {
|
|||||||
getStatusColor,
|
getStatusColor,
|
||||||
getStatusIndicatorColor,
|
getStatusIndicatorColor,
|
||||||
getStatusText,
|
getStatusText,
|
||||||
|
getStatusBadgeColor,
|
||||||
} from '@/components/pages/production/uniformity/uniformity-utils';
|
} from '@/components/pages/production/uniformity/uniformity-utils';
|
||||||
import { generateUniformityPDF } from '@/components/pages/production/uniformity/export/UniformityExportPDF';
|
import { generateUniformityPDF } from '@/components/pages/production/uniformity/export/UniformityExportPDF';
|
||||||
import { generateUniformityExcel } from '@/components/pages/production/uniformity/export/UniformityExportExcel';
|
import { generateUniformityExcel } from '@/components/pages/production/uniformity/export/UniformityExportExcel';
|
||||||
import Dropdown from '@/components/Dropdown';
|
import Dropdown from '@/components/Dropdown';
|
||||||
import Menu from '@/components/menu/Menu';
|
|
||||||
import MenuItem from '@/components/menu/MenuItem';
|
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import {
|
import {
|
||||||
UniformityTableFilterSchema,
|
UniformityTableFilterSchema,
|
||||||
@@ -113,12 +112,10 @@ const UniformityConfirmationPreview = ({
|
|||||||
|
|
||||||
const columns: ColumnDef<DetailOptionType>[] = [
|
const columns: ColumnDef<DetailOptionType>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: 'label',
|
|
||||||
header: 'Label',
|
header: 'Label',
|
||||||
cell: (props) => props.row.original.label,
|
cell: (props) => props.row.original.label,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'value',
|
|
||||||
header: 'Value',
|
header: 'Value',
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const id = props.row.original.id;
|
const id = props.row.original.id;
|
||||||
@@ -819,7 +816,7 @@ const UniformityTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full flex flex-row justify-center'>
|
<div className='w-full flex flex-row xl:justify-center justify-end'>
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
name='allRow'
|
name='allRow'
|
||||||
checked={isAllSelected}
|
checked={isAllSelected}
|
||||||
@@ -832,7 +829,7 @@ const UniformityTable = () => {
|
|||||||
},
|
},
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className='w-full flex flex-row xl:justify-center justify-end'>
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
name='row'
|
name='row'
|
||||||
checked={row.getIsSelected()}
|
checked={row.getIsSelected()}
|
||||||
@@ -862,8 +859,11 @@ const UniformityTable = () => {
|
|||||||
{
|
{
|
||||||
accessorKey: 'week',
|
accessorKey: 'week',
|
||||||
header: 'Tanggal (Week)',
|
header: 'Tanggal (Week)',
|
||||||
cell: (props) =>
|
cell: (props) => (
|
||||||
`${formatDate(props.row.original.applied_at, 'DD MMM YYYY')} (${props.row.original.week})`,
|
<span className='text-nowrap'>
|
||||||
|
{`${formatDate(props.row.original.applied_at, 'DD MMM YYYY')} (${props.row.original.week})`}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'status',
|
accessorKey: 'status',
|
||||||
@@ -872,20 +872,11 @@ const UniformityTable = () => {
|
|||||||
const uniformity = props.row.original;
|
const uniformity = props.row.original;
|
||||||
const status =
|
const status =
|
||||||
uniformity.latest_approval?.action ?? uniformity.status;
|
uniformity.latest_approval?.action ?? uniformity.status;
|
||||||
return (
|
|
||||||
<div className='w-full'>
|
const badgeColor = getStatusBadgeColor(status);
|
||||||
<Badge
|
const statusText = getStatusText(status);
|
||||||
statusIndicator={true}
|
|
||||||
variant='soft'
|
return <StatusBadge color={badgeColor} text={statusText} />;
|
||||||
className={{
|
|
||||||
badge: `rounded-xl w-full justify-start border border-gray-200 text-black ${getStatusColor(status)}`,
|
|
||||||
status: getStatusIndicatorColor(status),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getStatusText(status)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -900,334 +891,410 @@ const UniformityTable = () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ===== CALCULATE FILTER COUNT =====
|
||||||
|
const filterCount = useMemo(() => {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
if (filterStartDate && filterEndDate) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterLocation) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterProjectFlock) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterKandang) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}, [
|
||||||
|
filterStartDate,
|
||||||
|
filterEndDate,
|
||||||
|
filterLocation,
|
||||||
|
filterProjectFlock,
|
||||||
|
filterKandang,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const isFilterActive = filterCount > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className='[&_button]:w-full [&_button]:sm:w-fit [&_button]:last:mt-2 [&_button]:last:sm:mt-0 sm:flex sm:justify-between grid grid-cols-1 sm:gap-0 gap-2'>
|
<div className='@container w-full'>
|
||||||
<div className='w-full sm:w-fit flex flex-col sm:flex-row self-start gap-2'>
|
<div className='w-full p-3 flex flex-row justify-between gap-3 flex-wrap border-b border-base-content/10'>
|
||||||
<RequirePermission permissions='lti.production.uniformity.create'>
|
<div className='w-fit flex flex-row gap-3 flex-wrap'>
|
||||||
<Button color='primary' href='/production/uniformity/add'>
|
<RequirePermission permissions='lti.production.uniformity.create'>
|
||||||
<Icon icon='ic:round-plus' width={18} height={18} />
|
<Button
|
||||||
Add Uniformity
|
href='/production/uniformity/add'
|
||||||
</Button>
|
color='primary'
|
||||||
</RequirePermission>
|
className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm'
|
||||||
</div>
|
>
|
||||||
|
<Icon icon='heroicons:plus' width={20} height={20} />
|
||||||
<div className='sm:flex gap-2'>
|
Add Uniformity
|
||||||
<Button variant='outline' onClick={filterModal.openModal}>
|
|
||||||
<Icon icon='heroicons:funnel' width={18} height={18} />
|
|
||||||
Filter
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Dropdown
|
|
||||||
trigger={
|
|
||||||
<Button variant='outline' isLoading={isAnyExportLoading}>
|
|
||||||
<Icon
|
|
||||||
icon='heroicons:cloud-arrow-down'
|
|
||||||
width={18}
|
|
||||||
height={18}
|
|
||||||
/>
|
|
||||||
Export
|
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</RequirePermission>
|
||||||
align='end'
|
</div>
|
||||||
>
|
|
||||||
<Menu>
|
<div className='flex flex-1 flex-row justify-start sm:justify-end items-center gap-3 flex-wrap'>
|
||||||
<MenuItem title='Excel' onClick={handleExportExcel} />
|
<Button
|
||||||
<MenuItem title='PDF' onClick={handleExportPDF} />
|
variant='outline'
|
||||||
</Menu>
|
color='none'
|
||||||
</Dropdown>
|
onClick={filterModal.openModal}
|
||||||
|
className={cn(
|
||||||
|
'px-3 py-2.5 gap-1.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft transition-all',
|
||||||
|
{
|
||||||
|
'border-primary-gradient text-primary': isFilterActive,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||||
|
Filter
|
||||||
|
{isFilterActive && (
|
||||||
|
<Badge
|
||||||
|
className={{
|
||||||
|
badge:
|
||||||
|
'p-1.5 bg-[#FF3535] text-xs text-base-100 border border-base-300 rounded-lg',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{filterCount}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Dropdown
|
||||||
|
align='end'
|
||||||
|
direction='bottom'
|
||||||
|
className={{
|
||||||
|
content:
|
||||||
|
'mt-1 rounded-xl border border-base-content/5 shadow-sm overflow-hidden',
|
||||||
|
}}
|
||||||
|
trigger={
|
||||||
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
color='none'
|
||||||
|
className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
||||||
|
>
|
||||||
|
<div className='flex flex-row items-center gap-1.5'>
|
||||||
|
<Icon
|
||||||
|
icon='heroicons:cloud-arrow-down'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span>Export</span>
|
||||||
|
|
||||||
|
<div className='w-px self-stretch bg-base-content/10' />
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
icon='heroicons:chevron-down'
|
||||||
|
width={14}
|
||||||
|
height={14}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant='ghost'
|
||||||
|
color='none'
|
||||||
|
onClick={handleExportExcel}
|
||||||
|
isLoading={isExcelExportLoading}
|
||||||
|
className='w-full p-3 justify-start text-sm text-base-content/50 font-semibold text-nowrap'
|
||||||
|
>
|
||||||
|
<Icon icon='heroicons:table-cells' width={20} height={20} />
|
||||||
|
Export to Excel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant='ghost'
|
||||||
|
color='none'
|
||||||
|
onClick={handleExportPDF}
|
||||||
|
isLoading={isPdfExportLoading}
|
||||||
|
className='w-full p-3 justify-start text-sm text-base-content/50 font-semibold text-nowrap hover:bg-base-content/5'
|
||||||
|
>
|
||||||
|
<Icon icon='heroicons:document-text' width={20} height={20} />
|
||||||
|
Export to PDF
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<div className='my-4 divider'></div>
|
<section className='p-3'>
|
||||||
|
<UniformityChartWrapper
|
||||||
|
uniformitySwrKey={uniformitySwrKey}
|
||||||
|
isFiltered={isSubmitted}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
<section>
|
<Table<Uniformity>
|
||||||
<UniformityChartWrapper
|
data={isResponseSuccess(uniformities) ? uniformities?.data : []}
|
||||||
uniformitySwrKey={uniformitySwrKey}
|
columns={uniformityColumns}
|
||||||
isFiltered={isSubmitted}
|
pageSize={tableFilterState.pageSize}
|
||||||
/>
|
page={isResponseSuccess(uniformities) ? uniformities?.meta?.page : 0}
|
||||||
</section>
|
totalItems={
|
||||||
|
isResponseSuccess(uniformities)
|
||||||
<Card
|
? uniformities?.meta?.total_results
|
||||||
variant='bordered'
|
: 0
|
||||||
|
}
|
||||||
|
onPageChange={setPage}
|
||||||
|
isLoading={isLoading}
|
||||||
|
sorting={sorting}
|
||||||
|
setSorting={setSorting}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
setRowSelection={setRowSelection}
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'my-4 w-full relative',
|
containerClassName: cn('p-3 pt-0', {
|
||||||
|
'mb-20':
|
||||||
|
isResponseSuccess(uniformities) &&
|
||||||
|
uniformities?.data?.length === 0,
|
||||||
|
}),
|
||||||
|
headerColumnClassName:
|
||||||
|
'first:pl-3 first:pr-0 xl:first:pl-3 py-3 text-nowrap',
|
||||||
|
bodyColumnClassName:
|
||||||
|
'first:pl-3 first:pr-0 xl:first:pl-3 py-3 text-nowrap',
|
||||||
|
}}
|
||||||
|
emptyContent={<UniformityTableSkeleton />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConfirmationModal
|
||||||
|
ref={successModal.ref}
|
||||||
|
type='success'
|
||||||
|
iconPosition='left'
|
||||||
|
text='Data Berhasil Ditambahkan'
|
||||||
|
subtitleText='Data uniformity telah berhasil disimpan.'
|
||||||
|
closeOnBackdrop={false}
|
||||||
|
primaryButton={{
|
||||||
|
text: 'Ok',
|
||||||
|
color: 'primary',
|
||||||
|
onClick: handleSuccessModalClose,
|
||||||
|
}}
|
||||||
|
className={{
|
||||||
|
modalBox: 'rounded-2xl',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Table<Uniformity>
|
<div className='flex flex-col gap-4 mt-4'>
|
||||||
data={isResponseSuccess(uniformities) ? uniformities?.data : []}
|
{createdUniformity ? (
|
||||||
columns={uniformityColumns}
|
<UniformityConfirmationPreview
|
||||||
pageSize={tableFilterState.pageSize}
|
uniformityDetail={createdUniformity}
|
||||||
page={isResponseSuccess(uniformities) ? uniformities?.meta?.page : 0}
|
/>
|
||||||
totalItems={
|
) : selectedRowIds.length === 1 ? (
|
||||||
isResponseSuccess(uniformities)
|
<UniformityConfirmationPreview
|
||||||
? uniformities?.meta?.total_results
|
uniformity={selectedUniformities[0]}
|
||||||
: 0
|
/>
|
||||||
}
|
) : (
|
||||||
onPageChange={setPage}
|
<div className='text-center text-gray-500'>
|
||||||
isLoading={isLoading}
|
{selectedRowIds.length} data dipilih
|
||||||
sorting={sorting}
|
|
||||||
setSorting={setSorting}
|
|
||||||
rowSelection={rowSelection}
|
|
||||||
setRowSelection={setRowSelection}
|
|
||||||
className={{
|
|
||||||
containerClassName: cn({
|
|
||||||
'mb-20':
|
|
||||||
isResponseSuccess(uniformities) &&
|
|
||||||
uniformities?.data?.length === 0,
|
|
||||||
}),
|
|
||||||
tableWrapperClassName: 'overflow-x-auto min-h-full ',
|
|
||||||
tableClassName: 'font-inter w-full table-auto min-h-full!',
|
|
||||||
headerRowClassName: 'border-b border-b-gray-200',
|
|
||||||
headerColumnClassName:
|
|
||||||
'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
|
|
||||||
bodyRowClassName: 'border-b border-b-gray-200',
|
|
||||||
bodyColumnClassName:
|
|
||||||
'px-6 py-3 last:flex last:flex-row last:justify-end',
|
|
||||||
}}
|
|
||||||
emptyContent={<UniformityTableSkeleton />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ConfirmationModal
|
|
||||||
ref={successModal.ref}
|
|
||||||
type='success'
|
|
||||||
iconPosition='left'
|
|
||||||
text='Data Berhasil Ditambahkan'
|
|
||||||
subtitleText='Data uniformity telah berhasil disimpan.'
|
|
||||||
closeOnBackdrop={false}
|
|
||||||
primaryButton={{
|
|
||||||
text: 'Ok',
|
|
||||||
color: 'primary',
|
|
||||||
onClick: handleSuccessModalClose,
|
|
||||||
}}
|
|
||||||
className={{
|
|
||||||
modalBox: 'rounded-2xl',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='flex flex-col gap-4 mt-4'>
|
|
||||||
{createdUniformity ? (
|
|
||||||
<UniformityConfirmationPreview
|
|
||||||
uniformityDetail={createdUniformity}
|
|
||||||
/>
|
|
||||||
) : selectedRowIds.length === 1 ? (
|
|
||||||
<UniformityConfirmationPreview
|
|
||||||
uniformity={selectedUniformities[0]}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className='text-center text-gray-500'>
|
|
||||||
{selectedRowIds.length} data dipilih
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ConfirmationModal>
|
|
||||||
|
|
||||||
<ConfirmationModal
|
|
||||||
ref={singleDeleteModal.ref}
|
|
||||||
type='error'
|
|
||||||
iconPosition='left'
|
|
||||||
text={`Delete This Data?`}
|
|
||||||
subtitleText='Are you sure you want to delete this data?'
|
|
||||||
secondaryButton={{
|
|
||||||
text: 'Cancel',
|
|
||||||
}}
|
|
||||||
primaryButton={{
|
|
||||||
text: 'Delete',
|
|
||||||
color: 'primary',
|
|
||||||
isLoading: isDeleteLoading,
|
|
||||||
onClick: singleDeleteHandler,
|
|
||||||
}}
|
|
||||||
className={{
|
|
||||||
modalBox: 'rounded-2xl',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='flex flex-col gap-4 mt-4'>
|
|
||||||
<UniformityConfirmationPreview uniformity={selectedUniformity} />
|
|
||||||
</div>
|
|
||||||
</ConfirmationModal>
|
|
||||||
|
|
||||||
<ConfirmationModal
|
|
||||||
ref={singleApproveModal.ref}
|
|
||||||
type='success'
|
|
||||||
iconPosition='left'
|
|
||||||
text='Approve This Submission?'
|
|
||||||
subtitleText='Are you sure you want to approve this submission?'
|
|
||||||
secondaryButton={{
|
|
||||||
text: 'Cancel',
|
|
||||||
}}
|
|
||||||
primaryButton={{
|
|
||||||
text: 'Approve',
|
|
||||||
color: 'primary',
|
|
||||||
isLoading: isDeleteLoading,
|
|
||||||
onClick: singleApproveHandler,
|
|
||||||
}}
|
|
||||||
className={{
|
|
||||||
modalBox: 'rounded-2xl',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='flex flex-col gap-4 mt-4'>
|
|
||||||
{selectedRowIds.length === 1 ? (
|
|
||||||
<UniformityConfirmationPreview
|
|
||||||
uniformity={selectedUniformities[0]}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className='text-center text-gray-500'>
|
|
||||||
{selectedRowIds.length} data dipilih
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ConfirmationModal>
|
|
||||||
|
|
||||||
<ConfirmationModal
|
|
||||||
ref={bulkApproveModal.ref}
|
|
||||||
type='success'
|
|
||||||
iconPosition='left'
|
|
||||||
text={`Approve This Submission?`}
|
|
||||||
subtitleText={`Are you sure you want to approve this submission? (${selectedRowIds.length} data)`}
|
|
||||||
secondaryButton={{
|
|
||||||
text: 'Cancel',
|
|
||||||
}}
|
|
||||||
primaryButton={{
|
|
||||||
text: 'Approve',
|
|
||||||
color: 'primary',
|
|
||||||
isLoading: isBulkActionLoading,
|
|
||||||
onClick: bulkApproveHandler,
|
|
||||||
}}
|
|
||||||
className={{
|
|
||||||
modalBox: 'rounded-2xl',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='flex flex-col gap-4 mt-4'>
|
|
||||||
<UniformityConfirmationPreview uniformity={selectedUniformity} />
|
|
||||||
</div>
|
|
||||||
</ConfirmationModal>
|
|
||||||
|
|
||||||
<ConfirmationModal
|
|
||||||
ref={singleRejectModal.ref}
|
|
||||||
type='error'
|
|
||||||
iconPosition='left'
|
|
||||||
text='Reject This Submission?'
|
|
||||||
subtitleText='Are you sure you want to reject this submission?'
|
|
||||||
secondaryButton={{
|
|
||||||
text: 'Cancel',
|
|
||||||
}}
|
|
||||||
primaryButton={{
|
|
||||||
text: 'Reject',
|
|
||||||
color: 'primary',
|
|
||||||
isLoading: isDeleteLoading,
|
|
||||||
onClick: singleRejectHandler,
|
|
||||||
}}
|
|
||||||
className={{
|
|
||||||
modalBox: 'rounded-2xl',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='flex flex-col gap-4 mt-4'>
|
|
||||||
{selectedRowIds.length === 1 ? (
|
|
||||||
<UniformityConfirmationPreview
|
|
||||||
uniformity={selectedUniformities[0]}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className='text-center text-gray-500'>
|
|
||||||
{selectedRowIds.length} data dipilih
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ConfirmationModal>
|
|
||||||
|
|
||||||
<ConfirmationModal
|
|
||||||
ref={bulkRejectModal.ref}
|
|
||||||
type='error'
|
|
||||||
iconPosition='left'
|
|
||||||
text={`Apakah anda yakin ingin menolak ${selectedRowIds.length} data Uniformity yang dipilih?`}
|
|
||||||
secondaryButton={{
|
|
||||||
text: 'Cancel',
|
|
||||||
}}
|
|
||||||
primaryButton={{
|
|
||||||
text: 'Reject',
|
|
||||||
color: 'primary',
|
|
||||||
isLoading: isBulkActionLoading,
|
|
||||||
onClick: bulkRejectHandler,
|
|
||||||
}}
|
|
||||||
className={{
|
|
||||||
modalBox: 'rounded-2xl',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='flex flex-col gap-4 mt-4'>
|
|
||||||
{selectedRowIds.length === 1 ? (
|
|
||||||
<UniformityConfirmationPreview
|
|
||||||
uniformity={selectedUniformities[0]}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className='text-center text-gray-500'>
|
|
||||||
{selectedRowIds.length} data dipilih
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ConfirmationModal>
|
|
||||||
|
|
||||||
{/* Filter Modal */}
|
|
||||||
<Modal
|
|
||||||
ref={filterModal.ref}
|
|
||||||
className={{
|
|
||||||
modal: 'p-0',
|
|
||||||
modalBox: 'p-0 rounded-2xl xl:max-w-4/12 max-w-sm',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='space-y-6'>
|
|
||||||
{/* Modal Header */}
|
|
||||||
<div className='flex items-center justify-between gap-2 py-3 border-b border-gray-300 px-4'>
|
|
||||||
<div className='flex items-center gap-2 text-primary'>
|
|
||||||
<Icon icon='heroicons:funnel' width={20} height={20} />
|
|
||||||
<h3 className='font-semibold'>Filter Data</h3>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
variant='link'
|
|
||||||
onClick={filterModal.closeModal}
|
|
||||||
className='text-gray-500 hover:text-gray-700 transition-colors cursor-pointer'
|
|
||||||
>
|
|
||||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ConfirmationModal>
|
||||||
|
|
||||||
{/* Error List Alert */}
|
<ConfirmationModal
|
||||||
{formErrorList.length > 0 && (
|
ref={singleDeleteModal.ref}
|
||||||
<div className='w-full px-4'>
|
type='error'
|
||||||
<AlertErrorList formErrorList={formErrorList} onClose={close} />
|
iconPosition='left'
|
||||||
</div>
|
text={`Delete This Data?`}
|
||||||
)}
|
subtitleText='Are you sure you want to delete this data?'
|
||||||
|
secondaryButton={{
|
||||||
|
text: 'Cancel',
|
||||||
|
}}
|
||||||
|
primaryButton={{
|
||||||
|
text: 'Delete',
|
||||||
|
color: 'primary',
|
||||||
|
isLoading: isDeleteLoading,
|
||||||
|
onClick: singleDeleteHandler,
|
||||||
|
}}
|
||||||
|
className={{
|
||||||
|
modalBox: 'rounded-2xl',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col gap-4 mt-4'>
|
||||||
|
<UniformityConfirmationPreview uniformity={selectedUniformity} />
|
||||||
|
</div>
|
||||||
|
</ConfirmationModal>
|
||||||
|
|
||||||
<div className='space-y-4 px-4'>
|
<ConfirmationModal
|
||||||
<div className='grid grid-cols-1 sm:grid-cols-2 sm:gap-4'>
|
ref={singleApproveModal.ref}
|
||||||
<div>
|
type='success'
|
||||||
|
iconPosition='left'
|
||||||
|
text='Approve This Submission?'
|
||||||
|
subtitleText='Are you sure you want to approve this submission?'
|
||||||
|
secondaryButton={{
|
||||||
|
text: 'Cancel',
|
||||||
|
}}
|
||||||
|
primaryButton={{
|
||||||
|
text: 'Approve',
|
||||||
|
color: 'primary',
|
||||||
|
isLoading: isDeleteLoading,
|
||||||
|
onClick: singleApproveHandler,
|
||||||
|
}}
|
||||||
|
className={{
|
||||||
|
modalBox: 'rounded-2xl',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col gap-4 mt-4'>
|
||||||
|
{selectedRowIds.length === 1 ? (
|
||||||
|
<UniformityConfirmationPreview
|
||||||
|
uniformity={selectedUniformities[0]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className='text-center text-gray-500'>
|
||||||
|
{selectedRowIds.length} data dipilih
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ConfirmationModal>
|
||||||
|
|
||||||
|
<ConfirmationModal
|
||||||
|
ref={bulkApproveModal.ref}
|
||||||
|
type='success'
|
||||||
|
iconPosition='left'
|
||||||
|
text={`Approve This Submission?`}
|
||||||
|
subtitleText={`Are you sure you want to approve this submission? (${selectedRowIds.length} data)`}
|
||||||
|
secondaryButton={{
|
||||||
|
text: 'Cancel',
|
||||||
|
}}
|
||||||
|
primaryButton={{
|
||||||
|
text: 'Approve',
|
||||||
|
color: 'primary',
|
||||||
|
isLoading: isBulkActionLoading,
|
||||||
|
onClick: bulkApproveHandler,
|
||||||
|
}}
|
||||||
|
className={{
|
||||||
|
modalBox: 'rounded-2xl',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col gap-4 mt-4'>
|
||||||
|
<UniformityConfirmationPreview uniformity={selectedUniformity} />
|
||||||
|
</div>
|
||||||
|
</ConfirmationModal>
|
||||||
|
|
||||||
|
<ConfirmationModal
|
||||||
|
ref={singleRejectModal.ref}
|
||||||
|
type='error'
|
||||||
|
iconPosition='left'
|
||||||
|
text='Reject This Submission?'
|
||||||
|
subtitleText='Are you sure you want to reject this submission?'
|
||||||
|
secondaryButton={{
|
||||||
|
text: 'Cancel',
|
||||||
|
}}
|
||||||
|
primaryButton={{
|
||||||
|
text: 'Reject',
|
||||||
|
color: 'primary',
|
||||||
|
isLoading: isDeleteLoading,
|
||||||
|
onClick: singleRejectHandler,
|
||||||
|
}}
|
||||||
|
className={{
|
||||||
|
modalBox: 'rounded-2xl',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col gap-4 mt-4'>
|
||||||
|
{selectedRowIds.length === 1 ? (
|
||||||
|
<UniformityConfirmationPreview
|
||||||
|
uniformity={selectedUniformities[0]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className='text-center text-gray-500'>
|
||||||
|
{selectedRowIds.length} data dipilih
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ConfirmationModal>
|
||||||
|
|
||||||
|
<ConfirmationModal
|
||||||
|
ref={bulkRejectModal.ref}
|
||||||
|
type='error'
|
||||||
|
iconPosition='left'
|
||||||
|
text={`Reject This Submission?`}
|
||||||
|
subtitleText={`Are you sure you want to reject this submission? (${selectedRowIds.length} data)`}
|
||||||
|
secondaryButton={{
|
||||||
|
text: 'Cancel',
|
||||||
|
}}
|
||||||
|
primaryButton={{
|
||||||
|
text: 'Reject',
|
||||||
|
color: 'primary',
|
||||||
|
isLoading: isBulkActionLoading,
|
||||||
|
onClick: bulkRejectHandler,
|
||||||
|
}}
|
||||||
|
className={{
|
||||||
|
modalBox: 'rounded-2xl',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col gap-4 mt-4'>
|
||||||
|
{selectedRowIds.length === 1 ? (
|
||||||
|
<UniformityConfirmationPreview
|
||||||
|
uniformity={selectedUniformities[0]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className='text-center text-gray-500'>
|
||||||
|
{selectedRowIds.length} data dipilih
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ConfirmationModal>
|
||||||
|
|
||||||
|
{/* Filter Modal */}
|
||||||
|
<Modal
|
||||||
|
ref={filterModal.ref}
|
||||||
|
className={{
|
||||||
|
modal: 'p-0',
|
||||||
|
modalBox: 'p-0 rounded-[0.875rem]',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col'>
|
||||||
|
{/* Modal Header */}
|
||||||
|
<div className='flex items-center justify-between p-4 border-b border-base-content/10'>
|
||||||
|
<div className='flex items-center gap-2 text-primary'>
|
||||||
|
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||||
|
<h3 className='font-medium text-sm'>Filter Data</h3>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant='link'
|
||||||
|
onClick={filterModal.closeModal}
|
||||||
|
className='text-gray-500 hover:text-gray-700'
|
||||||
|
>
|
||||||
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form
|
||||||
|
className='flex flex-col'
|
||||||
|
onSubmit={handleFormSubmit}
|
||||||
|
onReset={handleResetFilters}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col p-4 gap-1.5'>
|
||||||
|
{/* Rentang Waktu */}
|
||||||
|
<div>
|
||||||
|
<label className='flex text-xs items-center gap-2 py-2 font-semibold'>
|
||||||
|
Tanggal
|
||||||
|
</label>
|
||||||
|
<div className='flex items-center gap-2'>
|
||||||
<DateInput
|
<DateInput
|
||||||
required
|
|
||||||
label='Tanggal mulai'
|
|
||||||
name='start_date'
|
name='start_date'
|
||||||
|
placeholder='Tanggal Mulai'
|
||||||
value={filterFormik.values.start_date}
|
value={filterFormik.values.start_date}
|
||||||
|
errorMessage={filterFormik.errors.start_date}
|
||||||
onChange={handleFilterStartDateChange}
|
onChange={handleFilterStartDateChange}
|
||||||
onBlur={filterFormik.handleBlur}
|
|
||||||
isError={
|
isError={
|
||||||
filterFormik.touched.start_date &&
|
filterFormik.touched.start_date &&
|
||||||
Boolean(filterFormik.errors.start_date)
|
Boolean(filterFormik.errors.start_date)
|
||||||
}
|
}
|
||||||
errorMessage={filterFormik.errors.start_date}
|
|
||||||
className={{ wrapper: 'w-full' }}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
<hr className='w-full max-w-3 h-px border-base-content/10'></hr>
|
||||||
|
|
||||||
<div>
|
|
||||||
<DateInput
|
<DateInput
|
||||||
required
|
|
||||||
label='Tanggal akhir'
|
|
||||||
name='end_date'
|
name='end_date'
|
||||||
|
placeholder='Tanggal Akhir'
|
||||||
value={filterFormik.values.end_date}
|
value={filterFormik.values.end_date}
|
||||||
|
errorMessage={filterFormik.errors.end_date}
|
||||||
onChange={handleFilterEndDateChange}
|
onChange={handleFilterEndDateChange}
|
||||||
onBlur={filterFormik.handleBlur}
|
|
||||||
isError={
|
isError={
|
||||||
filterFormik.touched.end_date &&
|
filterFormik.touched.end_date &&
|
||||||
Boolean(filterFormik.errors.end_date)
|
Boolean(filterFormik.errors.end_date)
|
||||||
}
|
}
|
||||||
errorMessage={filterFormik.errors.end_date}
|
|
||||||
className={{ wrapper: 'w-full' }}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1299,74 +1366,83 @@ const UniformityTable = () => {
|
|||||||
className={{ wrapper: 'w-full' }}
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{formErrorList.length > 0 && (
|
||||||
|
<div className='w-full'>
|
||||||
|
<AlertErrorList
|
||||||
|
formErrorList={formErrorList}
|
||||||
|
onClose={close}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className='flex justify-between gap-4 py-4 mt-8 border-t border-gray-300 bg-gray-100'>
|
<div className='flex justify-between gap-4 p-4 border-t border-base-content/10 bg-gray-100'>
|
||||||
<Button
|
<Button
|
||||||
|
type='reset'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className='ms-4 min-w-36 rounded-lg'
|
className='rounded-lg p-3 bg-gray-100 border-gray-100 text-base-content/65 hover:bg-base-content/10'
|
||||||
onClick={handleResetFilters}
|
|
||||||
>
|
>
|
||||||
Reset Filter
|
Reset Filter
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className='me-4 min-w-36 rounded-lg'
|
type='submit'
|
||||||
onClick={handleApplyFilters}
|
className='min-w-40 text-sm p-3 text-white rounded-lg'
|
||||||
>
|
>
|
||||||
Apply Filter
|
Apply Filter
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</Modal>
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
{/* Floating Actions Button */}
|
{/* Floating Actions Button */}
|
||||||
<FloatingActionsButton
|
<FloatingActionsButton
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
action: 'DETAIL',
|
action: 'DETAIL',
|
||||||
icon: 'mdi:eye-outline',
|
icon: 'mdi:eye-outline',
|
||||||
label: 'Lihat Detail',
|
label: 'Lihat Detail',
|
||||||
hidden: selectedRowIds.length !== 1,
|
hidden: selectedRowIds.length !== 1,
|
||||||
onClick() {
|
onClick() {
|
||||||
router.push(
|
router.push(
|
||||||
`/production/uniformity/detail?uniformityId=${selectedRowIds[0]}`
|
`/production/uniformity/detail?uniformityId=${selectedRowIds[0]}`
|
||||||
);
|
);
|
||||||
setRowSelection({});
|
setRowSelection({});
|
||||||
},
|
|
||||||
permissions: 'lti.production.uniformity.detail',
|
|
||||||
},
|
},
|
||||||
{
|
permissions: 'lti.production.uniformity.detail',
|
||||||
action: 'DELETE',
|
},
|
||||||
icon: 'mdi:delete-outline',
|
{
|
||||||
label: 'Delete',
|
action: 'DELETE',
|
||||||
hidden: selectedRowIds.length !== 1,
|
icon: 'mdi:delete-outline',
|
||||||
onClick: handleDelete,
|
label: 'Delete',
|
||||||
permissions: 'lti.production.uniformity.delete',
|
hidden: selectedRowIds.length !== 1,
|
||||||
},
|
onClick: handleDelete,
|
||||||
]}
|
permissions: 'lti.production.uniformity.delete',
|
||||||
approvals={[
|
},
|
||||||
{
|
]}
|
||||||
action: 'APPROVED',
|
approvals={[
|
||||||
icon: 'mdi:check-circle-outline',
|
{
|
||||||
label: 'Approve',
|
action: 'APPROVED',
|
||||||
onClick: handleBulkApprove,
|
icon: 'mdi:check-circle-outline',
|
||||||
permissions: 'lti.production.uniformity.approve',
|
label: 'Approve',
|
||||||
disabled: !canApproveReject,
|
onClick: handleBulkApprove,
|
||||||
},
|
permissions: 'lti.production.uniformity.approve',
|
||||||
{
|
disabled: !canApproveReject,
|
||||||
action: 'REJECTED',
|
},
|
||||||
icon: 'mdi:close-circle-outline',
|
{
|
||||||
label: 'Reject',
|
action: 'REJECTED',
|
||||||
onClick: handleBulkReject,
|
icon: 'mdi:close-circle-outline',
|
||||||
permissions: 'lti.production.uniformity.approve',
|
label: 'Reject',
|
||||||
disabled: !canApproveReject,
|
onClick: handleBulkReject,
|
||||||
},
|
permissions: 'lti.production.uniformity.approve',
|
||||||
]}
|
disabled: !canApproveReject,
|
||||||
selectedRowIds={selectedRowIds}
|
},
|
||||||
onClose={handleCloseFab}
|
]}
|
||||||
/>
|
selectedRowIds={selectedRowIds}
|
||||||
</Card>
|
onClose={handleCloseFab}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -319,18 +319,19 @@ const UniformityDetail: React.FC<UniformityDetailProps> = ({
|
|||||||
<DrawerHeader
|
<DrawerHeader
|
||||||
leftIconHref='/production/uniformity'
|
leftIconHref='/production/uniformity'
|
||||||
subtitle={`Details`}
|
subtitle={`Details`}
|
||||||
subtitleClassName='text-sm text-neutral'
|
subtitleClassName='text-sm font-medium text-base-content/50'
|
||||||
showDivider
|
showDivider
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Form Section */}
|
{/* Form Section */}
|
||||||
<div className='divider mt-3.5'></div>
|
<section className='w-full p-4'>
|
||||||
<section className='w-full px-6 mb-6'>
|
|
||||||
{initialValues ? (
|
{initialValues ? (
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
{/* Info Umum */}
|
{/* Info Umum */}
|
||||||
<div className=''>
|
<div className=''>
|
||||||
<p className='text-sm font-medium mb-5'>Informasi Umum</p>
|
<h2 className='text-base font-medium text-base-content/50 font-roboto mb-5'>
|
||||||
|
Informasi Umum
|
||||||
|
</h2>
|
||||||
<Table<DetailOptionType>
|
<Table<DetailOptionType>
|
||||||
data={infoUmumTableData}
|
data={infoUmumTableData}
|
||||||
columns={columnsInfoUmum}
|
columns={columnsInfoUmum}
|
||||||
@@ -345,7 +346,9 @@ const UniformityDetail: React.FC<UniformityDetailProps> = ({
|
|||||||
{/* Sampling and Range */}
|
{/* Sampling and Range */}
|
||||||
{initialValues.sampling && (
|
{initialValues.sampling && (
|
||||||
<div className=''>
|
<div className=''>
|
||||||
<p className='text-sm font-medium mb-5'>Sampling and Range</p>
|
<h2 className='text-base font-medium text-base-content/50 font-roboto mb-5'>
|
||||||
|
Sampling and Range
|
||||||
|
</h2>
|
||||||
<Table<DetailOptionType>
|
<Table<DetailOptionType>
|
||||||
data={samplingTableData}
|
data={samplingTableData}
|
||||||
columns={columnsSampling}
|
columns={columnsSampling}
|
||||||
@@ -361,7 +364,9 @@ const UniformityDetail: React.FC<UniformityDetailProps> = ({
|
|||||||
{/* Result */}
|
{/* Result */}
|
||||||
{initialValues.result && (
|
{initialValues.result && (
|
||||||
<div className=''>
|
<div className=''>
|
||||||
<p className='text-sm font-medium mb-5'>Result</p>
|
<h2 className='text-base font-medium text-base-content/50 font-roboto mb-5'>
|
||||||
|
Result
|
||||||
|
</h2>
|
||||||
<Table<DetailOptionType>
|
<Table<DetailOptionType>
|
||||||
data={resultTableData}
|
data={resultTableData}
|
||||||
columns={resultColumns}
|
columns={resultColumns}
|
||||||
|
|||||||
@@ -10,11 +10,10 @@ import {
|
|||||||
UniformityInfoUmum,
|
UniformityInfoUmum,
|
||||||
} from '@/types/api/production/uniformity';
|
} from '@/types/api/production/uniformity';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import Badge from '@/components/Badge';
|
import StatusBadge from '@/components/helper/StatusBadge';
|
||||||
import {
|
import {
|
||||||
getWeightStatusColor,
|
|
||||||
getWeightStatusIndicatorColor,
|
|
||||||
getWeightStatusText,
|
getWeightStatusText,
|
||||||
|
getWeightStatusBadgeColor,
|
||||||
} from '@/components/pages/production/uniformity/uniformity-utils';
|
} from '@/components/pages/production/uniformity/uniformity-utils';
|
||||||
import { BodyWeightData } from '@/types/api/production/uniformity';
|
import { BodyWeightData } from '@/types/api/production/uniformity';
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ const UniformityDetailsPreview = ({
|
|||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
accessorKey: 'number',
|
accessorKey: 'number',
|
||||||
header: 'No',
|
header: 'Number',
|
||||||
cell: (props) => props.row.original.number,
|
cell: (props) => props.row.original.number,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -65,30 +64,12 @@ const UniformityDetailsPreview = ({
|
|||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const status = props.row.original.status;
|
const status = props.row.original.status;
|
||||||
return status ? (
|
return status ? (
|
||||||
<div className='w-full'>
|
<StatusBadge
|
||||||
<Badge
|
color={getWeightStatusBadgeColor(status)}
|
||||||
statusIndicator={true}
|
text={getWeightStatusText(status)}
|
||||||
variant='soft'
|
/>
|
||||||
className={{
|
|
||||||
badge: `rounded-xl w-full justify-start border border-gray-200 text-black ${getWeightStatusColor(status)}`,
|
|
||||||
status: getWeightStatusIndicatorColor(status),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getWeightStatusText(status)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<Badge
|
<StatusBadge color='info' text='Unknown' />
|
||||||
statusIndicator={true}
|
|
||||||
variant='soft'
|
|
||||||
className={{
|
|
||||||
badge:
|
|
||||||
'rounded-xl w-full justify-start border border-gray-200 text-black bg-info/10',
|
|
||||||
status: 'bg-info',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Unknown
|
|
||||||
</Badge>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -102,7 +83,7 @@ const UniformityDetailsPreview = ({
|
|||||||
<DrawerHeader
|
<DrawerHeader
|
||||||
leftIcon=''
|
leftIcon=''
|
||||||
subtitle={info_umum?.file_name ?? 'Uniformity Details'}
|
subtitle={info_umum?.file_name ?? 'Uniformity Details'}
|
||||||
subtitleClassName='text-sm text-neutral line-clamp-1'
|
subtitleClassName='text-sm font-medium text-base-content/50 line-clamp-1'
|
||||||
showDivider={false}
|
showDivider={false}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@@ -114,8 +95,7 @@ const UniformityDetailsPreview = ({
|
|||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
|
|
||||||
{/* Form Section */}
|
{/* Form Section */}
|
||||||
<div className='divider mt-3.5'></div>
|
<section className='w-full p-4'>
|
||||||
<section className='w-full px-6'>
|
|
||||||
{info_umum ? (
|
{info_umum ? (
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
{/* Body Weight Details */}
|
{/* Body Weight Details */}
|
||||||
|
|||||||
@@ -493,24 +493,25 @@ const UniformityForm = ({
|
|||||||
<>
|
<>
|
||||||
<section className='w-full'>
|
<section className='w-full'>
|
||||||
<DrawerHeader
|
<DrawerHeader
|
||||||
leftIcon={formType == 'add' ? 'mdi:close' : 'mdi:arrow-left'}
|
leftIcon={formType == 'add' ? 'heroicons:x-mark' : 'mdi:arrow-left'}
|
||||||
leftIconSize={24}
|
leftIconSize={20}
|
||||||
leftIconHref={
|
leftIconHref={
|
||||||
formType == 'add'
|
formType == 'add'
|
||||||
? '/production/uniformity'
|
? '/production/uniformity'
|
||||||
: `/production/uniformity/detail`
|
: `/production/uniformity/detail`
|
||||||
}
|
}
|
||||||
leftIconClassName='hover:text-gray-400'
|
leftIconClassName='hover:text-base-content'
|
||||||
subtitle={formType == 'add' ? 'Add Uniformity' : 'Update Uniformity'}
|
subtitle={formType == 'add' ? 'Add Uniformity' : 'Update Uniformity'}
|
||||||
subtitleClassName='text-sm text-neutral'
|
subtitleClassName='text-sm font-medium text-base-content/50'
|
||||||
showDivider
|
showDivider
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='divider mt-3'></div>
|
<section className='w-full p-4'>
|
||||||
<section className='w-full px-6 mb-6'>
|
<h2 className='text-base font-medium text-base-content/50 font-roboto'>
|
||||||
<h2 className='text-2xl font-semibold mb-6'>Informasi Umum</h2>
|
Informasi Umum
|
||||||
|
</h2>
|
||||||
|
|
||||||
<form onSubmit={handleFormSubmit} className='flex flex-col gap-6'>
|
<form onSubmit={handleFormSubmit} className='flex flex-col gap-1.5'>
|
||||||
<DateInput
|
<DateInput
|
||||||
required
|
required
|
||||||
label='Tanggal'
|
label='Tanggal'
|
||||||
@@ -756,20 +757,23 @@ const UniformityForm = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isNextStep && (
|
{!isNextStep && (
|
||||||
<RequirePermission permissions='lti.production.uniformity.create'>
|
<>
|
||||||
<Button
|
<div className='border-t border-base-content/20 mt-3' />
|
||||||
type='submit'
|
<RequirePermission permissions='lti.production.uniformity.create'>
|
||||||
color='primary'
|
<Button
|
||||||
className='w-full'
|
type='submit'
|
||||||
disabled={formik.isSubmitting}
|
color='primary'
|
||||||
>
|
className='w-full mt-4'
|
||||||
{formik.isSubmitting ? (
|
disabled={formik.isSubmitting}
|
||||||
<span className='loading loading-spinner'></span>
|
>
|
||||||
) : (
|
{formik.isSubmitting ? (
|
||||||
'Next'
|
<span className='loading loading-spinner'></span>
|
||||||
)}
|
) : (
|
||||||
</Button>
|
'Next'
|
||||||
</RequirePermission>
|
)}
|
||||||
|
</Button>
|
||||||
|
</RequirePermission>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const UniformityPreviewForm = () => {
|
|||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
accessorKey: 'number',
|
accessorKey: 'number',
|
||||||
header: 'No',
|
header: 'Number',
|
||||||
cell: (props) => props.row.original.number,
|
cell: (props) => props.row.original.number,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -68,19 +68,18 @@ const UniformityPreviewForm = () => {
|
|||||||
<DrawerHeader
|
<DrawerHeader
|
||||||
leftIcon=''
|
leftIcon=''
|
||||||
subtitle={uniformityFormData?.file_name || 'Add Body Weight'}
|
subtitle={uniformityFormData?.file_name || 'Add Body Weight'}
|
||||||
subtitleClassName='text-sm text-neutral line-clamp-1'
|
subtitleClassName='text-sm font-medium text-base-content/50 line-clamp-1'
|
||||||
showDivider={false}
|
showDivider={false}
|
||||||
>
|
>
|
||||||
<Button variant='link' className='p-0 text-error' onClick={handleClose}>
|
<Button variant='link' className='p-0 text-error' onClick={handleClose}>
|
||||||
<Tooltip content='Hapus' position='left'>
|
<Tooltip content='Hapus' position='left'>
|
||||||
<Icon icon='mdi:trash-can-outline' width={20} height={20} />
|
<Icon icon='heroicons-outline:trash' width={18} height={18} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Button>
|
</Button>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
|
|
||||||
{/* Form Section */}
|
{/* Form Section */}
|
||||||
<div className='divider mt-3.5'></div>
|
<section className='w-full p-4'>
|
||||||
<section className='w-full px-6'>
|
|
||||||
{verifyUniformityResult ? (
|
{verifyUniformityResult ? (
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
<Table<BodyWeightData>
|
<Table<BodyWeightData>
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ import { useRouter } from 'next/navigation';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { UniformityApi } from '@/services/api/uniformity';
|
import { UniformityApi } from '@/services/api/uniformity';
|
||||||
import { isResponseError } from '@/lib/api-helper';
|
import { isResponseError } from '@/lib/api-helper';
|
||||||
import Badge from '@/components/Badge';
|
import StatusBadge from '@/components/helper/StatusBadge';
|
||||||
import { formatNumber } from '@/lib/helper';
|
import { formatNumber } from '@/lib/helper';
|
||||||
import {
|
import {
|
||||||
getWeightStatusColor,
|
|
||||||
getWeightStatusIndicatorColor,
|
|
||||||
getWeightStatusText,
|
getWeightStatusText,
|
||||||
|
getWeightStatusBadgeColor,
|
||||||
} from '@/components/pages/production/uniformity/uniformity-utils';
|
} from '@/components/pages/production/uniformity/uniformity-utils';
|
||||||
import { DetailOptionType } from '@/types/api/production/uniformity';
|
import { DetailOptionType } from '@/types/api/production/uniformity';
|
||||||
import {
|
import {
|
||||||
@@ -190,7 +189,7 @@ const UniformityResultForm = () => {
|
|||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
accessorKey: 'number',
|
accessorKey: 'number',
|
||||||
header: 'No',
|
header: 'Number',
|
||||||
cell: (props) => props.row.original.number,
|
cell: (props) => props.row.original.number,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -204,30 +203,12 @@ const UniformityResultForm = () => {
|
|||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const status = props.row.original.status;
|
const status = props.row.original.status;
|
||||||
return status ? (
|
return status ? (
|
||||||
<div className='w-full'>
|
<StatusBadge
|
||||||
<Badge
|
color={getWeightStatusBadgeColor(status)}
|
||||||
statusIndicator={true}
|
text={getWeightStatusText(status)}
|
||||||
variant='soft'
|
/>
|
||||||
className={{
|
|
||||||
badge: `rounded-xl w-full justify-start border border-gray-200 text-black ${getWeightStatusColor(status)}`,
|
|
||||||
status: getWeightStatusIndicatorColor(status),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getWeightStatusText(status)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<Badge
|
<StatusBadge color='info' text='Unknown' />
|
||||||
statusIndicator={true}
|
|
||||||
variant='soft'
|
|
||||||
className={{
|
|
||||||
badge:
|
|
||||||
'rounded-xl w-full justify-start border border-gray-200 text-black bg-info/10',
|
|
||||||
status: 'bg-info',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Unknown
|
|
||||||
</Badge>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -241,23 +222,24 @@ const UniformityResultForm = () => {
|
|||||||
<DrawerHeader
|
<DrawerHeader
|
||||||
leftIcon=''
|
leftIcon=''
|
||||||
subtitle={uniformityFormData?.document_name || 'Uniformity Result'}
|
subtitle={uniformityFormData?.document_name || 'Uniformity Result'}
|
||||||
subtitleClassName='text-sm text-neutral line-clamp-1'
|
subtitleClassName='text-sm font-medium text-base-content/50 line-clamp-1'
|
||||||
showDivider={false}
|
showDivider={false}
|
||||||
>
|
>
|
||||||
<Button variant='link' className='p-0 text-error' onClick={handleClose}>
|
<Button variant='link' className='p-0 text-error' onClick={handleClose}>
|
||||||
<Tooltip content='Hapus' position='left'>
|
<Tooltip content='Hapus' position='left'>
|
||||||
<Icon icon='mdi:trash-can-outline' width={20} height={20} />
|
<Icon icon='heroicons-outline:trash' width={20} height={20} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Button>
|
</Button>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
|
|
||||||
{/* Form Section */}
|
{/* Form Section */}
|
||||||
<div className='divider mt-3.5'></div>
|
<section className='w-full p-4'>
|
||||||
<section className='w-full px-6'>
|
|
||||||
{verifyUniformityResult ? (
|
{verifyUniformityResult ? (
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
<div className=''>
|
<div className=''>
|
||||||
<p className='text-sm font-medium mb-5'>Sampling and Range</p>
|
<h2 className='text-base font-medium text-base-content/50 font-roboto mb-5'>
|
||||||
|
Sampling and Range
|
||||||
|
</h2>
|
||||||
<Table<DetailOptionType>
|
<Table<DetailOptionType>
|
||||||
data={samplingTableData}
|
data={samplingTableData}
|
||||||
columns={columnsSampling}
|
columns={columnsSampling}
|
||||||
@@ -270,7 +252,9 @@ const UniformityResultForm = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className=''>
|
<div className=''>
|
||||||
<p className='text-sm font-medium mb-5'>Result</p>
|
<h2 className='text-base font-medium text-base-content/50 font-roboto mb-5'>
|
||||||
|
Result
|
||||||
|
</h2>
|
||||||
<Table<DetailOptionType>
|
<Table<DetailOptionType>
|
||||||
data={resultTableData}
|
data={resultTableData}
|
||||||
columns={resultColumns}
|
columns={resultColumns}
|
||||||
|
|||||||
+24
-15
@@ -1,4 +1,3 @@
|
|||||||
import Button from '@/components/Button';
|
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
|
|
||||||
const LeftLegend = () => {
|
const LeftLegend = () => {
|
||||||
@@ -45,11 +44,11 @@ const ChartArea = () => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex justify-between gap-2 sm:gap-4 md:gap-8 lg:gap-12 px-2 sm:px-4 py-2'>
|
<div className='flex justify-between gap-1 xs:gap-2 sm:gap-3 md:gap-4 lg:gap-6 px-1 xs:px-2 sm:px-3 md:px-4 py-2'>
|
||||||
{ranges.map((range) => (
|
{ranges.map((range) => (
|
||||||
<div
|
<div
|
||||||
key={range}
|
key={range}
|
||||||
className='skeleton h-3 w-8 sm:w-12 md:w-16 shrink-0'
|
className='skeleton h-3 w-6 xs:w-8 sm:w-10 md:w-12 flex-1 max-w-12 xs:max-w-14 sm:max-w-16'
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -65,28 +64,38 @@ const ChartArea = () => {
|
|||||||
|
|
||||||
const EmptyState = () => {
|
const EmptyState = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className='absolute inset-0 flex items-center justify-center z-10'>
|
||||||
<div className='absolute inset-0 flex flex-col items-center justify-center z-10 gap-2'>
|
<div className='flex flex-col items-center justify-center'>
|
||||||
<div className='border border-[#18181B]/25 rounded-2xl p-1 flex items-center justify-center my-2'>
|
{/* Filter icon */}
|
||||||
<Button className='rounded-2xl border border-sky-500 bg-primary text-white'>
|
<div className='mb-2'>
|
||||||
<Icon icon={'heroicons:funnel'} className='text-4xl text-whitd' />
|
<div className='w-12.5 h-12.5 bg-(--main-color-base-100,#FFFFFF) border border-base-content/10 rounded-[0.875rem] shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center'>
|
||||||
</Button>
|
<div className='w-9.5 h-9.5 bg-primary rounded-lg border border-primary flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'>
|
||||||
|
<Icon
|
||||||
|
icon='heroicons:funnel'
|
||||||
|
className='text-white'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className='text-xl font-semibold text-[#18181B80] leading-5'>
|
|
||||||
|
{/* Empty state text */}
|
||||||
|
<h3 className='text-base-content/50 font-semibold text-sm mb-1'>
|
||||||
No Filters Selected
|
No Filters Selected
|
||||||
</span>
|
</h3>
|
||||||
<span className='text-xs font-light text-[#18181B80] leading-4 text-center max-w-xs px-4'>
|
<p className='text-base-content/50 text-xs text-center max-w-xs'>
|
||||||
Please choose filters to narrow down your results and make your search
|
Please choose filters to narrow down your results and make your search
|
||||||
easier.
|
easier.
|
||||||
</span>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const UniformityBarChartSkeleton = () => {
|
const UniformityBarChartSkeleton = () => {
|
||||||
return (
|
return (
|
||||||
<div className='relative w-full h-full min-h-[300px] xl:min-h-[350px]'>
|
<div className='relative w-full h-full min-h-[270px] xl:min-h-[330px]'>
|
||||||
<div className='sm:flex hidden h-full gap-4'>
|
<div className='sm:flex hidden h-full gap-4'>
|
||||||
<LeftLegend />
|
<LeftLegend />
|
||||||
<ChartArea />
|
<ChartArea />
|
||||||
|
|||||||
+19
-13
@@ -1,4 +1,3 @@
|
|||||||
import Button from '@/components/Button';
|
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Cell, Pie, PieChart, ResponsiveContainer } from 'recharts';
|
import { Cell, Pie, PieChart, ResponsiveContainer } from 'recharts';
|
||||||
@@ -55,22 +54,29 @@ const UniformityGaugeChartSkeleton: React.FC<
|
|||||||
</Pie>
|
</Pie>
|
||||||
</PieChart>
|
</PieChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
<div className='absolute inset-x-0 top-24 flex flex-col items-center justify-center'>
|
<div className='absolute inset-x-0 top-24 flex flex-col items-center justify-center mt-4'>
|
||||||
<div className='border border-[#18181B]/25 rounded-2xl p-1 flex items-center justify-center mt-5'>
|
{/* Filter icon */}
|
||||||
<Button className='rounded-2xl border border-sky-500 bg-primary text-white'>
|
<div className='mb-2 mt-5'>
|
||||||
<Icon
|
<div className='w-12.5 h-12.5 bg-(--main-color-base-100,#FFFFFF) border border-base-content/10 rounded-[0.875rem] shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center'>
|
||||||
icon={'heroicons:funnel'}
|
<div className='w-9.5 h-9.5 bg-primary rounded-lg border border-primary flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'>
|
||||||
className='text-4xl text-whitd'
|
<Icon
|
||||||
/>
|
icon='heroicons:funnel'
|
||||||
</Button>
|
className='text-white'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className='text-lg font-semibold text-[#18181B80] leading-5 my-3'>
|
|
||||||
|
{/* Empty state text */}
|
||||||
|
<h3 className='text-base-content/50 font-semibold text-sm mb-1'>
|
||||||
No Filters Selected
|
No Filters Selected
|
||||||
</span>
|
</h3>
|
||||||
<span className='text-xs font-light text-[#18181B80] leading-4 text-center max-w-xs px-4'>
|
<p className='text-base-content/50 text-xs text-center max-w-xs'>
|
||||||
Please choose filters to narrow down your results and make your
|
Please choose filters to narrow down your results and make your
|
||||||
search easier.
|
search easier.
|
||||||
</span>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
import Button from '@/components/Button';
|
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
|
|
||||||
const UniformityTableSkeleton = () => {
|
const UniformityTableSkeleton = () => {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-center justify-center gap-2 my-20'>
|
<div className='flex flex-col items-center justify-center my-20'>
|
||||||
<div className='border border-[#18181B]/25 rounded-2xl p-1 flex items-center justify-center'>
|
{/* Document icon */}
|
||||||
<Button className='rounded-2xl border border-sky-500 bg-primary text-white'>
|
<div className='mb-2'>
|
||||||
<Icon
|
<div className='w-12.5 h-12.5 bg-(--main-color-base-100,#FFFFFF) border border-base-content/10 rounded-[0.875rem] shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center'>
|
||||||
icon={'heroicons-outline:chart-bar'}
|
<div className='w-9.5 h-9.5 bg-primary rounded-lg border border-primary flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'>
|
||||||
className='text-4xl text-whitd'
|
<Icon
|
||||||
/>
|
icon='heroicons:document-text'
|
||||||
</Button>
|
className='text-white'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className='text-xl font-semibold text-[#18181B80] leading-5'>
|
|
||||||
|
{/* Empty state text */}
|
||||||
|
<h3 className='text-base-content/50 font-semibold text-sm mb-1'>
|
||||||
No Data Available
|
No Data Available
|
||||||
</span>
|
</h3>
|
||||||
<span className='text-xs font-light text-[#18181B80] leading-4 text-center max-w-xs px-4'>
|
<p className='text-base-content/50 text-xs text-center max-w-xs'>
|
||||||
There is no uniformity data displayed. Enter uniformity check data to
|
There is no uniformity data displayed. Enter uniformity check data to
|
||||||
get started.
|
get started.
|
||||||
</span>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,6 +25,20 @@ export const getWeightStatusText = (status: string): string => {
|
|||||||
return weightStatusTextMap[status] || status;
|
return weightStatusTextMap[status] || status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const weightStatusBadgeColorMap: Record<
|
||||||
|
string,
|
||||||
|
'success' | 'error' | 'neutral' | 'info'
|
||||||
|
> = {
|
||||||
|
ideal: 'success',
|
||||||
|
outside: 'error',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWeightStatusBadgeColor = (
|
||||||
|
status: string
|
||||||
|
): 'success' | 'error' | 'neutral' | 'info' => {
|
||||||
|
return weightStatusBadgeColorMap[status] || 'neutral';
|
||||||
|
};
|
||||||
|
|
||||||
export const statusColorMap: Record<string, string> = {
|
export const statusColorMap: Record<string, string> = {
|
||||||
APPROVED: 'bg-[#00D39033]',
|
APPROVED: 'bg-[#00D39033]',
|
||||||
Disetujui: 'bg-[#00D39033]',
|
Disetujui: 'bg-[#00D39033]',
|
||||||
@@ -63,3 +77,29 @@ export const getStatusIndicatorColor = (status: string): string => {
|
|||||||
export const getStatusText = (status: string): string => {
|
export const getStatusText = (status: string): string => {
|
||||||
return statusTextMap[status] || status;
|
return statusTextMap[status] || status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const statusBadgeColorMap: Record<
|
||||||
|
string,
|
||||||
|
'success' | 'error' | 'neutral' | 'info'
|
||||||
|
> = {
|
||||||
|
APPROVED: 'success',
|
||||||
|
Disetujui: 'success',
|
||||||
|
approved: 'success',
|
||||||
|
disetujui: 'success',
|
||||||
|
REJECTED: 'error',
|
||||||
|
Ditolak: 'error',
|
||||||
|
rejected: 'error',
|
||||||
|
ditolak: 'error',
|
||||||
|
CREATED: 'neutral',
|
||||||
|
Pengajuan: 'neutral',
|
||||||
|
created: 'neutral',
|
||||||
|
pengajuan: 'neutral',
|
||||||
|
PENDING: 'neutral',
|
||||||
|
pending: 'neutral',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStatusBadgeColor = (
|
||||||
|
status: string
|
||||||
|
): 'success' | 'error' | 'neutral' | 'info' => {
|
||||||
|
return statusBadgeColorMap[status] || 'neutral';
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user