mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
feat(FE-314-315): API Integration project budgets and refactoring UI
This commit is contained in:
+33
-20
@@ -7,26 +7,39 @@
|
|||||||
default: false;
|
default: false;
|
||||||
prefersdark: false;
|
prefersdark: false;
|
||||||
color-scheme: 'light';
|
color-scheme: 'light';
|
||||||
--color-base-100: oklch(98% 0.001 106.423);
|
|
||||||
--color-base-200: oklch(97% 0.001 106.424);
|
/* Primary Colors */
|
||||||
--color-base-300: oklch(92% 0.003 48.717);
|
--color-primary: oklch(39.4% 0.177 301.9);
|
||||||
--color-base-content: oklch(22.389% 0.031 278.072);
|
--color-primary-content: oklch(87.5% 0.038 274.5);
|
||||||
--color-primary: oklch(60% 0.126 221.723);
|
|
||||||
--color-primary-content: oklch(100% 0 0);
|
/* Secondary Colors */
|
||||||
--color-secondary: oklch(52% 0.105 223.128);
|
--color-secondary: oklch(60.1% 0.258 335.7);
|
||||||
--color-secondary-content: oklch(100% 0 0);
|
--color-secondary-content: oklch(99.4% 0.007 337.8);
|
||||||
--color-accent: oklch(45% 0.085 224.283);
|
|
||||||
--color-accent-content: oklch(100% 0 0);
|
/* Accent Colors */
|
||||||
--color-neutral: oklch(39% 0.07 227.392);
|
--color-accent: oklch(76.2% 0.155 170.8);
|
||||||
--color-neutral-content: oklch(100% 0 0);
|
--color-accent-content: oklch(7.2% 0.007 167.6);
|
||||||
--color-info: oklch(58% 0.158 241.966);
|
|
||||||
--color-info-content: oklch(100% 0 0);
|
/* Neutral Colors */
|
||||||
--color-success: oklch(62% 0.194 149.214);
|
--color-neutral: oklch(22.4% 0.032 258.8);
|
||||||
--color-success-content: oklch(100% 0 0);
|
--color-neutral-content: oklch(87.7% 0.016 257.0);
|
||||||
--color-warning: oklch(85% 0.199 91.936);
|
|
||||||
--color-warning-content: oklch(0% 0 0);
|
/* Base Colors */
|
||||||
--color-error: oklch(57% 0.245 27.325);
|
--color-base-100: oklch(100% 0 0); /* #ffffff */
|
||||||
--color-error-content: oklch(100% 0 0);
|
--color-base-200: oklch(97.2% 0 0); /* #f2f2f2 */
|
||||||
|
--color-base-300: oklch(93.1% 0.002 249.7); /* #e5e6e6 */
|
||||||
|
--color-base-content: oklch(18.6% 0.024 257.7); /* #1f2937 */
|
||||||
|
|
||||||
|
/* Status/Utility Colors */
|
||||||
|
--color-info: oklch(67.4% 0.176 238.9);
|
||||||
|
--color-info-content: oklch(0% 0 0); /* #000000 */
|
||||||
|
--color-success: oklch(62.3% 0.147 149.0);
|
||||||
|
--color-success-content: oklch(100% 0 0); /* #ffffff */
|
||||||
|
--color-warning: oklch(82.2% 0.165 91.9);
|
||||||
|
--color-warning-content: oklch(0% 0 0); /* #000000 */
|
||||||
|
--color-error: oklch(61.8% 0.203 27.8);
|
||||||
|
--color-error-content: oklch(100% 0 0); /* #fffffff */
|
||||||
|
|
||||||
--radius-selector: 0rem;
|
--radius-selector: 0rem;
|
||||||
--radius-field: 0.25rem;
|
--radius-field: 0.25rem;
|
||||||
--radius-box: 0.25rem;
|
--radius-box: 0.25rem;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const AddProjectFlock = () => {
|
|||||||
// },
|
// },
|
||||||
// }));
|
// }));
|
||||||
return (
|
return (
|
||||||
<section className='w-full p-4 flex flex-row justify-center'>
|
<section className='w-full flex flex-row justify-center'>
|
||||||
<ProjectFlockForm formType='add' />
|
<ProjectFlockForm formType='add' />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const ProjectFlockEdit = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full p-4 flex flex-col justify-center'>
|
<div className='w-full flex flex-col justify-center'>
|
||||||
{isLoadingProjectFlock && (
|
{isLoadingProjectFlock && (
|
||||||
<span className='loading loading-spinner loading-xl' />
|
<span className='loading loading-spinner loading-xl' />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import Tooltip from '@/components/Tooltip';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ const FloatingActionsButton = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
`absolute ${positionStyles} inset-x-1/2 -translate-x-1/2 z-50`,
|
`absolute ${positionStyles} inset-x-1/2 -translate-x-1/2 z-50`,
|
||||||
'mx-auto w-full max-w-xl sm:mx-0 bg-base-300 p-4 rounded-xl shadow-md transition-all duration-300 transform',
|
'mx-auto w-full max-w-lg sm:mx-0 bg-base-300 p-4 rounded-xl shadow-md transition-all duration-300 transform',
|
||||||
'bg-slate-950 backdrop-blur-md'
|
'bg-slate-950 backdrop-blur-md'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -72,14 +73,15 @@ const FloatingActionsButton = ({
|
|||||||
key={index}
|
key={index}
|
||||||
onClick={action.onClick}
|
onClick={action.onClick}
|
||||||
className='text-white hover:text-gray-400 tooltip tooltip-bottom'
|
className='text-white hover:text-gray-400 tooltip tooltip-bottom'
|
||||||
data-tip={action.label}
|
|
||||||
>
|
>
|
||||||
<Icon
|
<Tooltip content={action.label || action.action}>
|
||||||
icon={action.icon}
|
<Icon
|
||||||
width={24}
|
icon={action.icon}
|
||||||
height={24}
|
width={20}
|
||||||
className={`text-${getActionColor(action.action)} font-thin`}
|
height={20}
|
||||||
/>
|
className={`text-${getActionColor(action.action)} font-thin`}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -91,7 +93,9 @@ const FloatingActionsButton = ({
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className='text-white hover:text-gray-400'
|
className='text-white hover:text-gray-400'
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:close' width={24} height={24} />
|
<Tooltip content='Close'>
|
||||||
|
<Icon icon='mdi:close' width={20} height={20} />
|
||||||
|
</Tooltip>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const CheckboxInput = ({
|
|||||||
id={name}
|
id={name}
|
||||||
name={name}
|
name={name}
|
||||||
className={cn(
|
className={cn(
|
||||||
'checkbox cursor-pointer',
|
'checkbox rounded-md cursor-pointer',
|
||||||
{
|
{
|
||||||
'border-error': isError,
|
'border-error': isError,
|
||||||
'border-success': isValid,
|
'border-success': isValid,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import Badge from '@/components/Badge';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import FloatingActionsButton from '@/components/FloatingActionsButton';
|
import FloatingActionsButton from '@/components/FloatingActionsButton';
|
||||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||||
@@ -13,7 +14,7 @@ import RowCollapseOptions from '@/components/table/RowCollapseOptions';
|
|||||||
import RowDropdownOptions from '@/components/table/RowDropdownOptions';
|
import RowDropdownOptions from '@/components/table/RowDropdownOptions';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn, formatDate } from '@/lib/helper';
|
||||||
import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data';
|
import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data';
|
||||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
@@ -270,7 +271,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
<div className='w-full flex flex-col justify-between items-end gap-2'>
|
<div className='w-full flex flex-col justify-between items-end gap-2'>
|
||||||
<div className='flex flex-col sm:flex-row gap-3 w-full'>
|
<div className='flex flex-col sm:flex-row gap-3 w-full'>
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
|
||||||
color='primary'
|
color='primary'
|
||||||
className='w-full sm:w-fit'
|
className='w-full sm:w-fit'
|
||||||
href='/production/project-flock/add'
|
href='/production/project-flock/add'
|
||||||
@@ -278,7 +278,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
<Icon icon='ic:round-plus' width={24} height={24} />
|
<Icon icon='ic:round-plus' width={24} height={24} />
|
||||||
Tambah
|
Tambah
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{/* <Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
color='success'
|
color='success'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -303,7 +303,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
>
|
>
|
||||||
<Icon icon='mdi:times' width={24} height={24} />
|
<Icon icon='mdi:times' width={24} height={24} />
|
||||||
Reject
|
Reject
|
||||||
</Button>
|
</Button> */}
|
||||||
<div className='ms-auto w-full sm:w-auto'>
|
<div className='ms-auto w-full sm:w-auto'>
|
||||||
<DebouncedTextInput
|
<DebouncedTextInput
|
||||||
name='search'
|
name='search'
|
||||||
@@ -395,9 +395,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
id: 'select',
|
id: 'select',
|
||||||
header: ({ table }) => {
|
header: ({ table }) => {
|
||||||
const allRows = table.getRowModel().rows;
|
const allRows = table.getRowModel().rows;
|
||||||
const selectableRows = allRows.filter(
|
const selectableRows = allRows;
|
||||||
(row) => row.original?.approval?.step_number == 1
|
|
||||||
);
|
|
||||||
|
|
||||||
const allSelected =
|
const allSelected =
|
||||||
selectableRows.every((row) => row.getIsSelected()) &&
|
selectableRows.every((row) => row.getIsSelected()) &&
|
||||||
@@ -421,12 +419,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
checked={allSelected}
|
checked={allSelected}
|
||||||
indeterminate={someSelected}
|
indeterminate={someSelected}
|
||||||
onChange={toggleSelectableRows}
|
onChange={toggleSelectableRows}
|
||||||
disabled={
|
|
||||||
isResponseSuccess(projectFlocks) &&
|
|
||||||
projectFlocks?.data?.filter(
|
|
||||||
(flock) => flock.approval.step_number == 1
|
|
||||||
).length == 0
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -435,14 +427,8 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
return (
|
return (
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
name='row'
|
name='row'
|
||||||
checked={
|
checked={row.getIsSelected()}
|
||||||
row.getIsSelected() &&
|
disabled={!row.getCanSelect()}
|
||||||
row.original.approval.step_number == 1
|
|
||||||
}
|
|
||||||
disabled={
|
|
||||||
!row.getCanSelect() ||
|
|
||||||
row.original.approval.step_number != 1
|
|
||||||
}
|
|
||||||
indeterminate={row.getIsSomeSelected()}
|
indeterminate={row.getIsSomeSelected()}
|
||||||
onChange={row.getToggleSelectedHandler()}
|
onChange={row.getToggleSelectedHandler()}
|
||||||
/>
|
/>
|
||||||
@@ -473,6 +459,40 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
{
|
{
|
||||||
accessorKey: 'approval.step_name',
|
accessorKey: 'approval.step_name',
|
||||||
header: 'Status',
|
header: 'Status',
|
||||||
|
cell: (props) => {
|
||||||
|
const approval = props.row.original.approval;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
variant='soft'
|
||||||
|
className={{
|
||||||
|
badge:
|
||||||
|
'rounded-lg px-2 w-full flex flex-row justify-start',
|
||||||
|
}}
|
||||||
|
color={
|
||||||
|
approval.step_number == 1
|
||||||
|
? 'neutral'
|
||||||
|
: approval.step_number == 2
|
||||||
|
? 'success'
|
||||||
|
: 'error'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon='mdi:circle'
|
||||||
|
width={12}
|
||||||
|
height={12}
|
||||||
|
color={
|
||||||
|
approval.step_number == 1
|
||||||
|
? 'neutral'
|
||||||
|
: approval.step_number == 2
|
||||||
|
? 'success'
|
||||||
|
: 'error'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{approval.step_name}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Kandang',
|
header: 'Kandang',
|
||||||
@@ -500,51 +520,51 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
accessorKey: 'created_at',
|
accessorKey: 'created_at',
|
||||||
header: 'Dibuat pada',
|
header: 'Dibuat pada',
|
||||||
cell: (props) =>
|
cell: (props) =>
|
||||||
new Date(props.row.original.created_at).toLocaleDateString(),
|
formatDate(props.row.original.created_at, 'MMM DD, YYYY'),
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
header: 'Aksi',
|
// header: 'Aksi',
|
||||||
cell: (props) => {
|
// cell: (props) => {
|
||||||
const currentPageSize =
|
// const currentPageSize =
|
||||||
props.table.getPaginationRowModel().rows.length;
|
// props.table.getPaginationRowModel().rows.length;
|
||||||
const currentPageRows =
|
// const currentPageRows =
|
||||||
props.table.getPaginationRowModel().flatRows;
|
// props.table.getPaginationRowModel().flatRows;
|
||||||
const currentRowRelativeIndex =
|
// const currentRowRelativeIndex =
|
||||||
currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
|
// currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
|
||||||
|
|
||||||
const isLast2Rows =
|
// const isLast2Rows =
|
||||||
currentRowRelativeIndex > currentPageSize - 2;
|
// currentRowRelativeIndex > currentPageSize - 2;
|
||||||
|
|
||||||
const deleteClickHandler = () => {
|
// const deleteClickHandler = () => {
|
||||||
setSelectedProjectFlock(props.row.original);
|
// setSelectedProjectFlock(props.row.original);
|
||||||
deleteModal.openModal();
|
// deleteModal.openModal();
|
||||||
};
|
// };
|
||||||
|
|
||||||
return (
|
// return (
|
||||||
<>
|
// <>
|
||||||
{currentPageSize > 2 && (
|
// {currentPageSize > 2 && (
|
||||||
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
// <RowDropdownOptions isLast2Rows={isLast2Rows}>
|
||||||
<RowOptionsMenu
|
// <RowOptionsMenu
|
||||||
type='dropdown'
|
// type='dropdown'
|
||||||
props={props}
|
// props={props}
|
||||||
deleteClickHandler={deleteClickHandler}
|
// deleteClickHandler={deleteClickHandler}
|
||||||
/>
|
// />
|
||||||
</RowDropdownOptions>
|
// </RowDropdownOptions>
|
||||||
)}
|
// )}
|
||||||
|
|
||||||
{currentPageSize <= 2 && (
|
// {currentPageSize <= 2 && (
|
||||||
<RowCollapseOptions>
|
// <RowCollapseOptions>
|
||||||
<RowOptionsMenu
|
// <RowOptionsMenu
|
||||||
type='collapse'
|
// type='collapse'
|
||||||
props={props}
|
// props={props}
|
||||||
deleteClickHandler={deleteClickHandler}
|
// deleteClickHandler={deleteClickHandler}
|
||||||
/>
|
// />
|
||||||
</RowCollapseOptions>
|
// </RowCollapseOptions>
|
||||||
)}
|
// )}
|
||||||
</>
|
// </>
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
]}
|
]}
|
||||||
pageSize={tableFilterState.pageSize}
|
pageSize={tableFilterState.pageSize}
|
||||||
page={
|
page={
|
||||||
@@ -597,7 +617,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
{
|
{
|
||||||
action: 'DELETE',
|
action: 'DELETE',
|
||||||
icon: 'material-symbols:delete-outline-rounded',
|
icon: 'material-symbols:delete-outline-rounded',
|
||||||
label: 'Hapus Massal',
|
label: `Hapus ${selectedRowIds.length} data`,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
toast.error(`Konfirmasi hapus ${selectedRowIds.length} data.`);
|
toast.error(`Konfirmasi hapus ${selectedRowIds.length} data.`);
|
||||||
},
|
},
|
||||||
@@ -611,7 +631,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
onClick: () => {
|
onClick: () => {
|
||||||
setApprovalAction('APPROVED');
|
setApprovalAction('APPROVED');
|
||||||
confirmModal.openModal();
|
confirmModal.openModal();
|
||||||
setRowSelection({});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -621,7 +640,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
onClick: () => {
|
onClick: () => {
|
||||||
setApprovalAction('REJECTED');
|
setApprovalAction('REJECTED');
|
||||||
confirmModal.openModal();
|
confirmModal.openModal();
|
||||||
setRowSelection({});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Badge from '@/components/Badge';
|
import Badge from '@/components/Badge';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
|
import Tooltip from '@/components/Tooltip';
|
||||||
import {
|
import {
|
||||||
formatCurrency,
|
formatCurrency,
|
||||||
formatDate,
|
formatDate,
|
||||||
@@ -10,6 +11,7 @@ import {
|
|||||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
const ProjectFlockDetail = ({
|
const ProjectFlockDetail = ({
|
||||||
@@ -17,6 +19,7 @@ const ProjectFlockDetail = ({
|
|||||||
}: {
|
}: {
|
||||||
projectFlock: ProjectFlock;
|
projectFlock: ProjectFlock;
|
||||||
}) => {
|
}) => {
|
||||||
|
const router = useRouter();
|
||||||
const [openBudgets, setOpenBudget] = useState(false);
|
const [openBudgets, setOpenBudget] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -32,18 +35,37 @@ const ProjectFlockDetail = ({
|
|||||||
<Icon icon='mdi:close' width={24} height={24} />
|
<Icon icon='mdi:close' width={24} height={24} />
|
||||||
</Link>
|
</Link>
|
||||||
<div className='divider divider-horizontal p-0 m-0'></div>
|
<div className='divider divider-horizontal p-0 m-0'></div>
|
||||||
<div className='text-sm text-secondary'>
|
<div className='text-sm text-neutral'>
|
||||||
Created On {formatDate(projectFlock.created_at, 'MMM DD, YYYY')}
|
Created On {formatDate(projectFlock.created_at, 'MMM DD, YYYY')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-row gap-3 justify-end'>
|
<div className='flex flex-row gap-3 justify-end'>
|
||||||
|
{projectFlock?.approval?.step_number == 2 && (
|
||||||
|
<Link
|
||||||
|
href={`/production/project-flock/chickin/add?projectFlockId=${projectFlock?.id}`}
|
||||||
|
className='text-success'
|
||||||
|
>
|
||||||
|
<Tooltip content='Chick In' position='bottom'>
|
||||||
|
<Icon
|
||||||
|
icon='mdi:checkbox-marked-outline'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
data-tip={'Chick In'}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
<Link
|
<Link
|
||||||
href={`/production/project-flock/detail/edit?projectFlockId=${projectFlock.id}`}
|
href={`/production/project-flock/detail/edit?projectFlockId=${projectFlock.id}`}
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:square-edit-outline' width={20} height={20} />
|
<Tooltip content='Edit' position='bottom'>
|
||||||
|
<Icon icon='mdi:square-edit-outline' width={20} height={20} />
|
||||||
|
</Tooltip>
|
||||||
</Link>
|
</Link>
|
||||||
<Button variant='link' className='p-0 text-error'>
|
<Button variant='link' className='p-0 text-error'>
|
||||||
<Icon icon='mdi:trash-can-outline' width={20} height={20} />
|
<Tooltip content='Hapus' position='bottom'>
|
||||||
|
<Icon icon='mdi:trash-can-outline' width={20} height={20} />
|
||||||
|
</Tooltip>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,7 +80,7 @@ const ProjectFlockDetail = ({
|
|||||||
variant='soft'
|
variant='soft'
|
||||||
color={
|
color={
|
||||||
projectFlock.approval.step_number == 1
|
projectFlock.approval.step_number == 1
|
||||||
? 'secondary'
|
? 'neutral'
|
||||||
: projectFlock.approval.step_number == 2
|
: projectFlock.approval.step_number == 2
|
||||||
? 'success'
|
? 'success'
|
||||||
: projectFlock.approval.step_number >= 3
|
: projectFlock.approval.step_number >= 3
|
||||||
@@ -75,7 +97,7 @@ const ProjectFlockDetail = ({
|
|||||||
height={12}
|
height={12}
|
||||||
color={
|
color={
|
||||||
projectFlock.approval.step_number == 1
|
projectFlock.approval.step_number == 1
|
||||||
? 'secondary'
|
? 'neutral'
|
||||||
: projectFlock.approval.step_number == 2
|
: projectFlock.approval.step_number == 2
|
||||||
? 'success'
|
? 'success'
|
||||||
: projectFlock.approval.step_number >= 3
|
: projectFlock.approval.step_number >= 3
|
||||||
@@ -87,7 +109,7 @@ const ProjectFlockDetail = ({
|
|||||||
</Badge>
|
</Badge>
|
||||||
<div className='divider divider-horizontal p-0 m-0'></div>
|
<div className='divider divider-horizontal p-0 m-0'></div>
|
||||||
<Badge
|
<Badge
|
||||||
color='secondary'
|
color='neutral'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className={{ badge: 'rounded-lg px-2' }}
|
className={{ badge: 'rounded-lg px-2' }}
|
||||||
>
|
>
|
||||||
@@ -103,7 +125,7 @@ const ProjectFlockDetail = ({
|
|||||||
<div className='col-span-2'>
|
<div className='col-span-2'>
|
||||||
<Badge
|
<Badge
|
||||||
variant='soft'
|
variant='soft'
|
||||||
color='secondary'
|
color='neutral'
|
||||||
className={{
|
className={{
|
||||||
badge: 'rounded-lg px-2',
|
badge: 'rounded-lg px-2',
|
||||||
}}
|
}}
|
||||||
@@ -191,7 +213,7 @@ const ProjectFlockDetail = ({
|
|||||||
</Badge>
|
</Badge>
|
||||||
<div className='divider divider-horizontal p-0 m-0'></div>
|
<div className='divider divider-horizontal p-0 m-0'></div>
|
||||||
<Badge
|
<Badge
|
||||||
color='secondary'
|
color='neutral'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className={{ badge: 'rounded-lg px-2 cursor-pointer' }}
|
className={{ badge: 'rounded-lg px-2 cursor-pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { FormikErrors, useFormik } from 'formik';
|
import { FormikErrors, useFormik } from 'formik';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { use, useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import useSWR, { KeyedMutator } from 'swr';
|
import useSWR, { KeyedMutator } from 'swr';
|
||||||
import {
|
import {
|
||||||
ProjectFlockBudgetsSchemaType,
|
ProjectFlockBudgetsSchemaType,
|
||||||
@@ -47,6 +47,7 @@ import Card from '@/components/Card';
|
|||||||
import ProjectFlockKandangTable from '@/components/pages/production/project-flock/form/ProjectFlockKandangTable';
|
import ProjectFlockKandangTable from '@/components/pages/production/project-flock/form/ProjectFlockKandangTable';
|
||||||
import { Nonstock } from '@/types/api/master-data/nonstock';
|
import { Nonstock } from '@/types/api/master-data/nonstock';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
import { useUiStore } from '@/stores/ui/ui.store';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
interface ProjectFlockFormProps {
|
interface ProjectFlockFormProps {
|
||||||
formType?: 'add' | 'edit' | 'detail';
|
formType?: 'add' | 'edit' | 'detail';
|
||||||
@@ -110,19 +111,6 @@ const ProjectFlockForm = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (initialValues?.approval?.step_name) {
|
|
||||||
const pengajuanRejected =
|
|
||||||
initialValues.approval.step_number == 1 &&
|
|
||||||
initialValues.approval.action == 'REJECTED';
|
|
||||||
const approvedDisabled =
|
|
||||||
initialValues.approval.step_number !== 1 || pengajuanRejected;
|
|
||||||
setIsApprovedDisabled(approvedDisabled);
|
|
||||||
setIsRejectedDisabled(!approvedDisabled || pengajuanRejected);
|
|
||||||
setApprovalAction(!approvedDisabled ? 'APPROVED' : 'REJECTED');
|
|
||||||
}
|
|
||||||
}, [initialValues]);
|
|
||||||
|
|
||||||
// Fetch Data
|
// Fetch Data
|
||||||
const { isLoadingOptions: isLoadingFlocks, options: optionsFlock } =
|
const { isLoadingOptions: isLoadingFlocks, options: optionsFlock } =
|
||||||
useSelect(FlockApi.basePath, 'id', 'name');
|
useSelect(FlockApi.basePath, 'id', 'name');
|
||||||
@@ -221,7 +209,12 @@ const ProjectFlockForm = ({
|
|||||||
formik.setFieldValue('area_id', (val as OptionType)?.value);
|
formik.setFieldValue('area_id', (val as OptionType)?.value);
|
||||||
formik.setFieldValue('area', val);
|
formik.setFieldValue('area', val);
|
||||||
|
|
||||||
formik.setFieldTouched('area_id', true);
|
if (Boolean(val)) {
|
||||||
|
formik.setFieldTouched('area_id', false);
|
||||||
|
formik.setFieldError('area_id', '');
|
||||||
|
} else {
|
||||||
|
formik.setFieldTouched('area_id', true);
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedArea((val as OptionType)?.value as string);
|
setSelectedArea((val as OptionType)?.value as string);
|
||||||
setSelectedLocation('');
|
setSelectedLocation('');
|
||||||
@@ -254,7 +247,12 @@ const ProjectFlockForm = ({
|
|||||||
val ? (val as OptionType)?.value : 0
|
val ? (val as OptionType)?.value : 0
|
||||||
);
|
);
|
||||||
|
|
||||||
formik.setFieldTouched(`${inputName}_id`, true);
|
if (Boolean(val)) {
|
||||||
|
formik.setFieldTouched(`${inputName}_id`, false);
|
||||||
|
formik.setFieldError(`${inputName}_id`, '');
|
||||||
|
} else {
|
||||||
|
formik.setFieldTouched(`${inputName}_id`, true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
|
const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
@@ -282,7 +280,7 @@ const ProjectFlockForm = ({
|
|||||||
const updateProjectFlockHandler = async (
|
const updateProjectFlockHandler = async (
|
||||||
payload: CreateProjectFlockPayload
|
payload: CreateProjectFlockPayload
|
||||||
) => {
|
) => {
|
||||||
const updateProjectFlockRes = await ProjectFlockApi.update(
|
const updateProjectFlockRes = await ProjectFlockApi.resubmit(
|
||||||
initialValues?.id as number,
|
initialValues?.id as number,
|
||||||
payload
|
payload
|
||||||
);
|
);
|
||||||
@@ -314,21 +312,14 @@ const ProjectFlockForm = ({
|
|||||||
0,
|
0,
|
||||||
initialValues?.flock_name?.lastIndexOf(' ')
|
initialValues?.flock_name?.lastIndexOf(' ')
|
||||||
) ?? '';
|
) ?? '';
|
||||||
|
const optionFind = optionsFlock.find((flock) => {
|
||||||
|
return flock.label == trimFlock;
|
||||||
|
}) as OptionType;
|
||||||
return {
|
return {
|
||||||
flock: initialValues?.flock_name
|
flock:
|
||||||
? {
|
optionsFlock.find((flock) => {
|
||||||
value:
|
return flock.label == trimFlock;
|
||||||
optionsFlock.find((flock) => {
|
}) ?? null,
|
||||||
return flock.label == trimFlock;
|
|
||||||
})?.value ?? 0,
|
|
||||||
label:
|
|
||||||
formType != 'detail'
|
|
||||||
? (optionsFlock.find((flock) => {
|
|
||||||
return flock.label == trimFlock;
|
|
||||||
})?.label ?? '')
|
|
||||||
: initialValues?.flock_name,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
area: initialValues?.area
|
area: initialValues?.area
|
||||||
? {
|
? {
|
||||||
value: initialValues.area?.id,
|
value: initialValues.area?.id,
|
||||||
@@ -355,14 +346,8 @@ const ProjectFlockForm = ({
|
|||||||
: null,
|
: null,
|
||||||
flock_name:
|
flock_name:
|
||||||
optionsFlock.find((flock) => {
|
optionsFlock.find((flock) => {
|
||||||
return (
|
return flock.label == trimFlock;
|
||||||
flock.label ==
|
})?.label ?? trimFlock,
|
||||||
initialValues?.flock_name?.slice(
|
|
||||||
0,
|
|
||||||
initialValues?.flock_name?.lastIndexOf(' ')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})?.label ?? '',
|
|
||||||
area_id: initialValues?.area?.id ?? 0,
|
area_id: initialValues?.area?.id ?? 0,
|
||||||
category: initialValues?.category as NonNullable<
|
category: initialValues?.category as NonNullable<
|
||||||
'GROWING' | 'LAYING' | undefined
|
'GROWING' | 'LAYING' | undefined
|
||||||
@@ -372,7 +357,18 @@ const ProjectFlockForm = ({
|
|||||||
kandang_ids: initialValues?.kandangs?.map(
|
kandang_ids: initialValues?.kandangs?.map(
|
||||||
(k: Kandang) => k.id
|
(k: Kandang) => k.id
|
||||||
) as number[],
|
) as number[],
|
||||||
project_budgets: [
|
project_budgets: initialValues?.project_budgets?.map((budget) => {
|
||||||
|
return {
|
||||||
|
nonstock: {
|
||||||
|
value: budget.nonstock?.id ?? '',
|
||||||
|
label: budget.nonstock?.name ?? '',
|
||||||
|
},
|
||||||
|
nonstock_id: budget.nonstock?.id ?? '',
|
||||||
|
qty: budget.qty,
|
||||||
|
price: budget.price,
|
||||||
|
total_price: budget.qty * budget.price,
|
||||||
|
};
|
||||||
|
}) ?? [
|
||||||
{
|
{
|
||||||
nonstock: null,
|
nonstock: null,
|
||||||
nonstock_id: '',
|
nonstock_id: '',
|
||||||
@@ -387,94 +383,13 @@ const ProjectFlockForm = ({
|
|||||||
// Formik
|
// Formik
|
||||||
const formik = useFormik<ProjectFlockFormValues>({
|
const formik = useFormik<ProjectFlockFormValues>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
flock: initialValues?.flock_name
|
...formikInitialValues,
|
||||||
? {
|
|
||||||
value:
|
|
||||||
optionsFlock.find((flock) => {
|
|
||||||
return (
|
|
||||||
flock.label ==
|
|
||||||
initialValues?.flock_name?.slice(
|
|
||||||
0,
|
|
||||||
initialValues?.flock_name?.lastIndexOf(' ')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})?.value ?? 0,
|
|
||||||
label:
|
|
||||||
formType != 'detail'
|
|
||||||
? (optionsFlock.find((flock) => {
|
|
||||||
return (
|
|
||||||
flock.label ==
|
|
||||||
initialValues?.flock_name?.slice(
|
|
||||||
0,
|
|
||||||
initialValues?.flock_name?.lastIndexOf(' ')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})?.label ?? '')
|
|
||||||
: 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_name:
|
|
||||||
formType != 'detail'
|
|
||||||
? optionsFlock.find((flock) => {
|
|
||||||
return (
|
|
||||||
flock.label ==
|
|
||||||
initialValues?.flock_name?.slice(
|
|
||||||
0,
|
|
||||||
initialValues?.flock_name?.lastIndexOf(' ')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})?.label
|
|
||||||
: (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,
|
|
||||||
kandang_ids: initialValues?.kandangs?.map((k: Kandang) => k.id) as (
|
|
||||||
| number
|
|
||||||
| undefined
|
|
||||||
)[],
|
|
||||||
project_budgets: [
|
|
||||||
{
|
|
||||||
nonstock: null,
|
|
||||||
nonstock_id: '',
|
|
||||||
qty: '',
|
|
||||||
price: '',
|
|
||||||
total_price: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as ProjectFlockFormValues,
|
} as ProjectFlockFormValues,
|
||||||
enableReinitialize: true,
|
|
||||||
validationSchema:
|
validationSchema:
|
||||||
formType == 'add' ? ProjectFlockFormSchema : UpdateProjectFlockFormSchema,
|
formType == 'add' ? ProjectFlockFormSchema : UpdateProjectFlockFormSchema,
|
||||||
validateOnBlur: true,
|
validateOnBlur: true,
|
||||||
validateOnChange: true,
|
// validateOnChange: true,
|
||||||
validateOnMount: true,
|
// validateOnMount: true,
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
setProjectFlockFormErrorMessage('');
|
setProjectFlockFormErrorMessage('');
|
||||||
const payload: CreateProjectFlockPayload = {
|
const payload: CreateProjectFlockPayload = {
|
||||||
@@ -522,7 +437,18 @@ const ProjectFlockForm = ({
|
|||||||
}, [initialValues, setSelectedArea, formType]);
|
}, [initialValues, setSelectedArea, formType]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
formikSetValues(formikInitialValues);
|
const trimFlock =
|
||||||
|
initialValues?.flock_name?.slice(
|
||||||
|
0,
|
||||||
|
initialValues?.flock_name?.lastIndexOf(' ')
|
||||||
|
) ?? '';
|
||||||
|
formikSetValues({
|
||||||
|
...formikInitialValues,
|
||||||
|
flock: optionsFlock.find((flock) => {
|
||||||
|
return flock.label == trimFlock;
|
||||||
|
}) as OptionType,
|
||||||
|
flock_name: trimFlock ?? '',
|
||||||
|
});
|
||||||
}, [formikSetValues]);
|
}, [formikSetValues]);
|
||||||
|
|
||||||
// Aktifkan lokasi jika formType = 'detail'
|
// Aktifkan lokasi jika formType = 'detail'
|
||||||
@@ -542,10 +468,6 @@ const ProjectFlockForm = ({
|
|||||||
}
|
}
|
||||||
}, [formType, initialValues]);
|
}, [formType, initialValues]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
formik.validateForm();
|
|
||||||
}, [formik.values]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const selectedRowIds = Object.keys(rowSelection)
|
const selectedRowIds = Object.keys(rowSelection)
|
||||||
.filter((id) => rowSelection[id])
|
.filter((id) => rowSelection[id])
|
||||||
@@ -583,6 +505,19 @@ const ProjectFlockForm = ({
|
|||||||
return unsub;
|
return unsub;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialValues?.approval?.step_name) {
|
||||||
|
const pengajuanRejected =
|
||||||
|
initialValues.approval.step_number == 1 &&
|
||||||
|
initialValues.approval.action == 'REJECTED';
|
||||||
|
const approvedDisabled =
|
||||||
|
initialValues.approval.step_number !== 1 || pengajuanRejected;
|
||||||
|
setIsApprovedDisabled(approvedDisabled);
|
||||||
|
setIsRejectedDisabled(!approvedDisabled || pengajuanRejected);
|
||||||
|
setApprovalAction(!approvedDisabled ? 'APPROVED' : 'REJECTED');
|
||||||
|
}
|
||||||
|
}, [initialValues]);
|
||||||
|
|
||||||
// Actions handler
|
// Actions handler
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
@@ -727,31 +662,59 @@ const ProjectFlockForm = ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
const inputPeriod =
|
const inputPeriod =
|
||||||
(initialValues?.period ?? selectedPeriod == 0) ? 1 : selectedPeriod;
|
(initialValues?.period ?? selectedPeriod == 0) ? 1 : selectedPeriod;
|
||||||
|
|
||||||
const filteredNonStockOptions = optionsNonstock.filter((nonstock) => {
|
const filteredNonStockOptions = optionsNonstock.filter((nonstock) => {
|
||||||
return !(formik.values.project_budgets ?? []).some(
|
const isNonstockAlreadyInBudgets = (
|
||||||
(budget) => budget.nonstock_id === nonstock.value
|
formik.values.project_budgets ?? []
|
||||||
);
|
).some((budget) => budget.nonstock_id === nonstock.value);
|
||||||
|
|
||||||
|
return !isNonstockAlreadyInBudgets;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className='w-full'>
|
<section className='w-full'>
|
||||||
<header className='flex flex-col gap-4 mb-6'>
|
{/* Header */}
|
||||||
<Button
|
<div className='flex flex-row justify-between items-center px-4 pt-4'>
|
||||||
href='/production/project-flock'
|
<div className='flex flex-row h-full gap-2'>
|
||||||
variant='link'
|
<Link
|
||||||
className='w-fit p-0 text-primary'
|
href={
|
||||||
>
|
formType == 'add'
|
||||||
<Icon icon='uil:arrow-left' width={24} height={24} />
|
? '/production/project-flock'
|
||||||
Kembali
|
: `/production/project-flock/detail?projectFlockId=${initialValues?.id}`
|
||||||
</Button>
|
}
|
||||||
|
className='hover:text-gray-400'
|
||||||
<h1 className='text-2xl font-bold text-center'>
|
>
|
||||||
{formType === 'add' && 'Tambah Project Flock'}
|
<Icon
|
||||||
{formType === 'edit' && 'Edit Project Flock'}
|
icon={formType == 'add' ? 'mdi:close' : 'mdi:arrow-left'}
|
||||||
{formType === 'detail' && 'Detail Project Flock'}
|
width={24}
|
||||||
</h1>
|
height={24}
|
||||||
</header>
|
/>
|
||||||
|
</Link>
|
||||||
|
<div className='divider divider-horizontal p-0 m-0'></div>
|
||||||
|
<div className='text-sm text-neutral'>
|
||||||
|
{formType == 'add' ? 'Add Flock' : 'Update Flock'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-row justify-end'>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (initialValues?.id) {
|
||||||
|
deleteModal.openModal();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
variant='link'
|
||||||
|
className='p-0 text-error'
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon='material-symbols:delete-outline-rounded'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className='justify-start text-sm'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{projectFlockFormErrorMessage && (
|
{projectFlockFormErrorMessage && (
|
||||||
<div className='my-4'>
|
<div className='my-4'>
|
||||||
<div role='alert' className='alert alert-error'>
|
<div role='alert' className='alert alert-error'>
|
||||||
@@ -829,15 +792,11 @@ const ProjectFlockForm = ({
|
|||||||
onSubmit={formik.handleSubmit}
|
onSubmit={formik.handleSubmit}
|
||||||
onReset={formik.handleReset}
|
onReset={formik.handleReset}
|
||||||
>
|
>
|
||||||
{/* Card Informasi Umum */}
|
{/* Form Informasi Umum */}
|
||||||
<Card
|
<div className='divider mt-3'></div>
|
||||||
title='Informasi Umum'
|
<div className='flex flex-col gap-4 px-4'>
|
||||||
variant='bordered'
|
<h2 className='text-2xl font-semibold'>Informasi Umum</h2>
|
||||||
className={{
|
<div className='flex flex-col gap-4'>
|
||||||
wrapper: 'w-full mb-4',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='grid sm:grid-cols-2 gap-4'>
|
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
label='Area'
|
label='Area'
|
||||||
@@ -852,6 +811,25 @@ const ProjectFlockForm = ({
|
|||||||
isClearable
|
isClearable
|
||||||
isDisabled={formType === 'detail'}
|
isDisabled={formType === 'detail'}
|
||||||
/>
|
/>
|
||||||
|
<SelectInput
|
||||||
|
required
|
||||||
|
label='Lokasi'
|
||||||
|
value={formik.values.location as OptionType}
|
||||||
|
onChange={locationChangeHandler}
|
||||||
|
options={
|
||||||
|
selectedArea != '' || initialValues?.area?.id
|
||||||
|
? optionsLocation
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
isLoading={isLoadingLocations}
|
||||||
|
isError={
|
||||||
|
formik.touched.location_id &&
|
||||||
|
Boolean(formik.errors.location_id)
|
||||||
|
}
|
||||||
|
errorMessage={formik.errors.location_id as string}
|
||||||
|
isClearable
|
||||||
|
isDisabled={formType === 'detail' || disabledLocation}
|
||||||
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
label='Flock'
|
label='Flock'
|
||||||
@@ -882,25 +860,6 @@ const ProjectFlockForm = ({
|
|||||||
isClearable
|
isClearable
|
||||||
isDisabled={formType === 'detail'}
|
isDisabled={formType === 'detail'}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
|
||||||
required
|
|
||||||
label='Lokasi'
|
|
||||||
value={formik.values.location as OptionType}
|
|
||||||
onChange={locationChangeHandler}
|
|
||||||
options={
|
|
||||||
selectedArea != '' || initialValues?.area?.id
|
|
||||||
? optionsLocation
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
isLoading={isLoadingLocations}
|
|
||||||
isError={
|
|
||||||
formik.touched.location_id &&
|
|
||||||
Boolean(formik.errors.location_id)
|
|
||||||
}
|
|
||||||
errorMessage={formik.errors.location_id as string}
|
|
||||||
isClearable
|
|
||||||
isDisabled={formType === 'detail' || disabledLocation}
|
|
||||||
/>
|
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
label='FCR'
|
label='FCR'
|
||||||
@@ -937,17 +896,12 @@ const ProjectFlockForm = ({
|
|||||||
value={selectedLocation ? inputPeriod : ''}
|
value={selectedLocation ? inputPeriod : ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
|
||||||
{/* Card Pilih Kandang */}
|
{/* Form Pilih Kandang */}
|
||||||
<Card
|
<div className='divider'></div>
|
||||||
collapsible
|
<div className='flex flex-col gap-4 px-4 pb-4'>
|
||||||
title='Pilih Kandang'
|
<h2 className='text-2xl font-semibold'>Pilih Kandang</h2>
|
||||||
variant='bordered'
|
|
||||||
className={{
|
|
||||||
wrapper: 'w-full mb-4',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='overflow-x-auto duration-300 ease-in-out'>
|
<div className='overflow-x-auto duration-300 ease-in-out'>
|
||||||
{isLoadingKandang && (
|
{isLoadingKandang && (
|
||||||
<span className='loading loading-dots loading-xl'></span>
|
<span className='loading loading-dots loading-xl'></span>
|
||||||
@@ -964,33 +918,43 @@ const ProjectFlockForm = ({
|
|||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
|
||||||
{/* Card Estimasi Budget */}
|
{/* Card Estimasi Budget */}
|
||||||
<Card
|
<div className='divider'></div>
|
||||||
collapsible
|
<div className='flex flex-col gap-4 px-4 pb-4'>
|
||||||
title='Estimasi Aggaran per Kandang'
|
<h2 className='text-2xl font-semibold'>
|
||||||
variant='bordered'
|
Estimasi Aggaran Per Flock
|
||||||
className={{
|
</h2>
|
||||||
wrapper: 'w-full',
|
<div className='flex flex-col gap-4'>
|
||||||
}}
|
{formik.values.project_budgets &&
|
||||||
>
|
formik.values.project_budgets.length > 0 ? (
|
||||||
<table className='w-full mt-4'>
|
formik.values.project_budgets.map((budget, index) => (
|
||||||
<thead>
|
<Card
|
||||||
<tr className='border-b border-gray-300'>
|
key={index}
|
||||||
<th className='text-start px-2 py-3'>Produk</th>
|
variant='bordered'
|
||||||
<th className='text-start px-2 py-3'>Kuantitas</th>
|
className={{
|
||||||
<th className='text-start px-2 py-3'>Harga Satuan</th>
|
wrapper: 'w-full',
|
||||||
<th className='text-start px-2 py-3'>Harga Total</th>
|
body: 'p-3',
|
||||||
<th className='text-start px-2 py-3'>Aksi</th>
|
}}
|
||||||
</tr>
|
>
|
||||||
</thead>
|
<div className='flex flex-col gap-2'>
|
||||||
<tbody>
|
<div className='flex flex-row justify-between items-center mb-2'>
|
||||||
{formik.values.project_budgets &&
|
<div className='text-lg'>Anggaran ke-{index + 1}</div>
|
||||||
formik.values.project_budgets.length > 0 ? (
|
<Button
|
||||||
formik.values.project_budgets.map((budget, index) => (
|
type='button'
|
||||||
<tr key={index} className='align-top'>
|
color='error'
|
||||||
<td className='px-2 py-3'>
|
onClick={() =>
|
||||||
|
onDeleteBudgetRowHandler(
|
||||||
|
budget.nonstock_id as number,
|
||||||
|
index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:trash' width={16} height={16} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-row justify-between items-center'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
isClearable
|
isClearable
|
||||||
options={filteredNonStockOptions ?? []}
|
options={filteredNonStockOptions ?? []}
|
||||||
@@ -1034,8 +998,8 @@ const ProjectFlockForm = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</td>
|
</div>
|
||||||
<td className='px-2 py-3'>
|
<div className='flex flex-row justify-between items-center'>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
name={`project_budgets[${index}].qty`}
|
name={`project_budgets[${index}].qty`}
|
||||||
placeholder='Masukkan jumlah'
|
placeholder='Masukkan jumlah'
|
||||||
@@ -1046,11 +1010,13 @@ const ProjectFlockForm = ({
|
|||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
endAdornment={
|
endAdornment={
|
||||||
isResponseSuccess(nonstocks)
|
<div className='text-gray-500'>
|
||||||
? (nonstocks.data.find(
|
{isResponseSuccess(nonstocks)
|
||||||
(ns) => ns.id === budget.nonstock_id
|
? (nonstocks.data.find(
|
||||||
)?.uom?.name ?? '')
|
(ns) => ns.id === budget.nonstock_id
|
||||||
: ''
|
)?.uom?.name ?? '')
|
||||||
|
: ''}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
errorMessage={
|
errorMessage={
|
||||||
(
|
(
|
||||||
@@ -1070,8 +1036,8 @@ const ProjectFlockForm = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</td>
|
</div>
|
||||||
<td className='px-2 py-3'>
|
<div className='flex flex-row justify-between items-center'>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
name={`project_budgets[${index}].price`}
|
name={`project_budgets[${index}].price`}
|
||||||
value={formik.values.project_budgets[index].price}
|
value={formik.values.project_budgets[index].price}
|
||||||
@@ -1082,6 +1048,17 @@ const ProjectFlockForm = ({
|
|||||||
placeholder='Masukkan harga satuan'
|
placeholder='Masukkan harga satuan'
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
startAdornment='Rp'
|
startAdornment='Rp'
|
||||||
|
endAdornment={
|
||||||
|
<div className='text-gray-500'>
|
||||||
|
{`Per ${
|
||||||
|
isResponseSuccess(nonstocks)
|
||||||
|
? (nonstocks.data.find(
|
||||||
|
(ns) => ns.id === budget.nonstock_id
|
||||||
|
)?.uom?.name ?? 'Item')
|
||||||
|
: 'Item'
|
||||||
|
}`}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
errorMessage={
|
errorMessage={
|
||||||
(
|
(
|
||||||
formik.errors.project_budgets?.[
|
formik.errors.project_budgets?.[
|
||||||
@@ -1100,8 +1077,8 @@ const ProjectFlockForm = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</td>
|
</div>
|
||||||
<td className='px-2 py-3'>
|
<div className='flex flex-row justify-between items-center'>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
name={`project_budgets[${index}].total_price`}
|
name={`project_budgets[${index}].total_price`}
|
||||||
value={
|
value={
|
||||||
@@ -1115,9 +1092,12 @@ const ProjectFlockForm = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
placeholder='Masukkan harga satuan'
|
placeholder='Masukkan harga total'
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
startAdornment='Rp'
|
startAdornment='Rp'
|
||||||
|
endAdornment={
|
||||||
|
<div className='text-gray-500'>Total</div>
|
||||||
|
}
|
||||||
errorMessage={
|
errorMessage={
|
||||||
(
|
(
|
||||||
formik.errors.project_budgets?.[
|
formik.errors.project_budgets?.[
|
||||||
@@ -1137,108 +1117,50 @@ const ProjectFlockForm = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</td>
|
</div>
|
||||||
<td className='px-2 py-4'>
|
</div>
|
||||||
<Button
|
</Card>
|
||||||
type='button'
|
))
|
||||||
color='error'
|
) : (
|
||||||
onClick={() =>
|
<div className='text-center py-4 text-gray-400'>
|
||||||
onDeleteBudgetRowHandler(
|
Tidak ada data estimasi anggaran.
|
||||||
budget.nonstock_id as number,
|
</div>
|
||||||
index
|
)}
|
||||||
)
|
<Button
|
||||||
}
|
type='button'
|
||||||
>
|
onClick={onAddBudgetRowHandler}
|
||||||
<Icon icon='mdi:trash' width={16} height={16} />
|
disabled={filteredNonStockOptions.length == 0}
|
||||||
</Button>
|
color='success'
|
||||||
</td>
|
className='w-fit self-center'
|
||||||
</tr>
|
>
|
||||||
))
|
<Icon icon='mdi:plus' width={16} height={16} /> Add Budget
|
||||||
) : (
|
</Button>
|
||||||
<tr>
|
</div>
|
||||||
<td colSpan={4} className='text-center py-4 text-gray-500'>
|
</div>
|
||||||
Tidak ada data estimasi anggaran.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<Button
|
<div className='flex flex-row justify-center gap-2 flex-wrap my-6 px-4'>
|
||||||
type='button'
|
{/* <div className='w-120'>
|
||||||
onClick={onAddBudgetRowHandler}
|
<div className='text-primary text-sm'>
|
||||||
disabled={filteredNonStockOptions.length == 0}
|
{JSON.stringify(formik.values)}
|
||||||
>
|
|
||||||
<Icon icon='mdi:plus' width={16} height={16} />
|
|
||||||
Tambah Item
|
|
||||||
</Button>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<div className='flex flex-row justify-center gap-2 flex-wrap my-6'>
|
|
||||||
{formType !== 'detail' && (
|
|
||||||
<div className='flex flex-row justify-end gap-2'>
|
|
||||||
<Button
|
|
||||||
type='button'
|
|
||||||
color='warning'
|
|
||||||
className='px-4'
|
|
||||||
onClick={formik.handleReset}
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type='submit'
|
|
||||||
color='primary'
|
|
||||||
isLoading={formik.isSubmitting}
|
|
||||||
disabled={
|
|
||||||
!formik.isValid || formik.isSubmitting
|
|
||||||
// TODO: Add logic && ketika nilai kandang_ids sudah beda dari initial values
|
|
||||||
}
|
|
||||||
className='px-4'
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className='text-error text-sm'>
|
||||||
|
{JSON.stringify(formik.errors)}
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
{formType !== 'detail' && (
|
||||||
|
<Button
|
||||||
|
type='submit'
|
||||||
|
color='primary'
|
||||||
|
isLoading={formik.isSubmitting}
|
||||||
|
disabled={!formik.isValid || formik.isSubmitting}
|
||||||
|
className='px-4 w-full'
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:plus' width={24} height={24} />
|
||||||
|
{formType == 'add' ? 'Add Flock' : 'Update Flock'}
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{formType != 'add' && (
|
|
||||||
<div className='flex flex-row gap-2 mb-6'>
|
|
||||||
{formType != 'edit' && (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
router.push(
|
|
||||||
`/production/project-flock/detail/edit?projectFlockId=${initialValues?.id}`
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
color='warning'
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
icon='mdi:pencil-outline'
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
className='justify-start text-sm'
|
|
||||||
/>
|
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (initialValues?.id) {
|
|
||||||
deleteModal.openModal();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
color='error'
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
icon='material-symbols:delete-outline-rounded'
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
className='justify-start text-sm'
|
|
||||||
/>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import Badge from '@/components/Badge';
|
||||||
|
import Card from '@/components/Card';
|
||||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||||
import PillBadge from '@/components/PillBadge';
|
import PillBadge from '@/components/PillBadge';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
@@ -9,6 +11,7 @@ import {
|
|||||||
ProjectFlock,
|
ProjectFlock,
|
||||||
ProjectFlockPeriods,
|
ProjectFlockPeriods,
|
||||||
} from '@/types/api/production/project-flock';
|
} from '@/types/api/production/project-flock';
|
||||||
|
import { Icon } from '@iconify/react';
|
||||||
import { OnChangeFn, Row } from '@tanstack/react-table';
|
import { OnChangeFn, Row } from '@tanstack/react-table';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
@@ -29,161 +32,119 @@ const ProjectFlockKandangTable = ({
|
|||||||
initialValues?: ProjectFlock;
|
initialValues?: ProjectFlock;
|
||||||
formType: 'add' | 'edit' | 'detail';
|
formType: 'add' | 'edit' | 'detail';
|
||||||
}) => {
|
}) => {
|
||||||
const initialKandangIdSet = useMemo(() => {
|
// Fungsi untuk menangani perubahan checkbox
|
||||||
return initialValues?.kandangs.map((k) => k.id) ?? [];
|
const handleCheckboxChange = (kandang: Kandang, isChecked: boolean) => {
|
||||||
}, [initialValues]);
|
// Hanya izinkan perubahan jika tidak dalam mode 'detail'
|
||||||
const isRowEnabled = (row: Row<Kandang>) => {
|
if (formType === 'detail') return;
|
||||||
const isDisabled =
|
|
||||||
!initialKandangIdSet.includes(row.original.id) &&
|
// Pastikan kandang.id ada dan tidak null/undefined
|
||||||
(row.original.status == 'ACTIVE' ||
|
if (kandang.id === undefined) return;
|
||||||
row.original.status == 'PENGAJUAN' ||
|
|
||||||
formType == 'detail');
|
const kandangIdString = kandang.id.toString();
|
||||||
return !isDisabled;
|
|
||||||
|
setRowSelection((prev) => {
|
||||||
|
const newSelection = { ...prev };
|
||||||
|
if (isChecked) {
|
||||||
|
newSelection[kandangIdString] = true;
|
||||||
|
} else {
|
||||||
|
delete newSelection[kandangIdString];
|
||||||
|
}
|
||||||
|
return newSelection;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Table<Kandang>
|
{listKandang.length > 0 ? (
|
||||||
data={listKandang}
|
<>
|
||||||
columns={[
|
{/* ... Bagian Badge Status ... */}
|
||||||
{
|
<div className='flex flex-row mb-4'>
|
||||||
id: 'select',
|
<Badge
|
||||||
header: ({ table }) => {
|
variant='soft'
|
||||||
const allRows = table.getRowModel().rows;
|
color='primary'
|
||||||
// 1. Filter semua baris dengan logika yang sama persis seperti di cell
|
className={{
|
||||||
const selectableRows = allRows.filter(isRowEnabled);
|
badge: 'rounded-lg px-2',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:circle' width={12} height={12} />
|
||||||
|
Tersedia (
|
||||||
|
{
|
||||||
|
listKandang.filter((kandang) => kandang.status == 'NON_ACTIVE')
|
||||||
|
.length
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</Badge>
|
||||||
|
<div className='divider divider-horizontal mx-1'></div>
|
||||||
|
<Badge
|
||||||
|
variant='soft'
|
||||||
|
color='neutral'
|
||||||
|
className={{
|
||||||
|
badge: 'rounded-lg px-2',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:circle' width={12} height={12} />
|
||||||
|
Tidak Tersedia (
|
||||||
|
{
|
||||||
|
listKandang.filter((kandang) => kandang.status != 'NON_ACTIVE')
|
||||||
|
.length
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
{/* --- */}
|
||||||
|
<Card
|
||||||
|
variant='bordered'
|
||||||
|
className={{
|
||||||
|
wrapper: 'w-full rounded-lg',
|
||||||
|
body: 'p-4',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col gap-4 w-full'>
|
||||||
|
{listKandang.map((kandang, index) => {
|
||||||
|
const kandangIdString =
|
||||||
|
kandang.id?.toString() ?? `temp-${index}`;
|
||||||
|
|
||||||
// 2. Cek apakah SEMUA baris yang BISA DIPILIH sudah terpilih
|
const isSelected =
|
||||||
const allSelected =
|
!!rowSelection[kandangIdString] ||
|
||||||
selectableRows.length > 0 &&
|
(kandang.id !== undefined &&
|
||||||
selectableRows.every((row) => row.getIsSelected());
|
selectedIds.includes(kandang.id));
|
||||||
|
|
||||||
// 3. Cek apakah BEBERAPA baris yang BISA DIPILIH sudah terpilih
|
const isDisabled =
|
||||||
const someSelected =
|
formType == 'detail' || kandang.status != 'NON_ACTIVE';
|
||||||
selectableRows.some((row) => row.getIsSelected()) &&
|
|
||||||
!allSelected;
|
|
||||||
|
|
||||||
// 4. Fungsi toggle HANYA akan mentoggle baris yang BISA DIPILIH
|
return (
|
||||||
const toggleSelectableRows = () => {
|
<div key={index} className='flex flex-row justify-between'>
|
||||||
const shouldSelect = !allSelected;
|
<CheckboxInput
|
||||||
selectableRows.forEach((row) =>
|
name={`kandang-${kandang.id}`} // Nama unik untuk setiap checkbox
|
||||||
row.toggleSelected(shouldSelect)
|
label={kandang.name}
|
||||||
|
checked={isSelected}
|
||||||
|
disabled={isDisabled}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleCheckboxChange(kandang, e.currentTarget.checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Badge
|
||||||
|
variant='soft'
|
||||||
|
color={
|
||||||
|
kandang.status == 'NON_ACTIVE' ? 'primary' : 'neutral'
|
||||||
|
}
|
||||||
|
className={{
|
||||||
|
badge: 'rounded-lg px-2',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:circle' width={12} height={12} />
|
||||||
|
{kandang.status != 'NON_ACTIVE' && 'Tidak'} Tersedia
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
})}
|
||||||
|
</div>
|
||||||
return (
|
</Card>
|
||||||
<div className='w-full flex flex-row justify-center'>
|
</>
|
||||||
<CheckboxInput
|
) : (
|
||||||
name='allRow'
|
<div className='text-center py-4 text-gray-400'>
|
||||||
checked={allSelected}
|
Pilih lokasi terlebih dahulu
|
||||||
indeterminate={someSelected}
|
</div>
|
||||||
onChange={toggleSelectableRows}
|
)}
|
||||||
disabled={
|
|
||||||
selectableRows.length === 0 || formType == 'detail'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<CheckboxInput
|
|
||||||
name='row'
|
|
||||||
checked={
|
|
||||||
(row.getIsSelected() &&
|
|
||||||
(row.original.status == 'NON_ACTIVE' ||
|
|
||||||
row.original.status == 'PENGAJUAN')) ||
|
|
||||||
(selectedIds && selectedIds.includes(row.original.id))
|
|
||||||
}
|
|
||||||
disabled={
|
|
||||||
formType == 'detail' ||
|
|
||||||
(!initialKandangIdSet.includes(row.original.id) &&
|
|
||||||
(row.original.status == 'ACTIVE' ||
|
|
||||||
row.original.status == 'PENGAJUAN'))
|
|
||||||
}
|
|
||||||
indeterminate={row.getIsSomeSelected()}
|
|
||||||
onChange={row.getToggleSelectedHandler()}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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.capacity,
|
|
||||||
header: 'Kapasitas',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorFn: (row) => row.location?.name,
|
|
||||||
header: 'Periode',
|
|
||||||
cell: (props) => {
|
|
||||||
const period =
|
|
||||||
listPeriods.length > 0
|
|
||||||
? listPeriods.find((p) => p.id == props.row.original.id)
|
|
||||||
: undefined;
|
|
||||||
const calcPeriod = period?.period == 0 ? 1 : period?.period;
|
|
||||||
const selected = props.row.getIsSelected();
|
|
||||||
const initPeriod = initialValues?.period;
|
|
||||||
return formType == 'detail'
|
|
||||||
? selected
|
|
||||||
? initPeriod
|
|
||||||
: '-'
|
|
||||||
: formType == 'add'
|
|
||||||
? (calcPeriod ?? '-')
|
|
||||||
: selected
|
|
||||||
? (initPeriod ?? '-')
|
|
||||||
: (calcPeriod ?? '-');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -141,6 +141,38 @@ export class ProjectFlockService extends BaseApiService<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resubmit Project Flock
|
||||||
|
*/
|
||||||
|
async resubmit(
|
||||||
|
id: number,
|
||||||
|
payload: UpdateProjectFlockPayload
|
||||||
|
): Promise<BaseApiResponse<ProjectFlock> | undefined> {
|
||||||
|
try {
|
||||||
|
const updatePath = `${this.basePath}/${id}/resubmit`;
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...(this.header ?? {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateRes = await httpClient<BaseApiResponse<ProjectFlock>>(
|
||||||
|
updatePath,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
body: payload,
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return updateRes;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (axios.isAxiosError<BaseApiResponse<ProjectFlock>>(error)) {
|
||||||
|
return error.response?.data;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Approve single Project Flock
|
* Approve single Project Flock
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user