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