mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-88): memisahkan file api project flock & penyesuaian tipe data dan paylod dengan BE
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
@@ -12,10 +12,11 @@ const ProjectFlockEdit = () => {
|
||||
|
||||
const projectFlockId = searchParams.get('projectFlockId');
|
||||
|
||||
const { data: projectFlock, isLoading: isLoadingCostumer } = useSWR(
|
||||
projectFlockId,
|
||||
(id: number) => ProjectFlockApi.getSingle(id)
|
||||
);
|
||||
const {
|
||||
data: projectFlock,
|
||||
isLoading: isLoadingProjectFlock,
|
||||
mutate: refreshProjectFlocks,
|
||||
} = useSWR(projectFlockId, (id: number) => ProjectFlockApi.getSingle(id));
|
||||
|
||||
if (!projectFlockId) {
|
||||
router.back();
|
||||
@@ -27,18 +28,25 @@ const ProjectFlockEdit = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingCostumer && (!projectFlock || isResponseError(projectFlock))) {
|
||||
if (
|
||||
!isLoadingProjectFlock &&
|
||||
(!projectFlock || isResponseError(projectFlock))
|
||||
) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingCostumer && (
|
||||
<div className='w-full p-4 flex flex-col justify-center'>
|
||||
{isLoadingProjectFlock && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingCostumer && isResponseSuccess(projectFlock) && (
|
||||
<ProjectFlockForm formType='edit' initialValues={projectFlock.data} />
|
||||
{!isLoadingProjectFlock && isResponseSuccess(projectFlock) && (
|
||||
<>
|
||||
{JSON.stringify(projectFlock.data)}
|
||||
|
||||
<ProjectFlockForm formType='edit' initialValues={projectFlock.data} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { FlockApi } from '@/services/api/master-data';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
@@ -23,12 +23,13 @@ const ProjectFlockDetail = () => {
|
||||
mutate: refreshProjectFlock,
|
||||
} = useSWR(projectFlockId, (id: number) => ProjectFlockApi.getSingle(id));
|
||||
|
||||
const flockUrl = `${FlockApi.basePath}`;
|
||||
const {
|
||||
data: flock,
|
||||
isLoading: isLoadingFlock,
|
||||
mutate: refreshFlock,
|
||||
} = useSWR(flockUrl, FlockApi.getAllFetcher);
|
||||
data: approvalLines,
|
||||
isLoading: isLoadingApprovalLines,
|
||||
mutate: refreshApprovalLines,
|
||||
} = useSWR('approvals', (id: number) =>
|
||||
ProjectFlockApi.getApprovalLines((projectFlockId ?? 0) as number)
|
||||
);
|
||||
|
||||
if (!projectFlockId) {
|
||||
router.back();
|
||||
@@ -48,37 +49,17 @@ const ProjectFlockDetail = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Attach flock id to project flock
|
||||
let projectFlockAttached: ProjectFlock | undefined;
|
||||
|
||||
if (isResponseSuccess(projectFlock) && isResponseSuccess(flock)) {
|
||||
projectFlockAttached = {
|
||||
...projectFlock.data,
|
||||
flock: flock.data.find(
|
||||
(flock) =>
|
||||
flock.name ==
|
||||
projectFlock?.data?.flock_name
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.slice(0, -1)
|
||||
.join(' ')
|
||||
),
|
||||
};
|
||||
console.log('projectFlockAttached');
|
||||
console.log(projectFlockAttached);
|
||||
console.log('flocks');
|
||||
console.log(flock.data);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingProjectFlock && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{projectFlockAttached && (
|
||||
<div className='w-full p-4 flex flex-col justify-center'>
|
||||
{isLoadingProjectFlock ||
|
||||
(isLoadingApprovalLines && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
))}
|
||||
{isResponseSuccess(projectFlock) && isResponseSuccess(approvalLines) && (
|
||||
<ProjectFlockForm
|
||||
formType='detail'
|
||||
initialValues={projectFlockAttached}
|
||||
initialValues={projectFlock.data}
|
||||
initialApprovals={approvalLines.data}
|
||||
refreshProjectFlocks={refreshProjectFlock}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import { HTMLAttributes, ReactNode, useEffect, useState } from 'react';
|
||||
import { cn } from '@/lib/helper';
|
||||
|
||||
export interface TabItem {
|
||||
id: string;
|
||||
label: ReactNode;
|
||||
content?: ReactNode;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface TabsProps
|
||||
extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
|
||||
tabs: TabItem[];
|
||||
variant?: 'bordered' | 'lifted' | 'boxed';
|
||||
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
||||
placement?: 'top' | 'bottom';
|
||||
/** Tab yang aktif secara default (uncontrolled mode) */
|
||||
defaultActiveId?: string;
|
||||
/** Tab yang aktif (controlled mode, dikontrol parent) */
|
||||
activeTabId?: string;
|
||||
className?:
|
||||
| string
|
||||
| {
|
||||
wrapper?: string;
|
||||
tab?: string;
|
||||
content?: string;
|
||||
};
|
||||
onTabChange?: (tabId: string) => void;
|
||||
}
|
||||
|
||||
const Tabs = ({
|
||||
tabs,
|
||||
variant,
|
||||
size = 'md',
|
||||
placement = 'top',
|
||||
defaultActiveId,
|
||||
activeTabId: controlledActiveId,
|
||||
className,
|
||||
onTabChange,
|
||||
...props
|
||||
}: TabsProps) => {
|
||||
// State internal hanya dipakai kalau `activeTabId` (controlled) tidak diset
|
||||
const [uncontrolledActiveId, setUncontrolledActiveId] = useState(
|
||||
defaultActiveId || tabs[0]?.id || ''
|
||||
);
|
||||
|
||||
const isControlled = controlledActiveId !== undefined;
|
||||
const activeTabId = isControlled ? controlledActiveId : uncontrolledActiveId;
|
||||
|
||||
const handleTabChange = (tabId: string) => {
|
||||
if (tabId === activeTabId) return;
|
||||
if (!isControlled) setUncontrolledActiveId(tabId);
|
||||
onTabChange?.(tabId);
|
||||
};
|
||||
|
||||
const { wrapper: wrapperClassName, tab: tabClassName } =
|
||||
typeof className === 'object'
|
||||
? className
|
||||
: { wrapper: className, tab: undefined };
|
||||
|
||||
const getTabsClasses = () => {
|
||||
const variantClasses: Record<string, string> = {
|
||||
bordered: 'tabs-bordered',
|
||||
lifted: 'tabs-lift',
|
||||
boxed: 'tabs-box',
|
||||
};
|
||||
|
||||
const sizeClasses: Record<string, string> = {
|
||||
xs: 'tabs-xs',
|
||||
sm: 'tabs-sm',
|
||||
md: '',
|
||||
lg: 'tabs-lg',
|
||||
xl: 'tabs-xl',
|
||||
};
|
||||
|
||||
const placementClasses: Record<string, string> = {
|
||||
top: '',
|
||||
bottom: 'tabs-bottom',
|
||||
};
|
||||
|
||||
return cn(
|
||||
'tabs',
|
||||
variant && variantClasses[variant],
|
||||
sizeClasses[size],
|
||||
placementClasses[placement],
|
||||
wrapperClassName
|
||||
);
|
||||
};
|
||||
|
||||
const getTabClasses = (isActive: boolean, isDisabled?: boolean) =>
|
||||
cn(
|
||||
'tab',
|
||||
{
|
||||
'tab-active': isActive,
|
||||
'tab-disabled': isDisabled,
|
||||
},
|
||||
tabClassName
|
||||
);
|
||||
|
||||
const activeContent = tabs.find((tab) => tab.id === activeTabId)?.content;
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
className={cn(
|
||||
'w-full',
|
||||
typeof className === 'string' ? className : undefined
|
||||
)}
|
||||
>
|
||||
<div role='tablist' className={getTabsClasses()}>
|
||||
{tabs.map(({ id, label, disabled }) => (
|
||||
<button
|
||||
key={id}
|
||||
role='tab'
|
||||
className={getTabClasses(id === activeTabId, disabled)}
|
||||
onClick={() => !disabled && handleTabChange(id)}
|
||||
disabled={disabled}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{activeContent && <div className='mt-4'>{activeContent}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tabs;
|
||||
@@ -29,7 +29,6 @@ const ChickinFormKandang = ({
|
||||
return (
|
||||
<div className='flex flex-col gap-4'>
|
||||
<FormHeader
|
||||
type='add'
|
||||
title='Chick In DOC'
|
||||
backUrl={`/production/project-flock/chickin/add?projectFlockId=${initialValues?.project_flock?.id}`}
|
||||
/>
|
||||
@@ -96,7 +95,7 @@ const ChickinFormKandang = ({
|
||||
tabs={[
|
||||
{
|
||||
id: 'formChickIn',
|
||||
label: 'Form Chick In',
|
||||
label: 'Tambah Chick In',
|
||||
content: (
|
||||
<ChickinFormView
|
||||
initialValues={initialValues}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import Alert from '@/components/Alert';
|
||||
import Button from '@/components/Button';
|
||||
import Card from '@/components/Card';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import PillBadge from '@/components/PillBadge';
|
||||
import Table from '@/components/Table';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { cn, formatDate, formatNumber } from '@/lib/helper';
|
||||
import { ChickinApi } from '@/services/api/production/chickin';
|
||||
import {
|
||||
Chickin,
|
||||
ProjectFlockKandang,
|
||||
} from '@/types/api/production/project-flock-kandang';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
const ChickinLogsView = ({
|
||||
initialValues,
|
||||
afterSubmit,
|
||||
}: {
|
||||
initialValues: ProjectFlockKandang;
|
||||
afterSubmit?: () => void;
|
||||
}) => {
|
||||
const confirmModal = useModal();
|
||||
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
||||
const [chickinErrorMessage, setChickinErrorMessage] = useState('');
|
||||
|
||||
const handleClickApprove = () => {
|
||||
confirmModal.openModal();
|
||||
};
|
||||
|
||||
const confirmationModalApproveClickHandler = async () => {
|
||||
setChickinErrorMessage('');
|
||||
setIsApproveLoading(true);
|
||||
const approveChickinRes = await ChickinApi.singleApproval(
|
||||
initialValues?.id as number,
|
||||
'APPROVED'
|
||||
);
|
||||
if (isResponseSuccess(approveChickinRes)) {
|
||||
toast.success(approveChickinRes?.message as string);
|
||||
}
|
||||
if (isResponseError(approveChickinRes)) {
|
||||
toast.error(approveChickinRes?.message as string);
|
||||
setChickinErrorMessage(approveChickinRes?.message as string);
|
||||
}
|
||||
confirmModal.closeModal();
|
||||
setIsApproveLoading(false);
|
||||
if (afterSubmit) {
|
||||
afterSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card
|
||||
title='Riwayat Chick In'
|
||||
className={{
|
||||
wrapper: 'w-full bg-white',
|
||||
}}
|
||||
>
|
||||
<div className='flex flex-row justify-start gap-3 mt-3'>
|
||||
<Button
|
||||
color='success'
|
||||
variant='outline'
|
||||
onClick={handleClickApprove}
|
||||
>
|
||||
<Icon width={24} height={24} icon='material-symbols:check' />
|
||||
Approve
|
||||
</Button>
|
||||
</div>
|
||||
<Table<Chickin>
|
||||
data={initialValues?.chickins || []}
|
||||
columns={[
|
||||
{
|
||||
header: '#',
|
||||
cell: (props) => props.row.index + 1,
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.chick_in_date,
|
||||
header: 'Tanggal Chick In',
|
||||
cell: (props) => {
|
||||
return formatDate(props.getValue() as string, 'DD MMM YYYY');
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.product_warehouse?.warehouse?.name,
|
||||
header: 'Kandang',
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.product_warehouse?.product?.name,
|
||||
header: 'Produk',
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.usage_qty ?? row.pending_usage_qty,
|
||||
header: 'Jumlah Chick In',
|
||||
cell: (props) => {
|
||||
if (props.row.original.usage_qty != 0) {
|
||||
return formatNumber(props.row.original.usage_qty);
|
||||
} else if (props.row.original.pending_usage_qty != 0) {
|
||||
return formatNumber(props.row.original.pending_usage_qty);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.pending_usage_qty,
|
||||
header: 'Status',
|
||||
cell: (props) => {
|
||||
return (
|
||||
<PillBadge
|
||||
content={
|
||||
props.row.original.usage_qty !== 0
|
||||
? 'Disetujui'
|
||||
: props.row.original.pending_usage_qty !== 0
|
||||
? 'Pending'
|
||||
: '-'
|
||||
}
|
||||
color={
|
||||
props.row.original.usage_qty !== 0
|
||||
? 'green'
|
||||
: props.row.original.pending_usage_qty !== 0
|
||||
? 'yellow'
|
||||
: 'gray'
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'mb-20': initialValues?.chickins?.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',
|
||||
paginationClassName: 'hidden',
|
||||
}}
|
||||
/>
|
||||
{chickinErrorMessage && (
|
||||
<div className='w-full' onClick={() => setChickinErrorMessage('')}>
|
||||
<Alert color='error'>{chickinErrorMessage}</Alert>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
<ConfirmationModal
|
||||
ref={confirmModal.ref}
|
||||
type='success'
|
||||
text={`Apakah anda yakin ingin approve data Chickin yang Pending?`}
|
||||
secondaryButton={{
|
||||
text: 'Tidak',
|
||||
}}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
color: 'success',
|
||||
onClick: confirmationModalApproveClickHandler,
|
||||
isLoading: isApproveLoading,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChickinLogsView;
|
||||
@@ -0,0 +1,236 @@
|
||||
'use client';
|
||||
|
||||
import Card from '@/components/Card';
|
||||
import Table from '@/components/Table';
|
||||
import {
|
||||
ChickinFormValues,
|
||||
ChickinRequestFormValues,
|
||||
ChickinSchema,
|
||||
} from '../ChickinForm.schema';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||
import NumberInput from '@/components/input/NumberInput';
|
||||
import Button from '@/components/Button';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import { flushSync } from 'react-dom';
|
||||
import { CreateChickinPayload } from '@/types/api/production/chickin';
|
||||
import { ChickinApi } from '@/services/api/production/chickin';
|
||||
import { isResponseError } from '@/lib/api-helper';
|
||||
import toast from 'react-hot-toast';
|
||||
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Alert from '@/components/Alert';
|
||||
|
||||
const ChickinFormView = ({
|
||||
formType = 'add',
|
||||
initialValues,
|
||||
afterSubmit,
|
||||
}: {
|
||||
formType?: 'add' | 'detail' | 'edit';
|
||||
initialValues: ProjectFlockKandang;
|
||||
afterSubmit?: () => void;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const [chickinErrorMessage, setChickinErrorMessage] = useState('');
|
||||
|
||||
const createChickin = useCallback(
|
||||
async (payload: CreateChickinPayload) => {
|
||||
const createChickinRes = await ChickinApi.create(payload);
|
||||
if (isResponseError(createChickinRes)) {
|
||||
setChickinErrorMessage(createChickinRes.message);
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success(createChickinRes?.message as string);
|
||||
// router.push(
|
||||
// `/production/project-flock/chickin/add?projectFlockId=${initialValues?.project_flock?.id}`
|
||||
// );
|
||||
if (afterSubmit) {
|
||||
afterSubmit();
|
||||
}
|
||||
},
|
||||
[router]
|
||||
);
|
||||
const handleReset = async () => {
|
||||
flushSync(() => {
|
||||
formik.resetForm({
|
||||
values: {
|
||||
project_flock_kandang_id: initialValues?.id,
|
||||
chickin_requests: initialValues?.available_qtys
|
||||
? initialValues.available_qtys.map((availableQty) => ({
|
||||
chick_in_date: '',
|
||||
product_warehouse_id: availableQty.product_warehouse.id,
|
||||
available_qty: availableQty.available_qty,
|
||||
note: `Chickin project-flock-kandang-${initialValues?.id} product-warehouse-${availableQty.product_warehouse.id}`,
|
||||
}))
|
||||
: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
formik.setTouched({
|
||||
chickin_requests: initialValues?.available_qtys?.map(() => ({
|
||||
chick_in_date: true,
|
||||
})),
|
||||
});
|
||||
|
||||
const errors = await formik.validateForm();
|
||||
formik.setErrors(errors);
|
||||
};
|
||||
|
||||
const formik = useFormik<ChickinFormValues>({
|
||||
enableReinitialize: true,
|
||||
validationSchema: ChickinSchema,
|
||||
initialValues: {
|
||||
project_flock_kandang_id: initialValues?.id,
|
||||
chickin_requests: initialValues?.available_qtys
|
||||
? initialValues.available_qtys.map((availableQty) => ({
|
||||
chick_in_date: '',
|
||||
product_warehouse_id: availableQty.product_warehouse.id,
|
||||
available_qty: availableQty.available_qty,
|
||||
note: `Chickin project-flock-kandang-${initialValues?.id} product-warehouse-${availableQty.product_warehouse.id}`,
|
||||
}))
|
||||
: [],
|
||||
},
|
||||
onSubmit: (values) => {
|
||||
setChickinErrorMessage('');
|
||||
createChickin(values as CreateChickinPayload);
|
||||
if (afterSubmit) {
|
||||
afterSubmit();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const { setValues: formikSetValues } = formik;
|
||||
|
||||
useEffect(() => {
|
||||
formikSetValues({
|
||||
project_flock_kandang_id: initialValues?.id,
|
||||
chickin_requests: initialValues?.available_qtys
|
||||
? initialValues.available_qtys.map((availableQty) => ({
|
||||
chick_in_date: '',
|
||||
product_warehouse_id: availableQty.product_warehouse.id,
|
||||
available_qty: availableQty.available_qty,
|
||||
note: `Chickin project-flock-kandang-${initialValues?.id} product-warehouse-${availableQty.product_warehouse.id}`,
|
||||
}))
|
||||
: [],
|
||||
});
|
||||
}, [formikSetValues, initialValues]);
|
||||
|
||||
return (
|
||||
<form
|
||||
className='flex flex-col gap-4'
|
||||
onReset={(e) => {
|
||||
handleReset();
|
||||
}}
|
||||
onSubmit={formik.handleSubmit}
|
||||
>
|
||||
<Card
|
||||
title='Informasi Chick In DOC'
|
||||
className={{
|
||||
wrapper: 'w-full bg-white',
|
||||
}}
|
||||
>
|
||||
<Table<ChickinRequestFormValues>
|
||||
data={formik.values.chickin_requests || []}
|
||||
columns={[
|
||||
{
|
||||
accessorFn: (row) => row.chick_in_date,
|
||||
header: 'Tanggal Chick In',
|
||||
cell(props) {
|
||||
return (
|
||||
<DateInput
|
||||
name={`chickin_requests[${props.row.index}].chick_in_date`}
|
||||
value={
|
||||
formik.values.chickin_requests[props.row.index]
|
||||
?.chick_in_date as string
|
||||
}
|
||||
onChange={formik.handleChange}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.product_warehouse_id,
|
||||
header: 'Produk',
|
||||
cell(props) {
|
||||
const availableQty = initialValues?.available_qtys?.find(
|
||||
(availableQty) =>
|
||||
availableQty.product_warehouse.id ===
|
||||
props.row.original.product_warehouse_id
|
||||
);
|
||||
return (
|
||||
<SelectInput
|
||||
value={
|
||||
{
|
||||
label: availableQty?.product_warehouse?.product?.name,
|
||||
value: availableQty?.product_warehouse?.product?.id,
|
||||
} as OptionType
|
||||
}
|
||||
options={[]}
|
||||
isDisabled
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.product_warehouse_id,
|
||||
header: 'Jumlah (ekor)',
|
||||
cell(props) {
|
||||
const availableQty = initialValues?.available_qtys?.find(
|
||||
(availableQty) =>
|
||||
availableQty.product_warehouse.id ===
|
||||
props.row.original.product_warehouse_id
|
||||
);
|
||||
return (
|
||||
<NumberInput
|
||||
name='qty[]'
|
||||
value={availableQty?.available_qty}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
className={{
|
||||
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-2 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-2 py-2 last:flex last:flex-row last:justify-end',
|
||||
paginationClassName: 'hidden',
|
||||
}}
|
||||
emptyContent={
|
||||
<div className='w-full p-5 text-center'>
|
||||
<span className='text-lg opacity-50'>
|
||||
Isi persediaan DOC untuk kandang belum tersedia...
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
<div className='flex flex-row justify-center gap-3'>
|
||||
<Button type='reset' color='warning' disabled={formik.isSubmitting}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button
|
||||
type='submit'
|
||||
color='primary'
|
||||
disabled={!formik.isValid || formik.isSubmitting}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
{chickinErrorMessage && (
|
||||
<div className='w-full' onClick={() => setChickinErrorMessage('')}>
|
||||
<Alert color='error'>{chickinErrorMessage}</Alert>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChickinFormView;
|
||||
@@ -13,7 +13,7 @@ import { ROWS_OPTIONS } from '@/config/constant';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { cn } from '@/lib/helper';
|
||||
import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
|
||||
@@ -11,10 +11,8 @@ import PillBadge from '@/components/PillBadge';
|
||||
import Table from '@/components/Table';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { cn } from '@/lib/helper';
|
||||
import {
|
||||
ProjectFlockApi,
|
||||
ProjectFlockKandangApi,
|
||||
} from '@/services/api/production';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { ProjectFlockKandangApi } from '@/services/api/production';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
|
||||
@@ -34,7 +34,7 @@ import TextInput from '@/components/input/TextInput';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import Collapse from '@/components/Collapse';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { BaseApiResponse, BaseGroupedApproval } from '@/types/api/api-general';
|
||||
import { APPROVAL_WORKFLOWS, FLOCK_CATEGORY_OPTIONS } from '@/config/constant';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
@@ -45,10 +45,12 @@ import StepItem from '@/components/steps/StepItem';
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
import { id, is } from 'react-day-picker/locale';
|
||||
import { formatDate } from '@/lib/helper';
|
||||
import Card from '@/components/Card';
|
||||
|
||||
interface ProjectFlockFormProps {
|
||||
formType?: 'add' | 'edit' | 'detail';
|
||||
initialValues?: ProjectFlock;
|
||||
initialApprovals?: BaseGroupedApproval[];
|
||||
refreshProjectFlocks?: KeyedMutator<
|
||||
BaseApiResponse<ProjectFlock> | undefined
|
||||
>;
|
||||
@@ -57,6 +59,7 @@ interface ProjectFlockFormProps {
|
||||
const ProjectFlockForm = ({
|
||||
formType = 'add',
|
||||
initialValues,
|
||||
initialApprovals,
|
||||
refreshProjectFlocks,
|
||||
}: ProjectFlockFormProps) => {
|
||||
// State
|
||||
@@ -70,14 +73,16 @@ const ProjectFlockForm = ({
|
||||
useState('');
|
||||
const [selectedArea, setSelectedArea] = useState('');
|
||||
const [selectedLocation, setSelectedLocation] = useState('');
|
||||
const [disabledLocation, setDisabledLocation] = useState(true);
|
||||
const [disabledLocation, setDisabledLocation] = useState(
|
||||
initialValues?.location?.id ? false : true
|
||||
);
|
||||
const [openSelectKandangs, setOpenSelectKandangs] = useState(
|
||||
initialValues?.kandangs && initialValues?.kandangs?.length > 0
|
||||
);
|
||||
const [optionsKandang, setOptionsKandang] = useState<Kandang[]>(
|
||||
initialValues?.kandangs ?? []
|
||||
);
|
||||
const [selectedFlock, setSelectedFlock] = useState<number>(
|
||||
const [selectedFlock, setSelectedFlock] = useState<number | undefined>(
|
||||
initialValues?.flock?.id ?? 0
|
||||
);
|
||||
|
||||
@@ -131,7 +136,12 @@ const ProjectFlockForm = ({
|
||||
options: optionsLocation,
|
||||
isLoadingOptions: isLoadingLocations,
|
||||
rawData: locations,
|
||||
} = useSelect(LocationApi.basePath, 'id', 'name');
|
||||
} = useSelect(LocationApi.basePath, 'id', 'name', '', {
|
||||
area_id:
|
||||
selectedArea != ''
|
||||
? selectedArea
|
||||
: ((initialValues?.area?.id ?? '') as string),
|
||||
});
|
||||
|
||||
const {
|
||||
options: optionsFcr,
|
||||
@@ -150,27 +160,34 @@ const ProjectFlockForm = ({
|
||||
} = useSWR(kandangUrl, KandangApi.getAllFetcher);
|
||||
|
||||
const { data: periodFlocks, isLoading: isLoadingPeriodFlocks } = useSWR(
|
||||
`${selectedFlock.toString()}/periods`,
|
||||
`${selectedFlock?.toString()}/periods`,
|
||||
(id: string) => ProjectFlockApi.getNextPeriod(id)
|
||||
);
|
||||
|
||||
const { data: approvalLines, isLoading: isLoadingApprovalLines } = useSWR(
|
||||
selectedFlock.toString(),
|
||||
(id: number) => ProjectFlockApi.getApprovalLines(id)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isResponseSuccess(kandang)) {
|
||||
if (selectedLocation) {
|
||||
setOptionsKandang(kandang.data);
|
||||
setOpenSelectKandangs(true);
|
||||
} else {
|
||||
formik.setFieldValue('kandang_ids', []);
|
||||
setOptionsKandang([]);
|
||||
setOpenSelectKandangs(false);
|
||||
formik.setFieldValue('kandang_ids', []);
|
||||
const selectedRowIds = Object.keys(rowSelection)
|
||||
.filter((id) => rowSelection[id])
|
||||
.map((id) => parseInt(id));
|
||||
if (
|
||||
JSON.stringify(kandang.data.map((k) => k.id)) !==
|
||||
JSON.stringify(formik.values.kandang_ids)
|
||||
) {
|
||||
formik.setFieldValue('kandang_ids', []);
|
||||
setRowSelection({});
|
||||
} else {
|
||||
formik.setFieldValue('kandang_ids', selectedRowIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [kandang]);
|
||||
}, [kandang, selectedLocation]);
|
||||
useEffect(() => {
|
||||
if (initialValues?.kandangs) {
|
||||
refreshKandang();
|
||||
@@ -181,7 +198,7 @@ const ProjectFlockForm = ({
|
||||
);
|
||||
setRowSelection(newRowSelection);
|
||||
}
|
||||
}, [initialValues, refreshKandang]);
|
||||
}, [initialValues, kandang]);
|
||||
|
||||
// Options Handler
|
||||
const areaChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
@@ -202,9 +219,13 @@ const ProjectFlockForm = ({
|
||||
};
|
||||
|
||||
const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
formik.setFieldValue('kandang_ids', []);
|
||||
setSelectedLocation((val as OptionType)?.value as string);
|
||||
optionChangeHandler(val, 'location');
|
||||
formik.setFieldValue('kandang_ids', []);
|
||||
const selectedRowIds = Object.keys(rowSelection)
|
||||
.filter((id) => rowSelection[id])
|
||||
.map((id) => parseInt(id));
|
||||
formik.setFieldValue('kandang_ids', selectedRowIds);
|
||||
};
|
||||
|
||||
const optionChangeHandler = (
|
||||
@@ -216,13 +237,8 @@ const ProjectFlockForm = ({
|
||||
`${inputName}_id`,
|
||||
val ? (val as OptionType)?.value : 0
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`${inputName}_name`,
|
||||
val ? (val as OptionType)?.label : 0
|
||||
);
|
||||
|
||||
formik.setFieldTouched(`${inputName}_id`, true);
|
||||
formik.setFieldTouched(`${inputName}_name`, true);
|
||||
};
|
||||
|
||||
const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
@@ -267,11 +283,12 @@ const ProjectFlockForm = ({
|
||||
// Formik InitialValue
|
||||
const formikInitialValues = useMemo<ProjectFlockFormValues>(() => {
|
||||
return {
|
||||
name: initialValues?.name ?? '',
|
||||
flock: initialValues?.flock_name
|
||||
name: initialValues?.flock_name,
|
||||
flock: initialValues?.flock
|
||||
? {
|
||||
value: initialValues?.flock?.id ?? 0,
|
||||
label: initialValues?.flock_name,
|
||||
label:
|
||||
initialValues?.flock?.name ?? initialValues?.flock_name ?? '',
|
||||
}
|
||||
: null,
|
||||
area: initialValues?.area
|
||||
@@ -312,11 +329,57 @@ const ProjectFlockForm = ({
|
||||
| undefined
|
||||
)[],
|
||||
};
|
||||
}, [initialValues, flocks]);
|
||||
}, [initialValues]);
|
||||
|
||||
// Formik
|
||||
const formik = useFormik<ProjectFlockFormValues>({
|
||||
initialValues: formikInitialValues,
|
||||
initialValues: {
|
||||
name: initialValues?.flock_name,
|
||||
flock: initialValues?.flock
|
||||
? {
|
||||
value: initialValues?.flock?.id ?? 0,
|
||||
label:
|
||||
initialValues?.flock?.name ?? initialValues?.flock_name ?? '',
|
||||
}
|
||||
: null,
|
||||
area: initialValues?.area
|
||||
? {
|
||||
value: initialValues.area?.id,
|
||||
label: initialValues.area.name,
|
||||
}
|
||||
: null,
|
||||
category_option: initialValues?.category
|
||||
? {
|
||||
value: initialValues.category,
|
||||
label: initialValues.category,
|
||||
}
|
||||
: null,
|
||||
fcr: initialValues?.fcr
|
||||
? {
|
||||
value: initialValues.fcr?.id,
|
||||
label: initialValues.fcr.name,
|
||||
}
|
||||
: null,
|
||||
location: initialValues?.location
|
||||
? {
|
||||
value: initialValues.location?.id,
|
||||
label: initialValues.location.name,
|
||||
}
|
||||
: null,
|
||||
flock_id: initialValues?.flock?.id ?? 0,
|
||||
flock_name: initialValues?.flock_name ?? '',
|
||||
area_id: initialValues?.area?.id ?? 0,
|
||||
category: initialValues?.category as NonNullable<
|
||||
'GROWING' | 'LAYING' | undefined
|
||||
>,
|
||||
fcr_id: initialValues?.fcr?.id ?? 0,
|
||||
location_id: initialValues?.location?.id ?? 0,
|
||||
period: initialValues?.period ?? 1,
|
||||
kandang_ids: initialValues?.kandangs?.map((k: Kandang) => k.id) as (
|
||||
| number
|
||||
| undefined
|
||||
)[],
|
||||
} as ProjectFlockFormValues,
|
||||
enableReinitialize: true,
|
||||
validationSchema:
|
||||
formType == 'add' ? ProjectFlockFormSchema : UpdateProjectFlockFormSchema,
|
||||
@@ -326,7 +389,7 @@ const ProjectFlockForm = ({
|
||||
onSubmit: async (values) => {
|
||||
setProjectFlockFormErrorMessage('');
|
||||
const payload: CreateProjectFlockPayload = {
|
||||
flock_name: values.flock_name as string,
|
||||
flock_name: values.flock?.label as string,
|
||||
area_id: values.area_id as number,
|
||||
category: values.category as string,
|
||||
fcr_id: values.fcr_id as number,
|
||||
@@ -367,7 +430,7 @@ const ProjectFlockForm = ({
|
||||
|
||||
useEffect(() => {
|
||||
formikSetValues(formikInitialValues);
|
||||
}, [formikSetValues, formikInitialValues]);
|
||||
}, [formikSetValues]);
|
||||
|
||||
// Aktifkan lokasi jika formType = 'detail'
|
||||
useEffect(() => {
|
||||
@@ -397,7 +460,7 @@ const ProjectFlockForm = ({
|
||||
if (isResponseError(periodFlocks)) {
|
||||
console.log(periodFlocks?.message as string);
|
||||
}
|
||||
}, [periodFlocks, toast]);
|
||||
}, [periodFlocks]);
|
||||
|
||||
useEffect(() => {
|
||||
const selectedRowIds = Object.keys(rowSelection)
|
||||
@@ -495,11 +558,11 @@ const ProjectFlockForm = ({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{formType == 'detail' && isResponseSuccess(approvalLines) && (
|
||||
{formType == 'detail' && initialApprovals && (
|
||||
<div className='w-full flex items-center gap-2 my-4'>
|
||||
<Steps className='w-full'>
|
||||
{projectFlockSteps?.steps.map((step, idx) => {
|
||||
const approvalLogs = approvalLines.data.find(
|
||||
const approvalLogs = initialApprovals.find(
|
||||
(approve) => approve.step_number == step.step_number
|
||||
);
|
||||
return (
|
||||
@@ -523,7 +586,7 @@ const ProjectFlockForm = ({
|
||||
content={
|
||||
<ul>
|
||||
{approvalLogs &&
|
||||
approvalLogs.approvals.map((approval, idx) => {
|
||||
approvalLogs?.approvals?.map((approval, idx) => {
|
||||
return (
|
||||
<li className='mb-2' key={`key-logs-${idx}`}>
|
||||
<div className='flex flex-col w-full text-start text-base'>
|
||||
@@ -601,8 +664,43 @@ const ProjectFlockForm = ({
|
||||
<Icon icon='mdi:times' width={24} height={24} />
|
||||
Reject
|
||||
</Button>
|
||||
{initialValues?.approval?.step_number == 2 && (
|
||||
<Button
|
||||
variant='outline'
|
||||
color='success'
|
||||
className='w-full sm:w-fit'
|
||||
onClick={() => {
|
||||
router.push(
|
||||
`/production/project-flock/chickin/add?projectFlockId=${initialValues?.id}`
|
||||
);
|
||||
}}
|
||||
>
|
||||
Chickin
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Card
|
||||
className={{
|
||||
body: 'text-primary',
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(formik.values)}
|
||||
</Card>
|
||||
<Card
|
||||
className={{
|
||||
body: 'text-success',
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(formik.initialValues)}
|
||||
</Card>
|
||||
<Card
|
||||
className={{
|
||||
body: 'text-error',
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(formik.errors)}
|
||||
</Card>
|
||||
<form
|
||||
className='w-auto h-auto'
|
||||
onSubmit={formik.handleSubmit}
|
||||
@@ -634,6 +732,10 @@ const ProjectFlockForm = ({
|
||||
onChange={(val) => {
|
||||
optionChangeHandler(val, 'flock');
|
||||
setSelectedFlock((val as OptionType)?.value as number);
|
||||
formik.setFieldValue(
|
||||
'flock_name',
|
||||
(val as OptionType)?.label
|
||||
);
|
||||
}}
|
||||
options={optionsFlock}
|
||||
isLoading={isLoadingFlocks}
|
||||
@@ -650,7 +752,11 @@ const ProjectFlockForm = ({
|
||||
label='Lokasi'
|
||||
value={formik.values.location as OptionType}
|
||||
onChange={locationChangeHandler}
|
||||
options={optionsLocation}
|
||||
options={
|
||||
selectedArea != '' || initialValues?.area?.id
|
||||
? optionsLocation
|
||||
: []
|
||||
}
|
||||
isLoading={isLoadingLocations}
|
||||
isError={
|
||||
formik.touched.location_id &&
|
||||
@@ -695,7 +801,7 @@ const ProjectFlockForm = ({
|
||||
name='period'
|
||||
label='Periode'
|
||||
placeholder='Masukkan periode yang project'
|
||||
value={formik.values.period as number}
|
||||
value={formik.values.period ?? (1 as number)}
|
||||
onChange={formik.handleChange}
|
||||
isError={
|
||||
formik.touched.period && Boolean(formik.errors.period)
|
||||
@@ -743,6 +849,7 @@ const ProjectFlockForm = ({
|
||||
setRowSelection={setRowSelection}
|
||||
selectedIds={formik.values.kandang_ids}
|
||||
formType={formType}
|
||||
initialValues={initialValues?.kandangs ?? []}
|
||||
/>
|
||||
</div>
|
||||
</Collapse>
|
||||
@@ -764,7 +871,10 @@ const ProjectFlockForm = ({
|
||||
type='submit'
|
||||
color='primary'
|
||||
isLoading={formik.isSubmitting}
|
||||
disabled={!formik.isValid || formik.isSubmitting}
|
||||
disabled={
|
||||
!formik.isValid || formik.isSubmitting
|
||||
// TODO: Add logic && ketika nilai kandang_ids sudah beda dari initial values
|
||||
}
|
||||
className='px-4'
|
||||
>
|
||||
Submit
|
||||
|
||||
@@ -5,148 +5,159 @@ import PillBadge from '@/components/PillBadge';
|
||||
import Table from '@/components/Table';
|
||||
import { cn } from '@/lib/helper';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { OnChangeFn } from '@tanstack/react-table';
|
||||
import { OnChangeFn, Row } from '@tanstack/react-table';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const ProjectFlockKandangTable = ({
|
||||
listKandang,
|
||||
rowSelection,
|
||||
setRowSelection,
|
||||
selectedIds,
|
||||
initialValues,
|
||||
formType = 'add',
|
||||
}: {
|
||||
listKandang: Kandang[];
|
||||
rowSelection: Record<string, boolean>;
|
||||
setRowSelection: OnChangeFn<Record<string, boolean>>;
|
||||
selectedIds: (number | undefined)[];
|
||||
initialValues?: Kandang[];
|
||||
formType: 'add' | 'edit' | 'detail';
|
||||
}) => {
|
||||
console.log('selectedIds');
|
||||
console.log(selectedIds);
|
||||
const initialKandangIdSet = useMemo(() => {
|
||||
return initialValues?.map((k) => k.id) ?? [];
|
||||
}, [initialValues]);
|
||||
const isRowEnabled = (row: Row<Kandang>) => {
|
||||
const isDisabled =
|
||||
!initialKandangIdSet.includes(row.original.id) &&
|
||||
(row.original.status == 'ACTIVE' ||
|
||||
row.original.status == 'PENGAJUAN' ||
|
||||
formType == 'detail');
|
||||
return !isDisabled;
|
||||
};
|
||||
return (
|
||||
<Table<Kandang>
|
||||
data={listKandang}
|
||||
columns={[
|
||||
{
|
||||
id: 'select',
|
||||
header: ({ table }) => {
|
||||
const allRows = table.getRowModel().rows;
|
||||
const selectableRows = allRows.filter(
|
||||
(row) =>
|
||||
row.original.status == 'NON_ACTIVE' ||
|
||||
row.original.status == 'PENGAJUAN'
|
||||
);
|
||||
<>
|
||||
{JSON.stringify(initialKandangIdSet)}
|
||||
<Table<Kandang>
|
||||
data={listKandang}
|
||||
columns={[
|
||||
{
|
||||
id: 'select',
|
||||
header: ({ table }) => {
|
||||
const allRows = table.getRowModel().rows;
|
||||
// 1. Filter semua baris dengan logika yang sama persis seperti di cell
|
||||
const selectableRows = allRows.filter(isRowEnabled);
|
||||
|
||||
const allSelected =
|
||||
selectableRows.every((row) => row.getIsSelected()) &&
|
||||
selectableRows.length != 0 &&
|
||||
formType != 'detail';
|
||||
// 2. Cek apakah SEMUA baris yang BISA DIPILIH sudah terpilih
|
||||
const allSelected =
|
||||
selectableRows.length > 0 &&
|
||||
selectableRows.every((row) => row.getIsSelected());
|
||||
|
||||
const someSelected =
|
||||
selectableRows.some((row) => row.getIsSelected()) &&
|
||||
!allSelected &&
|
||||
formType != 'detail';
|
||||
// 3. Cek apakah BEBERAPA baris yang BISA DIPILIH sudah terpilih
|
||||
const someSelected =
|
||||
selectableRows.some((row) => row.getIsSelected()) &&
|
||||
!allSelected;
|
||||
|
||||
const toggleSelectableRows = () => {
|
||||
const shouldSelect = !allSelected;
|
||||
selectableRows.forEach((row) => row.toggleSelected(shouldSelect));
|
||||
};
|
||||
// 4. Fungsi toggle HANYA akan mentoggle baris yang BISA DIPILIH
|
||||
const toggleSelectableRows = () => {
|
||||
const shouldSelect = !allSelected;
|
||||
selectableRows.forEach((row) =>
|
||||
row.toggleSelected(shouldSelect)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center'>
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center'>
|
||||
<CheckboxInput
|
||||
name='allRow'
|
||||
checked={allSelected}
|
||||
indeterminate={someSelected}
|
||||
onChange={toggleSelectableRows}
|
||||
disabled={
|
||||
selectableRows.length === 0 || formType == 'detail'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<CheckboxInput
|
||||
name='allRow'
|
||||
checked={allSelected}
|
||||
indeterminate={someSelected}
|
||||
onChange={toggleSelectableRows}
|
||||
name='row'
|
||||
checked={
|
||||
(row.getIsSelected() &&
|
||||
(row.original.status == 'NON_ACTIVE' ||
|
||||
row.original.status == 'PENGAJUAN')) ||
|
||||
(selectedIds && selectedIds.includes(row.original.id))
|
||||
}
|
||||
disabled={
|
||||
listKandang.filter(
|
||||
(kandang) =>
|
||||
kandang.status == 'NON_ACTIVE' ||
|
||||
kandang.status == 'PENGAJUAN'
|
||||
).length == 0 || formType == 'detail'
|
||||
formType == 'detail' ||
|
||||
(!initialKandangIdSet.includes(row.original.id) &&
|
||||
(row.original.status == 'ACTIVE' ||
|
||||
row.original.status == 'PENGAJUAN'))
|
||||
}
|
||||
indeterminate={row.getIsSomeSelected()}
|
||||
onChange={row.getToggleSelectedHandler()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
},
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<CheckboxInput
|
||||
name='row'
|
||||
checked={
|
||||
(row.getIsSelected() &&
|
||||
(row.original.status == 'NON_ACTIVE' ||
|
||||
row.original.status == 'PENGAJUAN')) ||
|
||||
selectedIds.includes(row.original.id)
|
||||
}
|
||||
disabled={
|
||||
!row.getCanSelect() ||
|
||||
(row.original.status != 'NON_ACTIVE' &&
|
||||
row.original.status != 'PENGAJUAN') ||
|
||||
formType == 'detail'
|
||||
}
|
||||
indeterminate={row.getIsSomeSelected()}
|
||||
onChange={row.getToggleSelectedHandler()}
|
||||
/>
|
||||
);
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
header: 'Kandang',
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
header: 'Kandang',
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.status,
|
||||
header: 'Status',
|
||||
cell: (props) => {
|
||||
return (
|
||||
<PillBadge
|
||||
color={(() => {
|
||||
switch (props.row.original.status) {
|
||||
case 'ACTIVE':
|
||||
return 'red';
|
||||
case 'PENGAJUAN':
|
||||
return 'green';
|
||||
case 'NON_ACTIVE':
|
||||
return 'blue';
|
||||
default:
|
||||
return 'gray';
|
||||
}
|
||||
})()}
|
||||
content={props.row.original.status
|
||||
.toLowerCase()
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase())}
|
||||
/>
|
||||
);
|
||||
{
|
||||
accessorFn: (row) => row.status,
|
||||
header: 'Status',
|
||||
cell: (props) => {
|
||||
return (
|
||||
<PillBadge
|
||||
color={(() => {
|
||||
switch (props.row.original.status) {
|
||||
case 'ACTIVE':
|
||||
return 'red';
|
||||
case 'PENGAJUAN':
|
||||
return 'green';
|
||||
case 'NON_ACTIVE':
|
||||
return 'blue';
|
||||
default:
|
||||
return 'gray';
|
||||
}
|
||||
})()}
|
||||
content={props.row.original.status
|
||||
.toLowerCase()
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase())}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.capacity,
|
||||
header: 'Kapasitas',
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.pic?.name,
|
||||
header: 'Penanggung Jawab',
|
||||
},
|
||||
]}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'mb-20': listKandang?.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',
|
||||
paginationClassName: 'hidden',
|
||||
}}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
/>
|
||||
{
|
||||
accessorFn: (row) => row.capacity,
|
||||
header: 'Kapasitas',
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.pic?.name,
|
||||
header: 'Penanggung Jawab',
|
||||
},
|
||||
]}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'mb-20': listKandang?.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',
|
||||
paginationClassName: 'hidden',
|
||||
}}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
UpdateRecordingFormSchema,
|
||||
} from './RecordingForm.schema';
|
||||
import { useRecordingFormHandlers } from './useRecordingFormHandlers';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { RECORDING_FLAG_OPTIONS } from '@/config/constant';
|
||||
import useSWR from 'swr';
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { BaseApiService } from '@/services/api/base';
|
||||
import {
|
||||
CreateProjectFlockPayload,
|
||||
ProjectFlock,
|
||||
UpdateProjectFlockPayload,
|
||||
} from '@/types/api/production/project-flock';
|
||||
import {
|
||||
CreateRecordingPayload,
|
||||
Recording,
|
||||
@@ -11,11 +6,6 @@ import {
|
||||
} from '@/types/api/production/recording';
|
||||
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
|
||||
|
||||
export const ProjectFlockApi = new BaseApiService<
|
||||
ProjectFlock,
|
||||
CreateProjectFlockPayload,
|
||||
UpdateProjectFlockPayload
|
||||
>('/production/project-flocks');
|
||||
export const ProjectFlockKandangApi = new BaseApiService<
|
||||
ProjectFlockKandang,
|
||||
unknown,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
Chickin,
|
||||
CreateChickinPayload,
|
||||
UpdateChickinPayload,
|
||||
} from '@/types/api/production/chickin';
|
||||
import { BaseApiService } from '../base';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { httpClient } from '@/services/http/client';
|
||||
|
||||
export class ChickinService extends BaseApiService<
|
||||
Chickin,
|
||||
CreateChickinPayload,
|
||||
UpdateChickinPayload
|
||||
> {
|
||||
constructor(basePath: string = '/production/chickins') {
|
||||
super(basePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve single marketing data
|
||||
*/
|
||||
async singleApproval(
|
||||
id: number,
|
||||
action: 'APPROVED' | 'REJECTED'
|
||||
): Promise<BaseApiResponse<{ message: string }> | undefined> {
|
||||
try {
|
||||
const path = `${this.basePath}/approvals`;
|
||||
return await httpClient<BaseApiResponse<{ message: string }>>(path, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
action: action,
|
||||
approvable_ids: [id],
|
||||
notes: `${action} chickin ${id}`,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error approve chickin:', error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const ChickinApi = new ChickinService('/production/chickins');
|
||||
Reference in New Issue
Block a user