mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 05:22:02 +00:00
fix(merge): resolve merge conflict
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from "@/components/helper/SuspenseHelper"
|
||||
|
||||
const Layout = ({
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,249 @@
|
||||
'use client';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
import ChickinForm from '@/components/pages/production/chickin/form/ChickinForm';
|
||||
import Table from '@/components/Table';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { cn } from '@/lib/helper';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import useSWR from 'swr';
|
||||
|
||||
const AddChickin = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const projectFlockId = searchParams.get('projectFlockId');
|
||||
|
||||
// Tables Props
|
||||
const { state: tableFilterState } = useTableFilter({
|
||||
initial: { search: '' },
|
||||
paramMap: { page: 'page', pageSize: 'limit' },
|
||||
});
|
||||
|
||||
// States
|
||||
const [selectedKandang, setSelectedKandang] = useState<Kandang | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [searchProjectFlock, setSearchProjectFlock] = useState('');
|
||||
|
||||
// Fetch Data
|
||||
const { data: projectFlock, isLoading: isLoadingProjectFlock } = useSWR(
|
||||
projectFlockId,
|
||||
(id: number) => ProjectFlockApi.getSingle(id)
|
||||
);
|
||||
const { data: listProjectFlock, isLoading: isLoadingListProjectFlock } =
|
||||
useSWR(`${ProjectFlockApi.basePath}?${new URLSearchParams({
|
||||
search: searchProjectFlock,
|
||||
}).toString()}`, ProjectFlockApi.getAllFetcher);
|
||||
|
||||
const getProjectFlockKandangUrl = `/kandangs/lookup`;
|
||||
const {
|
||||
data: projectFlockKandang,
|
||||
isLoading: isLoadingProjectFlockKandang,
|
||||
mutate: refreshProjectFlockKandang,
|
||||
} = useSWR(getProjectFlockKandangUrl, () =>
|
||||
ProjectFlockApi.customRequest<BaseApiResponse<ProjectFlockKandang>, 'GET'>(
|
||||
getProjectFlockKandangUrl,
|
||||
{
|
||||
method: 'GET',
|
||||
params: {
|
||||
project_flock_id: projectFlockId ?? 0,
|
||||
kandang_id: selectedKandang?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Mapping Options
|
||||
const options = isResponseSuccess(listProjectFlock)
|
||||
? listProjectFlock?.data.map((projectFlock) => {
|
||||
return {
|
||||
value: projectFlock.id,
|
||||
label: `${projectFlock?.flock.name} - ${projectFlock?.category} - Periode ${projectFlock.period}` ,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
const chickinModal = useModal();
|
||||
|
||||
// Use Effect
|
||||
useEffect(() => {
|
||||
refreshProjectFlockKandang();
|
||||
}, [selectedKandang, refreshProjectFlockKandang]);
|
||||
|
||||
if (!projectFlockId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!isLoadingProjectFlock &&
|
||||
(!projectFlock || isResponseError(projectFlock))
|
||||
) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle Function
|
||||
const handleChickinClick = (kandang: Kandang) => {
|
||||
setSelectedKandang(kandang);
|
||||
refreshProjectFlockKandang();
|
||||
chickinModal.openModal();
|
||||
};
|
||||
const handleAfterSubmit = () => {
|
||||
refreshProjectFlockKandang();
|
||||
chickinModal.closeModal();
|
||||
router.push('/production/chickin');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isResponseSuccess(projectFlock) && (
|
||||
<>
|
||||
<section className='w-full p-4'>
|
||||
<header className='flex flex-col gap-4'>
|
||||
<Button
|
||||
href='/production/project-flock'
|
||||
variant='link'
|
||||
className='w-fit p-0 text-primary'
|
||||
>
|
||||
<Icon icon='uil:arrow-left' width={24} height={24} />
|
||||
Kembali
|
||||
</Button>
|
||||
|
||||
<div className='flex flex-col gap-4 w-full my-4'>
|
||||
<div className='max-w-1/4'>
|
||||
<SelectInput
|
||||
required
|
||||
isSearchable
|
||||
label='Project Flock'
|
||||
options={options}
|
||||
isLoading={isLoadingListProjectFlock}
|
||||
value={{
|
||||
label: `${projectFlock.data.flock.name} - ${projectFlock.data.category} - Periode ${projectFlock.data.period}`,
|
||||
value: projectFlock.data.id,
|
||||
}}
|
||||
onChange={(val) =>
|
||||
router.push(
|
||||
`/production/chickin/add?projectFlockId=${
|
||||
(val as OptionType | null)?.value
|
||||
}`
|
||||
)
|
||||
}
|
||||
onInputChange={(val) => {
|
||||
setSearchProjectFlock(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<Table<Kandang>
|
||||
data={projectFlock.data.kandangs}
|
||||
columns={[
|
||||
{
|
||||
header: '#',
|
||||
cell: (props) =>
|
||||
tableFilterState.pageSize * (tableFilterState.page - 1) +
|
||||
props.row.index +
|
||||
1,
|
||||
},
|
||||
{
|
||||
accessorKey: 'name',
|
||||
header: 'Nama Kandang',
|
||||
},
|
||||
{
|
||||
header: 'Aksi',
|
||||
cell: (props) => {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
color='success'
|
||||
variant='outline'
|
||||
isLoading={isLoadingProjectFlockKandang}
|
||||
onClick={() => {
|
||||
handleChickinClick(props.row.original);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon='mdi:home-import-outline'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
Chickin
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
page={undefined}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'mb-20':
|
||||
isResponseSuccess(projectFlock) &&
|
||||
projectFlock.data.kandangs?.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',
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
<Modal ref={chickinModal.ref}>
|
||||
<div className='flex flex-row justify-between items-center'>
|
||||
<h1 className='text-xl font-semibold text-center mb-6'>
|
||||
Chickin Kandang - {selectedKandang?.name}
|
||||
</h1>
|
||||
<Button
|
||||
color='error'
|
||||
variant='link'
|
||||
onClick={chickinModal.closeModal}
|
||||
>
|
||||
<Icon
|
||||
className='text-black'
|
||||
icon='uil:times'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
{isResponseSuccess(projectFlockKandang) &&
|
||||
!isLoadingProjectFlockKandang && (
|
||||
<ChickinForm
|
||||
initialValues={{
|
||||
project_flock_kandang: projectFlockKandang.data,
|
||||
created_user: projectFlock.data.created_user,
|
||||
created_at: projectFlock.data.created_at,
|
||||
updated_at: projectFlock.data.updated_at,
|
||||
}}
|
||||
afterSubmit={handleAfterSubmit}
|
||||
/>
|
||||
)}
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddChickin;
|
||||
@@ -0,0 +1,10 @@
|
||||
import ChickinTable from "@/components/pages/production/chickin/ChickinTable";
|
||||
|
||||
const Chickin = () => {
|
||||
return (
|
||||
<section className="w-full p-4">
|
||||
<ChickinTable/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
export default Chickin;
|
||||
@@ -0,0 +1,137 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
ChangeEventHandler,
|
||||
FocusEventHandler,
|
||||
ReactNode,
|
||||
} from 'react';
|
||||
|
||||
import { cn } from '@/lib/helper';
|
||||
|
||||
export interface DateInputProps {
|
||||
label?: string;
|
||||
bottomLabel?: string;
|
||||
name: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
min?: string;
|
||||
max?: string;
|
||||
className?: {
|
||||
wrapper?: string;
|
||||
label?: string;
|
||||
inputWrapper?: string;
|
||||
input?: string;
|
||||
};
|
||||
isError?: boolean;
|
||||
isValid?: boolean;
|
||||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
required?: boolean;
|
||||
isLoading?: boolean;
|
||||
errorMessage?: string;
|
||||
startAdornment?: ReactNode;
|
||||
endAdornment?: ReactNode;
|
||||
onChange?: ChangeEventHandler<HTMLInputElement>;
|
||||
onBlur?: FocusEventHandler<HTMLInputElement>;
|
||||
}
|
||||
|
||||
const DateInput = ({
|
||||
label,
|
||||
bottomLabel,
|
||||
name,
|
||||
value,
|
||||
placeholder,
|
||||
min,
|
||||
max,
|
||||
className,
|
||||
isError,
|
||||
isValid,
|
||||
errorMessage,
|
||||
startAdornment,
|
||||
endAdornment,
|
||||
disabled = false,
|
||||
required = false,
|
||||
onChange,
|
||||
onBlur,
|
||||
readOnly = false,
|
||||
isLoading = false,
|
||||
}: DateInputProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'w-full flex flex-col gap-2 text-start',
|
||||
className?.wrapper
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label
|
||||
htmlFor={name}
|
||||
className={cn(
|
||||
'w-full text-sm font-normal leading-5',
|
||||
{
|
||||
'text-error': isError,
|
||||
},
|
||||
className?.label
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
{required && (
|
||||
<>
|
||||
{' '}
|
||||
<span className='tooltip tooltip-error' data-tip='required'>
|
||||
<span className='text-error'>*</span>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</label>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'input h-12 px-4 py-2 text-base font-normal leading-6 w-full rounded-lg! outline-none! transition-all duration-200 flex items-center',
|
||||
{
|
||||
'border-error': isError,
|
||||
'border-success!': isValid,
|
||||
},
|
||||
className?.inputWrapper
|
||||
)}
|
||||
>
|
||||
{startAdornment && startAdornment}
|
||||
|
||||
<input
|
||||
type='date'
|
||||
id={name}
|
||||
name={name}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
min={min}
|
||||
max={max}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'grow bg-transparent cursor-pointer',
|
||||
className?.input
|
||||
)}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
|
||||
{(isLoading || endAdornment) && (
|
||||
<div className='flex flex-row gap-2'>
|
||||
{isLoading && <span className='loading loading-spinner' />}
|
||||
{endAdornment && endAdornment}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isError && bottomLabel && (
|
||||
<p className='w-full text-sm opacity-60'>{bottomLabel}</p>
|
||||
)}
|
||||
{isError && errorMessage && (
|
||||
<p className='w-full text-sm text-error'>{errorMessage}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DateInput;
|
||||
@@ -0,0 +1,325 @@
|
||||
'use client';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||
import { OptionType } from '@/components/input/SelectInput';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import Table from '@/components/Table';
|
||||
import RowCollapseOptions from '@/components/table/RowCollapseOptions';
|
||||
import RowDropdownOptions from '@/components/table/RowDropdownOptions';
|
||||
import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector';
|
||||
import { ROWS_OPTIONS } from '@/config/constant';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { cn } from '@/lib/helper';
|
||||
import { ChickinApi, ProjectFlockApi } from '@/services/api/production';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { Chickin } from '@/types/api/production/chickin';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { CellContext, SortingState } from '@tanstack/react-table';
|
||||
import { useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import ChickinForm from './form/ChickinForm';
|
||||
|
||||
const ChickinTable = () => {
|
||||
const {
|
||||
state: tableFilterState,
|
||||
updateFilter,
|
||||
setPage,
|
||||
setPageSize,
|
||||
toQueryString: getTableFilterQueryString,
|
||||
} = useTableFilter({
|
||||
initial: {
|
||||
search: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
pageSize: 'limit',
|
||||
search: 'search',
|
||||
},
|
||||
});
|
||||
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [selectedChickin, setSelectedChickin] = useState<Chickin | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
|
||||
const deleteModal = useModal();
|
||||
const chickinModal = useModal();
|
||||
|
||||
// Data Fetching
|
||||
const {
|
||||
data: chickins,
|
||||
isLoading,
|
||||
mutate: refreshChickins,
|
||||
} = useSWR(
|
||||
`${ChickinApi.basePath}${getTableFilterQueryString()}`,
|
||||
ChickinApi.getAllFetcher
|
||||
);
|
||||
const {
|
||||
data: projectFlocks,
|
||||
isLoading: isLoadingProjectFlocks,
|
||||
} = useSWR(
|
||||
`${ProjectFlockApi.basePath}${getTableFilterQueryString()}`,
|
||||
ProjectFlockApi.getAllFetcher
|
||||
);
|
||||
|
||||
const searchChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateFilter('search', event.target.value);
|
||||
setPage(1);
|
||||
};
|
||||
|
||||
const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
const newVal = val as OptionType;
|
||||
setPageSize(newVal.value as number);
|
||||
setPage(1);
|
||||
};
|
||||
|
||||
const confirmationModalDeleteClickHandler = async () => {
|
||||
setIsDeleteLoading(true);
|
||||
try {
|
||||
await ChickinApi.delete(selectedChickin?.id as number);
|
||||
refreshChickins();
|
||||
deleteModal.closeModal();
|
||||
} finally {
|
||||
setIsDeleteLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='flex flex-col gap-2 mb-4'>
|
||||
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
|
||||
<Button
|
||||
href='/production/chickin/add?projectFlockId=1'
|
||||
color='primary'
|
||||
>
|
||||
<Icon icon='uil:plus' width={24} height={24} />
|
||||
Tambah
|
||||
</Button>
|
||||
<DebouncedTextInput
|
||||
name='search'
|
||||
placeholder='Cari Chickin'
|
||||
value={tableFilterState.search}
|
||||
onChange={searchChangeHandler}
|
||||
className={{ wrapper: 'sm:max-w-3xs' }}
|
||||
/>
|
||||
</div>
|
||||
<TableRowSizeSelector
|
||||
value={tableFilterState.pageSize}
|
||||
onChange={pageSizeChangeHandler}
|
||||
options={ROWS_OPTIONS}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Table<Chickin>
|
||||
data={isResponseSuccess(chickins) ? chickins?.data : []}
|
||||
columns={[
|
||||
{
|
||||
header: '#',
|
||||
cell: (props) =>
|
||||
tableFilterState.pageSize * (tableFilterState.page - 1) +
|
||||
props.row.index +
|
||||
1,
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.project_flock_kandang?.kandang.name,
|
||||
header: 'Kandang',
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.quantity,
|
||||
header: 'Jumlah Chickin',
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.chick_in_date,
|
||||
header: 'Tanggal Chickin',
|
||||
cell: (props) => {
|
||||
if (props.row.original.chick_in_date) {
|
||||
return new Date(props.row.original.chick_in_date).toLocaleDateString(
|
||||
'id-ID'
|
||||
);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.note,
|
||||
header: 'Catatan',
|
||||
},
|
||||
{
|
||||
header: 'Aksi',
|
||||
cell: (props) => {
|
||||
const currentPageSize =
|
||||
props.table.getPaginationRowModel().rows.length;
|
||||
const currentPageRows =
|
||||
props.table.getPaginationRowModel().flatRows;
|
||||
const currentRowRelativeIndex =
|
||||
currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
|
||||
|
||||
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2;
|
||||
|
||||
const deleteClickHandler = () => {
|
||||
setSelectedChickin(props.row.original);
|
||||
deleteModal.openModal();
|
||||
};
|
||||
|
||||
const editClickHandler = () => {
|
||||
setSelectedChickin(props.row.original);
|
||||
chickinModal.openModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{currentPageSize > 2 && (
|
||||
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
||||
<RowOptionsMenu
|
||||
type='dropdown'
|
||||
props={props}
|
||||
deleteClickHandler={deleteClickHandler}
|
||||
editClickHandler={editClickHandler}
|
||||
/>
|
||||
</RowDropdownOptions>
|
||||
)}
|
||||
|
||||
{currentPageSize <= 2 && (
|
||||
<RowCollapseOptions>
|
||||
<RowOptionsMenu
|
||||
type='collapse'
|
||||
props={props}
|
||||
deleteClickHandler={deleteClickHandler}
|
||||
editClickHandler={editClickHandler}
|
||||
/>
|
||||
</RowCollapseOptions>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
page={isResponseSuccess(chickins) ? chickins?.meta?.page : 0}
|
||||
totalItems={
|
||||
isResponseSuccess(chickins) ? chickins?.meta?.total_results : 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoading}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'mb-20':
|
||||
isResponseSuccess(chickins) && chickins?.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',
|
||||
}}
|
||||
/>
|
||||
<ConfirmationModal
|
||||
ref={deleteModal.ref}
|
||||
type='error'
|
||||
text={`Apakah anda yakin ingin menghapus data Chickin ini?`}
|
||||
secondaryButton={{
|
||||
text: 'Tidak',
|
||||
}}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
onClick: confirmationModalDeleteClickHandler,
|
||||
isLoading: isDeleteLoading,
|
||||
color: 'error',
|
||||
}}
|
||||
/>
|
||||
<Modal ref={chickinModal.ref}>
|
||||
<div className='flex flex-row justify-between items-center'>
|
||||
<h1 className='text-xl font-semibold text-center mb-6'>
|
||||
Chickin Kandang - { selectedChickin?.project_flock_kandang && selectedChickin?.project_flock_kandang.kandang?.name}
|
||||
</h1>
|
||||
<Button
|
||||
color='error'
|
||||
variant='link'
|
||||
onClick={chickinModal.closeModal}
|
||||
>
|
||||
<Icon
|
||||
className='text-black'
|
||||
icon='uil:times'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
<ChickinForm initialValues={selectedChickin} formType='edit' afterSubmit={() => {
|
||||
refreshChickins()
|
||||
chickinModal.closeModal()
|
||||
}}/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const RowOptionsMenu = ({
|
||||
type = 'dropdown',
|
||||
props,
|
||||
editClickHandler,
|
||||
deleteClickHandler,
|
||||
}: {
|
||||
type: 'dropdown' | 'collapse';
|
||||
props: CellContext<Chickin, unknown>;
|
||||
editClickHandler: () => void;
|
||||
deleteClickHandler: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
tabIndex={type == 'dropdown' ? 0 : undefined}
|
||||
className={cn(
|
||||
{
|
||||
'dropdown-content': type === 'dropdown',
|
||||
'mt-2': type === 'collapse',
|
||||
},
|
||||
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
href={`/production/chickin/detail?projectFlockId=${props.row.original.id}`}
|
||||
variant='ghost'
|
||||
color='primary'
|
||||
className='justify-start text-sm'
|
||||
>
|
||||
<Icon icon='mdi:eye-outline' width={16} height={16} />
|
||||
Detail
|
||||
</Button>
|
||||
<Button
|
||||
variant='ghost'
|
||||
color='warning'
|
||||
className='justify-start text-sm'
|
||||
onClick={editClickHandler}
|
||||
>
|
||||
<Icon icon='mdi:pencil-outline' width={16} height={16} />
|
||||
Edit
|
||||
</Button>
|
||||
<Button
|
||||
onClick={deleteClickHandler}
|
||||
variant='ghost'
|
||||
color='error'
|
||||
className='text-error hover:text-inherit'
|
||||
>
|
||||
<Icon
|
||||
icon='material-symbols:delete-outline-rounded'
|
||||
width={16}
|
||||
height={16}
|
||||
className='justify-start text-sm'
|
||||
/>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChickinTable;
|
||||
@@ -0,0 +1,11 @@
|
||||
import * as Yup from 'yup';
|
||||
|
||||
export const ChickinFormSchema = Yup.object({
|
||||
chick_in_date: Yup.string().required('Tanggal masuk wajib diisi!'),
|
||||
note: Yup.string().required('Catatan wajib diisi!'),
|
||||
quantity: Yup.number().min(1, 'Jumlah wajib diisi!').required('Jumlah wajib diisi!'),
|
||||
})
|
||||
|
||||
export type ChickinFormValues = Yup.InferType<typeof ChickinFormSchema>;
|
||||
|
||||
export const UpdateChickinFormSchema = ChickinFormSchema;
|
||||
@@ -0,0 +1,206 @@
|
||||
'use client';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import {
|
||||
Chickin,
|
||||
CreateChickinPayload,
|
||||
UpdateChickinPayload,
|
||||
} from '@/types/api/production/chickin';
|
||||
import {
|
||||
ChickinFormSchema,
|
||||
ChickinFormValues,
|
||||
UpdateChickinFormSchema,
|
||||
} from '@/components/pages/production/chickin/form/ChickinForm.schema';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import { ChickinApi } from '@/services/api/production';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import { isResponseError } from '@/lib/api-helper';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Icon } from '@iconify/react';
|
||||
import TextArea from '@/components/input/TextArea';
|
||||
import TextInput from '@/components/input/TextInput';
|
||||
|
||||
interface ChickinFormProps {
|
||||
formType?: 'add' | 'detail' | 'edit';
|
||||
initialValues?: Chickin;
|
||||
afterSubmit?: () => void;
|
||||
}
|
||||
|
||||
const ChickinForm = ({
|
||||
formType = 'add',
|
||||
initialValues,
|
||||
afterSubmit,
|
||||
}: ChickinFormProps) => {
|
||||
// Helper Function
|
||||
const formatDateForInput = (dateString?: string): string => {
|
||||
if (!dateString) return '';
|
||||
return new Date(dateString).toISOString().split('T')[0];
|
||||
};
|
||||
|
||||
// State
|
||||
const [chickinFormErrorMessage, setChickinFormErrorMessage] = useState('');
|
||||
|
||||
// Initial Value
|
||||
const formikInitialValue = useMemo<ChickinFormValues>(() => {
|
||||
return {
|
||||
chick_in_date: formatDateForInput(initialValues?.chick_in_date) ?? '',
|
||||
note: initialValues?.note ?? `Catatan Chickin ${initialValues?.project_flock_kandang?.project_flock.flock.name}`,
|
||||
quantity: initialValues?.quantity ?? 1,
|
||||
};
|
||||
}, [initialValues]);
|
||||
|
||||
// Handle Submit Function
|
||||
const handleCreate = useCallback(
|
||||
async (
|
||||
payload: CreateChickinPayload,
|
||||
afterSubmit: (() => void) | undefined
|
||||
) => {
|
||||
const res = await ChickinApi.create(payload);
|
||||
if (isResponseError(res)) {
|
||||
setChickinFormErrorMessage(res.message);
|
||||
return;
|
||||
}
|
||||
toast.success(res?.message as string);
|
||||
afterSubmit?.();
|
||||
},
|
||||
[]
|
||||
);
|
||||
const handleUpdate = useCallback(
|
||||
async (
|
||||
payload: UpdateChickinPayload,
|
||||
afterSubmit: (() => void) | undefined
|
||||
) => {
|
||||
const res = await ChickinApi.update(
|
||||
payload.project_flock_kandang_id as number,
|
||||
payload
|
||||
);
|
||||
if (isResponseError(res)) {
|
||||
setChickinFormErrorMessage(res.message);
|
||||
return;
|
||||
}
|
||||
toast.success(res?.message as string);
|
||||
afterSubmit?.();
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// Formik
|
||||
const formik = useFormik<ChickinFormValues>({
|
||||
initialValues: formikInitialValue,
|
||||
enableReinitialize: true,
|
||||
validationSchema:
|
||||
formType === 'edit' ? UpdateChickinFormSchema : ChickinFormSchema,
|
||||
onSubmit: async (values) => {
|
||||
// reset error message
|
||||
setChickinFormErrorMessage('');
|
||||
|
||||
if (initialValues?.project_flock_kandang?.id == undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create payload
|
||||
const payload = {
|
||||
chick_in_date: values.chick_in_date,
|
||||
project_flock_kandang_id: initialValues?.project_flock_kandang?.id,
|
||||
};
|
||||
|
||||
// cek type form yang disubmit
|
||||
switch (formType) {
|
||||
case 'add':
|
||||
handleCreate(payload, afterSubmit);
|
||||
break;
|
||||
case 'edit':
|
||||
handleUpdate(payload, afterSubmit);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Initialize Formik
|
||||
const { setValues: formikSetValues } = formik;
|
||||
useEffect(() => {
|
||||
formikSetValues(formikInitialValue);
|
||||
}, [formikSetValues, formikInitialValue]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<form
|
||||
className='min-h-48 flex flex-col gap-4'
|
||||
onSubmit={formik.handleSubmit}
|
||||
onReset={formik.handleReset}
|
||||
>
|
||||
<DateInput
|
||||
value={formik.values.chick_in_date}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
name='chick_in_date'
|
||||
label='Tanggal Chickin'
|
||||
required
|
||||
isError={
|
||||
formik.touched.chick_in_date && Boolean(formik.errors.chick_in_date)
|
||||
}
|
||||
errorMessage={formik.errors.chick_in_date}
|
||||
/>
|
||||
<TextInput
|
||||
value={formik.values.quantity}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
name='quantity'
|
||||
label='Jumlah Chickin'
|
||||
required
|
||||
isError={formik.touched.quantity && Boolean(formik.errors.quantity)}
|
||||
errorMessage={formik.errors.quantity}
|
||||
type='number'
|
||||
disabled
|
||||
/>
|
||||
<TextArea
|
||||
required
|
||||
label='Catatan'
|
||||
name='note'
|
||||
value={formik.values.note}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={formik.touched.note && Boolean(formik.errors.note)}
|
||||
errorMessage={formik.errors.note}
|
||||
|
||||
/>
|
||||
{initialValues?.project_flock_kandang?.id == undefined && (
|
||||
<p className='text-error'>Project Flock Kandang tidak ditemukan.</p>
|
||||
)}
|
||||
{chickinFormErrorMessage && (
|
||||
<div
|
||||
role='alert'
|
||||
className='alert alert-error'
|
||||
onClick={() => {
|
||||
setChickinFormErrorMessage('');
|
||||
}}
|
||||
>
|
||||
<Icon icon='mdi:times' />
|
||||
<span>{chickinFormErrorMessage}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className='flex justify-center mt-auto gap-2'>
|
||||
<Button color='warning' type='reset'>
|
||||
Reset
|
||||
</Button>
|
||||
<Button
|
||||
type='submit'
|
||||
isLoading={formik.isSubmitting}
|
||||
disabled={
|
||||
!formik.isValid ||
|
||||
formik.isSubmitting ||
|
||||
!initialValues?.project_flock_kandang?.id
|
||||
}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChickinForm;
|
||||
@@ -56,6 +56,15 @@ const RowOptionsMenu = ({
|
||||
<Icon icon='mdi:eye-outline' width={16} height={16} />
|
||||
Detail
|
||||
</Button>
|
||||
<Button
|
||||
href={`/production/chickin/add?projectFlockId=${props.row.original.id}`}
|
||||
variant='ghost'
|
||||
color='success'
|
||||
className='justify-start text-sm'
|
||||
>
|
||||
<Icon icon='mdi:home-import-outline' width={16} height={16} />
|
||||
Chickin
|
||||
</Button>
|
||||
{/* <Button
|
||||
href={`/production/project-flock/detail/edit?projectFlockId=${props.row.original.id}`}
|
||||
variant='ghost'
|
||||
|
||||
@@ -24,7 +24,7 @@ export const MAIN_DRAWER_LINKS: MAIN_DRAWER_MENU[] = [
|
||||
},
|
||||
{
|
||||
title: 'Chick In',
|
||||
link: '/production/chick-in',
|
||||
link: '/production/chickin',
|
||||
icon: 'mdi:home-import-outline',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -9,6 +9,11 @@ import {
|
||||
Recording,
|
||||
UpdateRecordingPayload,
|
||||
} from '@/types/api/production/recording';
|
||||
import {
|
||||
Chickin,
|
||||
CreateChickinPayload,
|
||||
UpdateChickinPayload,
|
||||
} from '@/types/api/production/chickin';
|
||||
|
||||
export const ProjectFlockApi = new BaseApiService<
|
||||
ProjectFlock,
|
||||
@@ -20,3 +25,8 @@ export const RecordingApi = new BaseApiService<
|
||||
CreateRecordingPayload,
|
||||
UpdateRecordingPayload
|
||||
>('/flock/recordings');
|
||||
export const ChickinApi = new BaseApiService<
|
||||
Chickin,
|
||||
CreateChickinPayload,
|
||||
UpdateChickinPayload
|
||||
>('/production/chickins');
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { BaseMetadata } from "@/types/api/api-general";
|
||||
import { ProjectFlockKandang } from "@/types/api/production/project-flock-kandang";
|
||||
|
||||
export type BaseChickin = {
|
||||
id?: number;
|
||||
chick_in_date?: string;
|
||||
quantity?: number;
|
||||
note?: string;
|
||||
project_flock_kandang?: ProjectFlockKandang;
|
||||
}
|
||||
|
||||
export type Chickin = BaseMetadata & BaseChickin;
|
||||
|
||||
export type CreateChickinPayload = {
|
||||
project_flock_kandang_id: number;
|
||||
chick_in_date: string;
|
||||
}
|
||||
|
||||
export type UpdateChickinPayload = CreateChickinPayload;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Kandang } from "@/type/master-data/kandang";
|
||||
import { ProjectFlock } from "@/types/api/production/project-flock";
|
||||
|
||||
export type BaseProjectFlockKandang = {
|
||||
id: number;
|
||||
project_flock_id: number;
|
||||
kandang_id: number;
|
||||
kandang: Kandang;
|
||||
project_flock: ProjectFlock;
|
||||
}
|
||||
|
||||
export type ProjectFlockKandang = BaseProjectFlockKandang;
|
||||
Reference in New Issue
Block a user