mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
feat(FE-86-88): Adding reject button and integrate with approval api
This commit is contained in:
@@ -13,7 +13,7 @@ const ProjectFlockDetail = () => {
|
|||||||
|
|
||||||
const projectFlockId = searchParams.get("projectFlockId");
|
const projectFlockId = searchParams.get("projectFlockId");
|
||||||
|
|
||||||
const { data: projectFlock, isLoading: isLoadingCostumer } = useSWR(
|
const { data: projectFlock, isLoading: isLoadingProjectFlock } = useSWR(
|
||||||
projectFlockId,
|
projectFlockId,
|
||||||
(id: number) => ProjectFlockApi.getSingle(id)
|
(id: number) => ProjectFlockApi.getSingle(id)
|
||||||
);
|
);
|
||||||
@@ -28,15 +28,15 @@ const ProjectFlockDetail = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isLoadingCostumer && (!projectFlock || isResponseError(projectFlock))){
|
if(!isLoadingProjectFlock && (!projectFlock || isResponseError(projectFlock))){
|
||||||
router.replace("/404");
|
router.replace("/404");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full p-4 flex flex-row justify-center">
|
<div className="w-full p-4 flex flex-row justify-center">
|
||||||
{isLoadingCostumer && <span className="loading loading-spinner loading-xl" />}
|
{isLoadingProjectFlock && <span className="loading loading-spinner loading-xl" />}
|
||||||
{!isLoadingCostumer && isResponseSuccess(projectFlock) && (
|
{!isLoadingProjectFlock && isResponseSuccess(projectFlock) && (
|
||||||
<ProjectFlockForm formType="detail" initialValues={projectFlock.data} />
|
<ProjectFlockForm formType="detail" initialValues={projectFlock.data} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ const DateInput = ({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
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',
|
'input h-12 px-4 py-2 text-base font-normal leading-6 w-full rounded outline-none! transition-all duration-200 flex items-center',
|
||||||
{
|
{
|
||||||
'border-error': isError,
|
'border-error': isError,
|
||||||
'border-success!': isValid,
|
'border-success!': isValid,
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ const FileInput = ({
|
|||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={cn(
|
className={cn(
|
||||||
'grow file-input w-full h-12 rounded-lg!',
|
'grow file-input w-full h-12 rounded',
|
||||||
className?.input
|
className?.input
|
||||||
)}
|
)}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
|||||||
classNames={{
|
classNames={{
|
||||||
control: ({ isFocused, isDisabled }) =>
|
control: ({ isFocused, isDisabled }) =>
|
||||||
cn(
|
cn(
|
||||||
'w-full min-h-12! rounded-lg! border bg-white transition-shadow cursor-pointer!',
|
'w-full min-h-12! rounded border bg-white transition-shadow cursor-pointer!',
|
||||||
{
|
{
|
||||||
'border-red-500! ring-2 ring-red-200': isError,
|
'border-red-500! ring-2 ring-red-200': isError,
|
||||||
'border-indigo-500 ring-2 ring-indigo-200': isFocused,
|
'border-indigo-500 ring-2 ring-indigo-200': isFocused,
|
||||||
@@ -176,7 +176,7 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
|||||||
input: () => cn('text-gray-900'),
|
input: () => cn('text-gray-900'),
|
||||||
indicatorsContainer: () => cn('flex items-center gap-1 pr-2'),
|
indicatorsContainer: () => cn('flex items-center gap-1 pr-2'),
|
||||||
dropdownIndicator: ({ isFocused }) =>
|
dropdownIndicator: ({ isFocused }) =>
|
||||||
cn('p-1 rounded-md hover:bg-gray-100', {
|
cn('p-1 rounded hover:bg-gray-100', {
|
||||||
'text-gray-900': isFocused,
|
'text-gray-900': isFocused,
|
||||||
'text-gray-500': !isFocused,
|
'text-gray-500': !isFocused,
|
||||||
'text-error!': isError,
|
'text-error!': isError,
|
||||||
@@ -185,7 +185,7 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
|||||||
cn('border border-gray-200 rounded-lg bg-white shadow-lg!'),
|
cn('border border-gray-200 rounded-lg bg-white shadow-lg!'),
|
||||||
menuList: () => cn('p-2! max-h-60 overflow-auto'),
|
menuList: () => cn('p-2! max-h-60 overflow-auto'),
|
||||||
option: ({ isFocused, isSelected }) =>
|
option: ({ isFocused, isSelected }) =>
|
||||||
cn('mt-1 px-3 py-2 rounded-md cursor-pointer!', {
|
cn('mt-1 px-3 py-2 rounded cursor-pointer!', {
|
||||||
'bg-indigo-600 text-white': isFocused,
|
'bg-indigo-600 text-white': isFocused,
|
||||||
'bg-blue-500!': isSelected,
|
'bg-blue-500!': isSelected,
|
||||||
'text-gray-700': !isFocused && !isSelected,
|
'text-gray-700': !isFocused && !isSelected,
|
||||||
@@ -193,7 +193,7 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
|||||||
multiValue: ({ getValue, index }) => {
|
multiValue: ({ getValue, index }) => {
|
||||||
const selectedValues = getValue() as T[];
|
const selectedValues = getValue() as T[];
|
||||||
return cn(
|
return cn(
|
||||||
'bg-indigo-50 rounded-md py-0.5 pl-2 pr-1 flex items-center gap-1!',
|
'bg-indigo-50 rounded py-0.5 pl-2 pr-1 flex items-center gap-1!',
|
||||||
selectedValues[index]?.className
|
selectedValues[index]?.className
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ const TextArea = ({
|
|||||||
|
|
||||||
<textarea
|
<textarea
|
||||||
className={cn(
|
className={cn(
|
||||||
'input h-auto px-4 py-2 text-base font-normal leading-6 w-full rounded-lg! outline-none! transition-all',
|
'input h-auto px-4 py-2 text-base font-normal leading-6 w-full rounded outline-none! transition-all',
|
||||||
{
|
{
|
||||||
'border-error': isError,
|
'border-error': isError,
|
||||||
'border-success!': isValid,
|
'border-success!': isValid,
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ const TextInput = ({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
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',
|
'input h-12 px-4 py-2 text-base font-normal leading-6 w-full rounded outline-none! transition-all duration-200',
|
||||||
{
|
{
|
||||||
'border-error': isError,
|
'border-error': isError,
|
||||||
'border-success!': isValid,
|
'border-success!': isValid,
|
||||||
|
|||||||
@@ -57,13 +57,6 @@ const ChickinTable = () => {
|
|||||||
`${ChickinApi.basePath}${getTableFilterQueryString()}`,
|
`${ChickinApi.basePath}${getTableFilterQueryString()}`,
|
||||||
ChickinApi.getAllFetcher
|
ChickinApi.getAllFetcher
|
||||||
);
|
);
|
||||||
const {
|
|
||||||
data: projectFlocks,
|
|
||||||
isLoading: isLoadingProjectFlocks,
|
|
||||||
} = useSWR(
|
|
||||||
`${ProjectFlockApi.basePath}${getTableFilterQueryString()}`,
|
|
||||||
ProjectFlockApi.getAllFetcher
|
|
||||||
);
|
|
||||||
|
|
||||||
const searchChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const searchChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
updateFilter('search', event.target.value);
|
updateFilter('search', event.target.value);
|
||||||
|
|||||||
@@ -16,13 +16,12 @@ import { ProjectFlockApi } from '@/services/api/production';
|
|||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
import { Kandang } from '@/types/api/master-data/kandang';
|
||||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
|
||||||
import { Icon } from '@iconify/react';
|
|
||||||
import {
|
import {
|
||||||
CellContext,
|
ProjectFlockApprovalPayload,
|
||||||
ColumnDef,
|
ProjectFlock,
|
||||||
SortingState,
|
} from '@/types/api/production/project-flock';
|
||||||
} from '@tanstack/react-table';
|
import { Icon } from '@iconify/react';
|
||||||
|
import { CellContext, SortingState } from '@tanstack/react-table';
|
||||||
import { ChangeEventHandler, useState } from 'react';
|
import { ChangeEventHandler, useState } from 'react';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
@@ -56,6 +55,7 @@ const RowOptionsMenu = ({
|
|||||||
<Icon icon='mdi:eye-outline' width={16} height={16} />
|
<Icon icon='mdi:eye-outline' width={16} height={16} />
|
||||||
Detail
|
Detail
|
||||||
</Button>
|
</Button>
|
||||||
|
{props.row.original.approval.step_name === 'Aktif' && (
|
||||||
<Button
|
<Button
|
||||||
href={`/production/chickin/add?projectFlockId=${props.row.original.id}`}
|
href={`/production/chickin/add?projectFlockId=${props.row.original.id}`}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
@@ -65,7 +65,9 @@ const RowOptionsMenu = ({
|
|||||||
<Icon icon='mdi:home-import-outline' width={16} height={16} />
|
<Icon icon='mdi:home-import-outline' width={16} height={16} />
|
||||||
Chickin
|
Chickin
|
||||||
</Button>
|
</Button>
|
||||||
{/* <Button
|
)}
|
||||||
|
{props.row.original.approval.step_name === 'Pengajuan' && (
|
||||||
|
<Button
|
||||||
href={`/production/project-flock/detail/edit?projectFlockId=${props.row.original.id}`}
|
href={`/production/project-flock/detail/edit?projectFlockId=${props.row.original.id}`}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='warning'
|
color='warning'
|
||||||
@@ -73,7 +75,8 @@ const RowOptionsMenu = ({
|
|||||||
>
|
>
|
||||||
<Icon icon='mdi:pencil-outline' width={16} height={16} />
|
<Icon icon='mdi:pencil-outline' width={16} height={16} />
|
||||||
Edit
|
Edit
|
||||||
</Button> */}
|
</Button>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
onClick={deleteClickHandler}
|
onClick={deleteClickHandler}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
@@ -144,20 +147,20 @@ const ProjectFlockTable = () => {
|
|||||||
search: areaSelectInputValue,
|
search: areaSelectInputValue,
|
||||||
limit: '100',
|
limit: '100',
|
||||||
}).toString()}`;
|
}).toString()}`;
|
||||||
const {
|
const { data: areas, isLoading: isLoadingAreas } = useSWR(
|
||||||
data: areas,
|
areaUrl,
|
||||||
isLoading: isLoadingAreas,
|
AreaApi.getAllFetcher
|
||||||
} = useSWR(areaUrl, AreaApi.getAllFetcher);
|
);
|
||||||
|
|
||||||
const locationUrl = `${LocationApi.basePath}?${new URLSearchParams({
|
const locationUrl = `${LocationApi.basePath}?${new URLSearchParams({
|
||||||
search: locationSelectInputValue,
|
search: locationSelectInputValue,
|
||||||
area_id: selectedArea != null ? selectedArea.value.toString() : '',
|
area_id: selectedArea != null ? selectedArea.value.toString() : '',
|
||||||
limit: '100',
|
limit: '100',
|
||||||
}).toString()}`;
|
}).toString()}`;
|
||||||
const {
|
const { data: locations, isLoading: isLoadingLocations } = useSWR(
|
||||||
data: locations,
|
locationUrl,
|
||||||
isLoading: isLoadingLocations,
|
LocationApi.getAllFetcher
|
||||||
} = useSWR(locationUrl, LocationApi.getAllFetcher);
|
);
|
||||||
|
|
||||||
const kandangUrl = `${KandangApi.basePath}?${new URLSearchParams({
|
const kandangUrl = `${KandangApi.basePath}?${new URLSearchParams({
|
||||||
search: kandangSelectInputValue,
|
search: kandangSelectInputValue,
|
||||||
@@ -165,10 +168,10 @@ const ProjectFlockTable = () => {
|
|||||||
selectedLocation != null ? selectedLocation.value.toString() : '',
|
selectedLocation != null ? selectedLocation.value.toString() : '',
|
||||||
limit: '100',
|
limit: '100',
|
||||||
}).toString()}`;
|
}).toString()}`;
|
||||||
const {
|
const { data: kandangs, isLoading: isLoadingKandang } = useSWR(
|
||||||
data: kandangs,
|
kandangUrl,
|
||||||
isLoading: isLoadingKandang,
|
KandangApi.getAllFetcher
|
||||||
} = useSWR(kandangUrl, KandangApi.getAllFetcher);
|
);
|
||||||
|
|
||||||
// Data to Options Mapping
|
// Data to Options Mapping
|
||||||
const optionsArea = isResponseSuccess(areas)
|
const optionsArea = isResponseSuccess(areas)
|
||||||
@@ -200,129 +203,6 @@ const ProjectFlockTable = () => {
|
|||||||
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
||||||
const [selectedFlocks, setSelectedFlocks] = useState<ProjectFlock[]>([]);
|
const [selectedFlocks, setSelectedFlocks] = useState<ProjectFlock[]>([]);
|
||||||
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
||||||
|
|
||||||
// Columns
|
|
||||||
const projectFlocksColumns: ColumnDef<ProjectFlock>[] = [
|
|
||||||
{
|
|
||||||
id: 'select',
|
|
||||||
header: () => {
|
|
||||||
const allSelected =
|
|
||||||
isResponseSuccess(projectFlocks) &&
|
|
||||||
projectFlocks.data.length > 0 &&
|
|
||||||
selectedIds.length === projectFlocks.data.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
type='checkbox'
|
|
||||||
className='checkbox checkbox-sm'
|
|
||||||
checked={allSelected}
|
|
||||||
onChange={(e) => handleSelectAll(e.target.checked)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: (props) => {
|
|
||||||
const id = props.row.original.id;
|
|
||||||
const isChecked = selectedIds.includes(id);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
type='checkbox'
|
|
||||||
className='checkbox checkbox-sm'
|
|
||||||
checked={isChecked}
|
|
||||||
onChange={(e) => handleSelectRow(id, e.target.checked)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
accessorKey: 'flock.name',
|
|
||||||
header: 'Flock',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'area.name',
|
|
||||||
header: 'Area',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'location.name',
|
|
||||||
header: 'Lokasi',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'fcr.name',
|
|
||||||
header: 'FCR',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'category',
|
|
||||||
header: 'Kategori',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Kandang',
|
|
||||||
cell: (props) => {
|
|
||||||
const kandang = props.row.original.kandangs;
|
|
||||||
if (kandang) {
|
|
||||||
const kandangNames = kandang.map((k: Kandang) => k.name);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{kandangNames.length > 0 ? kandangNames.join(', ') : 'Tidak ada'}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'period',
|
|
||||||
header: 'Periode',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'created_at',
|
|
||||||
header: 'Dibuat pada',
|
|
||||||
cell: (props) =>
|
|
||||||
new Date(props.row.original.created_at).toLocaleDateString(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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 = () => {
|
|
||||||
setSelectedProjectFlock(props.row.original);
|
|
||||||
deleteModal.openModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{currentPageSize > 2 && (
|
|
||||||
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
|
||||||
<RowOptionsMenu
|
|
||||||
type='dropdown'
|
|
||||||
props={props}
|
|
||||||
deleteClickHandler={deleteClickHandler}
|
|
||||||
/>
|
|
||||||
</RowDropdownOptions>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{currentPageSize <= 2 && (
|
|
||||||
<RowCollapseOptions>
|
|
||||||
<RowOptionsMenu
|
|
||||||
type='dropdown'
|
|
||||||
props={props}
|
|
||||||
deleteClickHandler={deleteClickHandler}
|
|
||||||
/>
|
|
||||||
</RowCollapseOptions>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Handler
|
// Handler
|
||||||
const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => {
|
const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
const newVal = val as OptionType;
|
const newVal = val as OptionType;
|
||||||
@@ -343,9 +223,15 @@ const ProjectFlockTable = () => {
|
|||||||
};
|
};
|
||||||
const handleSelectAll = (checked: boolean) => {
|
const handleSelectAll = (checked: boolean) => {
|
||||||
if (checked && isResponseSuccess(projectFlocks)) {
|
if (checked && isResponseSuccess(projectFlocks)) {
|
||||||
const allIds = projectFlocks.data.map((item) => item.id);
|
const allIds = projectFlocks.data
|
||||||
|
.filter((item) => item.approval.step_name === 'Pengajuan')
|
||||||
|
.map((item) => item.id);
|
||||||
setSelectedIds(allIds);
|
setSelectedIds(allIds);
|
||||||
setSelectedFlocks(projectFlocks.data);
|
setSelectedFlocks(
|
||||||
|
projectFlocks.data.filter(
|
||||||
|
(item) => item.approval.step_name === 'Pengajuan'
|
||||||
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
setSelectedIds([]);
|
setSelectedIds([]);
|
||||||
setSelectedFlocks([]);
|
setSelectedFlocks([]);
|
||||||
@@ -374,12 +260,12 @@ const ProjectFlockTable = () => {
|
|||||||
setIsApproveLoading(true);
|
setIsApproveLoading(true);
|
||||||
const approveProjectFlockRes = await ProjectFlockApi.customRequest<
|
const approveProjectFlockRes = await ProjectFlockApi.customRequest<
|
||||||
BaseApiResponse<ProjectFlock>,
|
BaseApiResponse<ProjectFlock>,
|
||||||
'POST'
|
ProjectFlockApprovalPayload
|
||||||
>(`/approve`, {
|
>(`/approvals`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
payload: 'POST',
|
payload: {
|
||||||
params: {
|
action: 'APPROVED',
|
||||||
ids: selectedFlocks.map((flock) => flock.id).join(','),
|
approvable_ids: selectedFlocks.map((flock) => flock.id),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -391,6 +277,9 @@ const ProjectFlockTable = () => {
|
|||||||
toast.error(approveProjectFlockRes?.message as string);
|
toast.error(approveProjectFlockRes?.message as string);
|
||||||
confirmModal.closeModal();
|
confirmModal.closeModal();
|
||||||
}
|
}
|
||||||
|
setSelectedIds([]);
|
||||||
|
setSelectedFlocks([]);
|
||||||
|
refreshProjectFlocks();
|
||||||
setIsApproveLoading(false);
|
setIsApproveLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -508,7 +397,137 @@ const ProjectFlockTable = () => {
|
|||||||
|
|
||||||
<Table<ProjectFlock>
|
<Table<ProjectFlock>
|
||||||
data={isResponseSuccess(projectFlocks) ? projectFlocks?.data : []}
|
data={isResponseSuccess(projectFlocks) ? projectFlocks?.data : []}
|
||||||
columns={projectFlocksColumns}
|
columns={[
|
||||||
|
{
|
||||||
|
id: 'select',
|
||||||
|
header: () => {
|
||||||
|
const allSelected =
|
||||||
|
isResponseSuccess(projectFlocks) &&
|
||||||
|
projectFlocks.data.length > 0 &&
|
||||||
|
selectedIds.length === projectFlocks.data.length;
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type='checkbox'
|
||||||
|
className='checkbox checkbox-sm'
|
||||||
|
checked={allSelected}
|
||||||
|
onChange={(e) => handleSelectAll(e.target.checked)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: (props) => {
|
||||||
|
const id = props.row.original.id;
|
||||||
|
const isChecked = selectedIds.includes(id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
disabled={
|
||||||
|
props.row.original.approval.step_name != 'Pengajuan'
|
||||||
|
}
|
||||||
|
type='checkbox'
|
||||||
|
className='checkbox checkbox-sm'
|
||||||
|
checked={isChecked}
|
||||||
|
onChange={(e) => handleSelectRow(id, e.target.checked)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: 'flock.name',
|
||||||
|
header: 'Flock',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'area.name',
|
||||||
|
header: 'Area',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'location.name',
|
||||||
|
header: 'Lokasi',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'fcr.name',
|
||||||
|
header: 'FCR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'category',
|
||||||
|
header: 'Kategori',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'approval.step_name',
|
||||||
|
header: 'Status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Kandang',
|
||||||
|
cell: (props) => {
|
||||||
|
const kandang = props.row.original.kandangs;
|
||||||
|
if (kandang) {
|
||||||
|
const kandangNames = kandang.map((k: Kandang) => k.name);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{kandangNames.length > 0
|
||||||
|
? kandangNames.join(', ')
|
||||||
|
: 'Tidak ada'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'period',
|
||||||
|
header: 'Periode',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'created_at',
|
||||||
|
header: 'Dibuat pada',
|
||||||
|
cell: (props) =>
|
||||||
|
new Date(props.row.original.created_at).toLocaleDateString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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 = () => {
|
||||||
|
setSelectedProjectFlock(props.row.original);
|
||||||
|
deleteModal.openModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{currentPageSize > 2 && (
|
||||||
|
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
||||||
|
<RowOptionsMenu
|
||||||
|
type='dropdown'
|
||||||
|
props={props}
|
||||||
|
deleteClickHandler={deleteClickHandler}
|
||||||
|
/>
|
||||||
|
</RowDropdownOptions>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{currentPageSize <= 2 && (
|
||||||
|
<RowCollapseOptions>
|
||||||
|
<RowOptionsMenu
|
||||||
|
type='dropdown'
|
||||||
|
props={props}
|
||||||
|
deleteClickHandler={deleteClickHandler}
|
||||||
|
/>
|
||||||
|
</RowCollapseOptions>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
pageSize={tableFilterState.pageSize}
|
pageSize={tableFilterState.pageSize}
|
||||||
page={
|
page={
|
||||||
isResponseSuccess(projectFlocks) ? projectFlocks?.meta?.page : 0
|
isResponseSuccess(projectFlocks) ? projectFlocks?.meta?.page : 0
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
UpdateProjectFlockFormSchema,
|
UpdateProjectFlockFormSchema,
|
||||||
} from '@/components/pages/production/project-flock/form/ProjectFlockForm.schema';
|
} from '@/components/pages/production/project-flock/form/ProjectFlockForm.schema';
|
||||||
import {
|
import {
|
||||||
|
ProjectFlockApprovalPayload,
|
||||||
CreateProjectFlockPayload,
|
CreateProjectFlockPayload,
|
||||||
PeriodFlock,
|
PeriodFlock,
|
||||||
ProjectFlock,
|
ProjectFlock,
|
||||||
@@ -70,6 +71,18 @@ const ProjectFlockForm = ({
|
|||||||
|
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
||||||
|
const [isApprovedDisabled, setIsApprovedDisabled] = useState(initialValues?.approval.step_name == 'Pengajuan' ? false : true);
|
||||||
|
const [isRejectedDisabled, setIsRejectedDisabled] = useState(!isApprovedDisabled);
|
||||||
|
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(!isApprovedDisabled ? 'APPROVED' : 'REJECTED');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialValues?.approval?.step_name) {
|
||||||
|
const approvedDisabled = initialValues.approval.step_name !== 'Pengajuan';
|
||||||
|
setIsApprovedDisabled(approvedDisabled);
|
||||||
|
setIsRejectedDisabled(!approvedDisabled);
|
||||||
|
setApprovalAction(!approvedDisabled ? 'APPROVED' : 'REJECTED');
|
||||||
|
}
|
||||||
|
}, [initialValues]);
|
||||||
|
|
||||||
// Fetch Data
|
// Fetch Data
|
||||||
const flockUrl = `${FlockApi.basePath}?${new URLSearchParams({
|
const flockUrl = `${FlockApi.basePath}?${new URLSearchParams({
|
||||||
@@ -422,23 +435,39 @@ const ProjectFlockForm = ({
|
|||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalApproveClickHandler = async () => {
|
const confirmationModalClickHandler = async ({
|
||||||
|
action = 'APPROVED',
|
||||||
|
}: {
|
||||||
|
action: 'APPROVED' | 'REJECTED';
|
||||||
|
}) => {
|
||||||
|
if (initialValues?.id === undefined) return;
|
||||||
setIsApproveLoading(true);
|
setIsApproveLoading(true);
|
||||||
const approveProjectFlockRes = await ProjectFlockApi.customRequest<
|
const approveProjectFlockRes = await ProjectFlockApi.customRequest<
|
||||||
BaseApiResponse<ProjectFlock>,
|
BaseApiResponse<ProjectFlock>,
|
||||||
'POST'
|
ProjectFlockApprovalPayload
|
||||||
>(`/${initialValues?.id}/approve`, {
|
>(`/approvals`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
payload: {
|
||||||
|
action: action,
|
||||||
|
approvable_ids: [initialValues.id],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isResponseSuccess(approveProjectFlockRes)) {
|
if (isResponseSuccess(approveProjectFlockRes)) {
|
||||||
toast.success('Project Flock berhasil di-approve!');
|
if(action == 'APPROVED'){
|
||||||
confirmModal.closeModal();
|
setIsApprovedDisabled(true);
|
||||||
|
setIsRejectedDisabled(false);
|
||||||
|
}
|
||||||
|
if(action == 'REJECTED'){
|
||||||
|
setIsRejectedDisabled(true);
|
||||||
|
setIsApprovedDisabled(false);
|
||||||
|
}
|
||||||
|
toast.success(approveProjectFlockRes.message as string);
|
||||||
}
|
}
|
||||||
if (isResponseError(approveProjectFlockRes)) {
|
if (isResponseError(approveProjectFlockRes)) {
|
||||||
toast.error(approveProjectFlockRes?.message as string);
|
toast.error(approveProjectFlockRes?.message as string);
|
||||||
confirmModal.closeModal();
|
|
||||||
}
|
}
|
||||||
|
confirmModal.closeModal();
|
||||||
setIsApproveLoading(false);
|
setIsApproveLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -481,21 +510,43 @@ const ProjectFlockForm = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{formType == 'detail' && (
|
{formType == 'detail' && (
|
||||||
<div className='w-full py-4'>
|
<div className='w-full flex flex-col sm:flex-row gap-2 py-4'>
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
color='success'
|
color='success'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (initialValues?.id) {
|
if (initialValues?.id) {
|
||||||
|
setApprovalAction('APPROVED');
|
||||||
confirmModal.openModal();
|
confirmModal.openModal();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={!initialValues?.id}
|
disabled={
|
||||||
|
!initialValues?.id ||
|
||||||
|
isApprovedDisabled
|
||||||
|
}
|
||||||
className='w-full sm:w-fit'
|
className='w-full sm:w-fit'
|
||||||
>
|
>
|
||||||
<Icon icon='material-symbols:check' width={24} height={24} />
|
<Icon icon='material-symbols:check' width={24} height={24} />
|
||||||
Approve
|
Approve
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
color='error'
|
||||||
|
onClick={() => {
|
||||||
|
if (initialValues?.id) {
|
||||||
|
setApprovalAction('REJECTED');
|
||||||
|
confirmModal.openModal();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={
|
||||||
|
!initialValues?.id ||
|
||||||
|
isRejectedDisabled
|
||||||
|
}
|
||||||
|
className='w-full sm:w-fit'
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:times' width={24} height={24} />
|
||||||
|
Reject
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<form
|
<form
|
||||||
@@ -505,9 +556,7 @@ const ProjectFlockForm = ({
|
|||||||
>
|
>
|
||||||
<div className='card bg-base-100 shadow w-full mb-6'>
|
<div className='card bg-base-100 shadow w-full mb-6'>
|
||||||
<div className='card-body'>
|
<div className='card-body'>
|
||||||
<div className='card-title mb-4'>
|
<div className='card-title mb-4'>Informasi Umum</div>
|
||||||
Informasi Umum
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='grid sm:grid-cols-2 gap-4'>
|
<div className='grid sm:grid-cols-2 gap-4'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
@@ -614,7 +663,7 @@ const ProjectFlockForm = ({
|
|||||||
variant='link'
|
variant='link'
|
||||||
className={`text-primary rotate-${
|
className={`text-primary rotate-${
|
||||||
openSelectKandangs ? '180' : '0'
|
openSelectKandangs ? '180' : '0'
|
||||||
} transition-transform hover:text-inherit`}
|
} transition-transform hover:text-inherit me-3`}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon='material-symbols:keyboard-arrow-down'
|
icon='material-symbols:keyboard-arrow-down'
|
||||||
@@ -631,7 +680,7 @@ const ProjectFlockForm = ({
|
|||||||
>
|
>
|
||||||
<div className='overflow-x-auto'>
|
<div className='overflow-x-auto'>
|
||||||
{isLoadingKandang && (
|
{isLoadingKandang && (
|
||||||
<span className="loading loading-dots loading-xl"></span>
|
<span className='loading loading-dots loading-xl'></span>
|
||||||
)}
|
)}
|
||||||
<table className='table'>
|
<table className='table'>
|
||||||
{/* head */}
|
{/* head */}
|
||||||
@@ -673,7 +722,7 @@ const ProjectFlockForm = ({
|
|||||||
</label>
|
</label>
|
||||||
</th>
|
</th>
|
||||||
<th>Kandang</th>
|
<th>Kandang</th>
|
||||||
<th>Status</th>
|
{/* <th>Status</th> */}
|
||||||
<th>Penanggung Jawab</th>
|
<th>Penanggung Jawab</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -704,7 +753,7 @@ const ProjectFlockForm = ({
|
|||||||
</label>
|
</label>
|
||||||
</th>
|
</th>
|
||||||
<td>{kandang.name}</td>
|
<td>{kandang.name}</td>
|
||||||
<td>{kandang.status}</td>
|
{/* <td>{kandang.status}</td> */}
|
||||||
<td>{kandang.pic?.name}</td>
|
<td>{kandang.pic?.name}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
@@ -795,16 +844,20 @@ const ProjectFlockForm = ({
|
|||||||
|
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
ref={confirmModal.ref}
|
ref={confirmModal.ref}
|
||||||
type='success'
|
type={approvalAction == 'APPROVED' ? 'success' : 'error'}
|
||||||
text={`Apakah anda yakin ingin approve Project Flock berikut? (${initialValues?.flock?.name} - ${initialValues?.area?.name})?`}
|
text={`Apakah anda yakin ingin ${approvalAction == 'APPROVED' ? 'approve' : 'reject'} Project Flock berikut? (${initialValues?.flock?.name} - ${initialValues?.area?.name})?`}
|
||||||
secondaryButton={{
|
secondaryButton={{
|
||||||
text: 'Tidak',
|
text: 'Tidak',
|
||||||
}}
|
}}
|
||||||
primaryButton={{
|
primaryButton={{
|
||||||
text: 'Ya',
|
text: 'Ya',
|
||||||
color: 'success',
|
color: approvalAction == 'APPROVED' ? 'success' : 'error',
|
||||||
isLoading: isApproveLoading,
|
isLoading: isApproveLoading,
|
||||||
onClick: confirmationModalApproveClickHandler,
|
onClick: () => {
|
||||||
|
confirmationModalClickHandler({
|
||||||
|
action: approvalAction,
|
||||||
|
});
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Vendored
+9
@@ -104,3 +104,12 @@ export type ApprovalsLine = {
|
|||||||
role?: string;
|
role?: string;
|
||||||
status: 'approved' | 'rejected' | 'waiting';
|
status: 'approved' | 'rejected' | 'waiting';
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
|
export type BaseApproval = {
|
||||||
|
step_number: number;
|
||||||
|
step_name: string;
|
||||||
|
action: string;
|
||||||
|
notes: string | null;
|
||||||
|
action_by: CreatedUser;
|
||||||
|
action_at: string;
|
||||||
|
};
|
||||||
|
|||||||
+16
-11
@@ -1,9 +1,9 @@
|
|||||||
import { Area } from "@/types/api/master-data/area";
|
import { Area } from '@/types/api/master-data/area';
|
||||||
import { Fcr } from "@/types/api/master-data/fcr";
|
import { Fcr } from '@/types/api/master-data/fcr';
|
||||||
import { Flock } from "@/types/api/master-data/flock";
|
import { Flock } from '@/types/api/master-data/flock';
|
||||||
import { Kandang } from "@/types/api/master-data/kandang";
|
import { Kandang } from '@/types/api/master-data/kandang';
|
||||||
import { Location } from "@/types/api/master-data/location";
|
import { Location } from '@/types/api/master-data/location';
|
||||||
import { BaseMetadata } from "@/types/api/api-general";
|
import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
|
||||||
|
|
||||||
export type BaseProjectFlock = {
|
export type BaseProjectFlock = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -21,15 +21,15 @@ export type BaseProjectFlock = {
|
|||||||
period: number;
|
period: number;
|
||||||
kandang_ids: number[];
|
kandang_ids: number[];
|
||||||
kandangs: Kandang[];
|
kandangs: Kandang[];
|
||||||
}
|
approval: BaseApproval;
|
||||||
|
};
|
||||||
|
|
||||||
export type PeriodFlock = {
|
export type PeriodFlock = {
|
||||||
flock: Flock;
|
flock: Flock;
|
||||||
next_period: number;
|
next_period: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export type ProjectFlock = BaseMetadata & BaseProjectFlock;
|
||||||
export type ProjectFlock = BaseMetadata & BaseProjectFlock
|
|
||||||
|
|
||||||
export type CreateProjectFlockPayload = {
|
export type CreateProjectFlockPayload = {
|
||||||
flock_id: number;
|
flock_id: number;
|
||||||
@@ -39,6 +39,11 @@ export type CreateProjectFlockPayload = {
|
|||||||
location_id: number;
|
location_id: number;
|
||||||
period: number;
|
period: number;
|
||||||
kandang_ids: number[];
|
kandang_ids: number[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export type UpdateProjectFlockPayload = CreateProjectFlockPayload;
|
export type UpdateProjectFlockPayload = CreateProjectFlockPayload;
|
||||||
|
|
||||||
|
export type ProjectFlockApprovalPayload = {
|
||||||
|
action: 'APPROVED' | 'REJECTED';
|
||||||
|
approvable_ids: number[];
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user