mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-238-239-240): implement approval workflow chickin & project flock, membuat custom hook useApprovals, dan handling error format approvals
This commit is contained in:
@@ -16,8 +16,12 @@ export default function AddChickinKandang() {
|
||||
data: projectFlockKandang,
|
||||
isLoading: isLoading,
|
||||
mutate: refreshProjectFlockKandang,
|
||||
} = useSWR(projectFlockKandangId, (id: number) =>
|
||||
ProjectFlockKandangApi.getSingle(id)
|
||||
} = useSWR(
|
||||
`get-single-project-flock-kandang/${projectFlockKandangId}`,
|
||||
async () =>
|
||||
ProjectFlockKandangApi.getSingle(
|
||||
parseInt(projectFlockKandangId as string)
|
||||
)
|
||||
);
|
||||
|
||||
if (!projectFlockKandangId) {
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
|
||||
import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { FlockApi } from '@/services/api/master-data';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
const ProjectFlockDetail = () => {
|
||||
@@ -15,22 +12,12 @@ const ProjectFlockDetail = () => {
|
||||
|
||||
const projectFlockId = searchParams.get('projectFlockId');
|
||||
|
||||
const [projectName, setProjectName] = useState();
|
||||
|
||||
const {
|
||||
data: projectFlock,
|
||||
isLoading: isLoadingProjectFlock,
|
||||
mutate: refreshProjectFlock,
|
||||
} = useSWR(projectFlockId, (id: number) => ProjectFlockApi.getSingle(id));
|
||||
|
||||
const {
|
||||
data: approvalLines,
|
||||
isLoading: isLoadingApprovalLines,
|
||||
mutate: refreshApprovalLines,
|
||||
} = useSWR('approvals', (id: number) =>
|
||||
ProjectFlockApi.getApprovalLines((projectFlockId ?? 0) as number)
|
||||
);
|
||||
|
||||
if (!projectFlockId) {
|
||||
router.back();
|
||||
|
||||
@@ -51,15 +38,13 @@ const ProjectFlockDetail = () => {
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-col justify-center'>
|
||||
{isLoadingProjectFlock ||
|
||||
(isLoadingApprovalLines && (
|
||||
{isLoadingProjectFlock && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
))}
|
||||
{isResponseSuccess(projectFlock) && isResponseSuccess(approvalLines) && (
|
||||
)}
|
||||
{isResponseSuccess(projectFlock) && (
|
||||
<ProjectFlockForm
|
||||
formType='detail'
|
||||
initialValues={projectFlock.data}
|
||||
initialApprovals={approvalLines.data}
|
||||
refreshProjectFlocks={refreshProjectFlock}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -16,7 +16,7 @@ export const useModal = () => {
|
||||
|
||||
const openModal = useCallback(() => {
|
||||
if (!ref.current) return;
|
||||
ref.current.showModal();
|
||||
ref.current.show();
|
||||
setOpen(true);
|
||||
}, []);
|
||||
|
||||
@@ -30,7 +30,6 @@ export const useModal = () => {
|
||||
open ? closeModal() : openModal();
|
||||
}, [open, closeModal, openModal]);
|
||||
|
||||
// Gunakan useEffect agar event listener tidak didaftarkan berulang kali
|
||||
useEffect(() => {
|
||||
const dialog = ref.current;
|
||||
if (!dialog) return;
|
||||
@@ -48,7 +47,6 @@ export const useModal = () => {
|
||||
|
||||
interface ModalProps {
|
||||
ref: RefObject<HTMLDialogElement | null>;
|
||||
id?: string;
|
||||
children?: ReactNode;
|
||||
closeOnBackdrop?: boolean;
|
||||
className?: {
|
||||
@@ -57,13 +55,7 @@ interface ModalProps {
|
||||
};
|
||||
}
|
||||
|
||||
const Modal = ({
|
||||
ref,
|
||||
id,
|
||||
children,
|
||||
closeOnBackdrop,
|
||||
className,
|
||||
}: ModalProps) => {
|
||||
const Modal = ({ ref, children, closeOnBackdrop, className }: ModalProps) => {
|
||||
const handleBackdropClick = (e: React.MouseEvent<HTMLDialogElement>) => {
|
||||
if (closeOnBackdrop && e.target === ref.current) {
|
||||
ref.current?.close();
|
||||
@@ -73,7 +65,6 @@ const Modal = ({
|
||||
return (
|
||||
<dialog
|
||||
ref={ref}
|
||||
id={id}
|
||||
className={cn('modal', className?.modal)}
|
||||
onClick={handleBackdropClick}
|
||||
>
|
||||
|
||||
@@ -4,8 +4,17 @@ import StepItem from '@/components/steps/StepItem';
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
|
||||
import { cn, formatDate } from '@/lib/helper';
|
||||
import { BaseApproval, BaseGroupedApproval } from '@/types/api/api-general';
|
||||
import { ApprovalLine } from '@/types/config/constant';
|
||||
import {
|
||||
BaseApiResponse,
|
||||
BaseApproval,
|
||||
BaseGroupedApproval,
|
||||
ModuleWithApproval,
|
||||
} from '@/types/api/api-general';
|
||||
import { AppConfigData, ApprovalLine } from '@/types/config/constant';
|
||||
import useSWR from 'swr';
|
||||
import { httpClientFetcher } from '@/services/http/client';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
export type ApprovalStepStatus = 'APPROVED' | 'REJECTED' | 'WAITING' | 'IDLE';
|
||||
|
||||
@@ -120,7 +129,7 @@ export const formatGroupedApprovalsToApprovalSteps = (
|
||||
|
||||
const currentStepNumber = approvalLineItem.step_number;
|
||||
const lastStepNumber =
|
||||
groupedApprovals[groupedApprovals.length - 1].step_number;
|
||||
groupedApprovals[groupedApprovals.length - 1]?.step_number;
|
||||
|
||||
if (!approvalGroup && currentStepNumber <= lastStepNumber) {
|
||||
throw new Error(
|
||||
@@ -137,10 +146,11 @@ export const formatGroupedApprovalsToApprovalSteps = (
|
||||
};
|
||||
}
|
||||
|
||||
let approvalStatus: ApprovalStepStatus;
|
||||
let approvalStatus: ApprovalStepStatus = 'IDLE';
|
||||
|
||||
if (approvalGroup.step_number <= latestApproval.step_number) {
|
||||
switch (approvalGroup.approvals[0].action) {
|
||||
if (approvalGroup.approvals) {
|
||||
switch (approvalGroup?.approvals[0]?.action) {
|
||||
case 'CREATED':
|
||||
case 'APPROVED':
|
||||
approvalStatus = 'APPROVED';
|
||||
@@ -154,19 +164,20 @@ export const formatGroupedApprovalsToApprovalSteps = (
|
||||
approvalStatus = 'IDLE';
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (approvalGroup.step_number === latestApproval.step_number + 1) {
|
||||
approvalStatus = 'WAITING';
|
||||
} else {
|
||||
approvalStatus = 'IDLE';
|
||||
}
|
||||
|
||||
const approvalLogs: ApprovalStepLog[] = approvalGroup.approvals.map(
|
||||
(approval) => ({
|
||||
const approvalLogs: ApprovalStepLog[] = approvalGroup.approvals
|
||||
? approvalGroup.approvals.map((approval) => ({
|
||||
action_by: approval.action_by.name,
|
||||
date: approval.action_at,
|
||||
notes: approval.notes,
|
||||
})
|
||||
);
|
||||
}))
|
||||
: [];
|
||||
|
||||
return {
|
||||
name: approvalGroup.step_name,
|
||||
@@ -179,3 +190,113 @@ export const formatGroupedApprovalsToApprovalSteps = (
|
||||
};
|
||||
|
||||
export default ApprovalSteps;
|
||||
|
||||
const useApprovalSteps = <T extends ModuleWithApproval>({
|
||||
moduleUrl,
|
||||
moduleName,
|
||||
moduleId,
|
||||
params,
|
||||
}: {
|
||||
moduleUrl: string;
|
||||
moduleName: string;
|
||||
moduleId: string;
|
||||
params?: {
|
||||
page: number;
|
||||
limit: number;
|
||||
search?: string;
|
||||
};
|
||||
}) => {
|
||||
const paramString = new URLSearchParams({
|
||||
page: params?.page?.toString() || '',
|
||||
limit: params?.limit?.toString() || '',
|
||||
search: params?.search || '',
|
||||
}).toString();
|
||||
|
||||
const SWR_KEY_CONSTANTS = '/constants';
|
||||
const SWR_KEY_APPROVALS =
|
||||
moduleName && moduleId
|
||||
? `/approvals?module_name=${moduleName}&module_id=${moduleId}&group_step_number=true${
|
||||
params ? `&${paramString}` : ''
|
||||
}`
|
||||
: null;
|
||||
const SWR_KEY_CURRENT_DATA = moduleUrl;
|
||||
|
||||
// Get Approval Lines dari GET /constant
|
||||
const { data: constData, isLoading: constIsLoading } = useSWR(
|
||||
SWR_KEY_CONSTANTS,
|
||||
async (url) => {
|
||||
return await httpClientFetcher<AppConfigData>(url);
|
||||
}
|
||||
);
|
||||
|
||||
// Get Grouped Data dari GET /approvals
|
||||
const {
|
||||
data: approvalData,
|
||||
isLoading: approvalIsLoading,
|
||||
mutate: mutateApprovals,
|
||||
} = useSWR(SWR_KEY_APPROVALS, async (url) => {
|
||||
return await httpClientFetcher<BaseApiResponse<BaseGroupedApproval[]>>(url);
|
||||
});
|
||||
|
||||
// Get latest approval
|
||||
const {
|
||||
data: currentData,
|
||||
isLoading: currentIsLoading,
|
||||
mutate: mutateCurrentData,
|
||||
} = useSWR(SWR_KEY_CURRENT_DATA, async (url) => {
|
||||
return await httpClientFetcher<BaseApiResponse<T>>(url);
|
||||
});
|
||||
|
||||
// Fungsi Refresh
|
||||
const refresh = useCallback(async () => {
|
||||
await Promise.all([mutateApprovals(), mutateCurrentData()]);
|
||||
}, [mutateApprovals, mutateCurrentData]);
|
||||
|
||||
const { approvalLine, groupedApprovals, latestApproval } = useMemo(() => {
|
||||
const line = constData
|
||||
? (constData.approval_workflows.find((approval) => {
|
||||
return approval.key === moduleName;
|
||||
})?.steps ?? [])
|
||||
: [];
|
||||
|
||||
const grouped = isResponseSuccess(approvalData) ? approvalData.data : [];
|
||||
|
||||
const latest = isResponseSuccess(currentData)
|
||||
? currentData.data?.approval
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
approvalLine: line,
|
||||
groupedApprovals: grouped,
|
||||
latestApproval: latest,
|
||||
};
|
||||
}, [constData, approvalData, currentData, moduleName]);
|
||||
|
||||
const isLoading = constIsLoading || approvalIsLoading || currentIsLoading;
|
||||
|
||||
const approvals = useMemo(() => {
|
||||
if (isLoading || !approvalLine.length || !latestApproval) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
return formatGroupedApprovalsToApprovalSteps(
|
||||
approvalLine,
|
||||
groupedApprovals,
|
||||
latestApproval as BaseApproval
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn('Gagal memformat approval steps:', error);
|
||||
return [];
|
||||
}
|
||||
}, [isLoading, approvalLine, groupedApprovals, latestApproval]);
|
||||
|
||||
// Return Hook
|
||||
return {
|
||||
approvals,
|
||||
isLoading,
|
||||
rawData: isResponseSuccess(currentData) ? currentData.data : undefined,
|
||||
refresh,
|
||||
};
|
||||
};
|
||||
|
||||
export { useApprovalSteps };
|
||||
|
||||
@@ -10,6 +10,9 @@ import Tabs from '@/components/Tabs';
|
||||
import ChickinFormView from './tabs/ChickinFormView';
|
||||
import ChickinLogsView from './tabs/ChickLogsView';
|
||||
import { useState } from 'react';
|
||||
import ApprovalSteps, {
|
||||
useApprovalSteps,
|
||||
} from '@/components/pages/ApprovalSteps';
|
||||
const ChickinFormKandang = ({
|
||||
formType = 'add',
|
||||
initialValues,
|
||||
@@ -21,9 +24,20 @@ const ChickinFormKandang = ({
|
||||
}) => {
|
||||
const [activeTabId, setActiveTabId] = useState<string>('formChickIn');
|
||||
|
||||
const {
|
||||
approvals,
|
||||
isLoading: approvalsLoading,
|
||||
refresh: refreshApprovals,
|
||||
} = useApprovalSteps({
|
||||
moduleUrl: `/production/project-flock-kandangs/${initialValues?.id}`,
|
||||
moduleName: 'PROJECT_FLOCK_KANDANGS',
|
||||
moduleId: initialValues?.id.toString() ?? '',
|
||||
});
|
||||
|
||||
const afterSubmitFormChickin = () => {
|
||||
setActiveTabId('logsChickIn');
|
||||
afterSubmit && afterSubmit();
|
||||
refreshApprovals();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -32,6 +46,11 @@ const ChickinFormKandang = ({
|
||||
title='Chick In DOC'
|
||||
backUrl={`/production/project-flock/chickin/add?projectFlockId=${initialValues?.project_flock?.id}`}
|
||||
/>
|
||||
|
||||
{approvals && !approvalsLoading && (
|
||||
<ApprovalSteps approvals={approvals} />
|
||||
)}
|
||||
|
||||
<Card
|
||||
title='Informasi Kandang'
|
||||
className={{
|
||||
@@ -108,7 +127,7 @@ const ChickinFormKandang = ({
|
||||
content: (
|
||||
<ChickinLogsView
|
||||
initialValues={initialValues}
|
||||
afterSubmit={afterSubmit}
|
||||
afterSubmit={afterSubmitFormChickin}
|
||||
/>
|
||||
),
|
||||
id: 'logsChickIn',
|
||||
|
||||
@@ -47,9 +47,7 @@ const ChickinLogsView = ({
|
||||
}
|
||||
confirmModal.closeModal();
|
||||
setIsApproveLoading(false);
|
||||
if (afterSubmit) {
|
||||
afterSubmit();
|
||||
}
|
||||
afterSubmit && afterSubmit();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
+13
-2
@@ -58,7 +58,15 @@ const ProjectFlockChickinDetail = ({
|
||||
options: options,
|
||||
isLoadingOptions: isLoadingListProjectFlock,
|
||||
rawData: listProjectFlock,
|
||||
} = useSelect<ProjectFlock>(ProjectFlockApi.basePath, 'id', 'flock_name');
|
||||
} = useSelect<ProjectFlock>(
|
||||
ProjectFlockApi.basePath,
|
||||
'id',
|
||||
'flock_name',
|
||||
'',
|
||||
{
|
||||
search: searchProjectFlock,
|
||||
}
|
||||
);
|
||||
|
||||
// Handle Function
|
||||
const handleChickinClick = async (
|
||||
@@ -242,7 +250,7 @@ const ProjectFlockChickinDetail = ({
|
||||
</div>
|
||||
}
|
||||
data={
|
||||
isResponseSuccess(listProjectFlockKandang)
|
||||
projectFlock && isResponseSuccess(listProjectFlockKandang)
|
||||
? listProjectFlockKandang.data
|
||||
: []
|
||||
}
|
||||
@@ -293,6 +301,8 @@ const ProjectFlockChickinDetail = ({
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase())}
|
||||
/>
|
||||
) : projectFlock?.approval?.step_number === 1 ? (
|
||||
<PillBadge color='red' content={'Tidak Dapat Chick In'} />
|
||||
) : (
|
||||
<PillBadge color='gray' content={'Belum Chick In'} />
|
||||
);
|
||||
@@ -310,6 +320,7 @@ const ProjectFlockChickinDetail = ({
|
||||
handleChickinClick(props.row.original);
|
||||
}}
|
||||
className='p-1'
|
||||
disabled={projectFlock?.approval?.step_number === 1}
|
||||
>
|
||||
<Icon
|
||||
icon='mdi:home-import-outline'
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import { Icon } from '@iconify/react';
|
||||
import { useFormik } from 'formik';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { use, useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import useSWR, { KeyedMutator } from 'swr';
|
||||
import {
|
||||
ProjectFlockFormSchema,
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
import {
|
||||
ProjectFlockApprovalPayload,
|
||||
CreateProjectFlockPayload,
|
||||
PeriodFlock,
|
||||
ProjectFlock,
|
||||
} from '@/types/api/production/project-flock';
|
||||
import toast from 'react-hot-toast';
|
||||
@@ -34,23 +33,19 @@ import TextInput from '@/components/input/TextInput';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import Collapse from '@/components/Collapse';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { BaseApiResponse, BaseGroupedApproval } from '@/types/api/api-general';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { APPROVAL_WORKFLOWS, FLOCK_CATEGORY_OPTIONS } from '@/config/constant';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import ProjectFlockKandangTable from './ProjectFlockKandangTable';
|
||||
import ApprovalSteps from '@/components/pages/ApprovalSteps';
|
||||
import Steps from '@/components/steps/Steps';
|
||||
import StepItem from '@/components/steps/StepItem';
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
import { id, is } from 'react-day-picker/locale';
|
||||
import { formatDate } from '@/lib/helper';
|
||||
import ApprovalSteps, {
|
||||
useApprovalSteps,
|
||||
} from '@/components/pages/ApprovalSteps';
|
||||
import Card from '@/components/Card';
|
||||
|
||||
interface ProjectFlockFormProps {
|
||||
formType?: 'add' | 'edit' | 'detail';
|
||||
initialValues?: ProjectFlock;
|
||||
initialApprovals?: BaseGroupedApproval[];
|
||||
refreshProjectFlocks?: KeyedMutator<
|
||||
BaseApiResponse<ProjectFlock> | undefined
|
||||
>;
|
||||
@@ -59,16 +54,11 @@ interface ProjectFlockFormProps {
|
||||
const ProjectFlockForm = ({
|
||||
formType = 'add',
|
||||
initialValues,
|
||||
initialApprovals,
|
||||
refreshProjectFlocks,
|
||||
}: ProjectFlockFormProps) => {
|
||||
// State
|
||||
const router = useRouter();
|
||||
|
||||
const projectFlockSteps = APPROVAL_WORKFLOWS.find(
|
||||
(step) => step.key === 'PROJECT_FLOCKS'
|
||||
);
|
||||
|
||||
const [projectFlockFormErrorMessage, setProjectFlockFormErrorMessage] =
|
||||
useState('');
|
||||
const [selectedArea, setSelectedArea] = useState('');
|
||||
@@ -120,34 +110,28 @@ const ProjectFlockForm = ({
|
||||
}, [initialValues]);
|
||||
|
||||
// Fetch Data
|
||||
const {
|
||||
rawData: flocks,
|
||||
isLoadingOptions: isLoadingFlocks,
|
||||
options: optionsFlock,
|
||||
} = useSelect(FlockApi.basePath, 'id', 'name');
|
||||
const { isLoadingOptions: isLoadingFlocks, options: optionsFlock } =
|
||||
useSelect(FlockApi.basePath, 'id', 'name');
|
||||
|
||||
const {
|
||||
options: optionsArea,
|
||||
isLoadingOptions: isLoadingAreas,
|
||||
rawData: areas,
|
||||
} = useSelect(AreaApi.basePath, 'id', 'name');
|
||||
const { options: optionsArea, isLoadingOptions: isLoadingAreas } = useSelect(
|
||||
AreaApi.basePath,
|
||||
'id',
|
||||
'name'
|
||||
);
|
||||
|
||||
const {
|
||||
options: optionsLocation,
|
||||
isLoadingOptions: isLoadingLocations,
|
||||
rawData: locations,
|
||||
} = useSelect(LocationApi.basePath, 'id', 'name', '', {
|
||||
const { options: optionsLocation, isLoadingOptions: isLoadingLocations } =
|
||||
useSelect(LocationApi.basePath, 'id', 'name', '', {
|
||||
area_id:
|
||||
selectedArea != ''
|
||||
? selectedArea
|
||||
: ((initialValues?.area?.id ?? '') as string),
|
||||
});
|
||||
|
||||
const {
|
||||
options: optionsFcr,
|
||||
isLoadingOptions: isLoadingFcrs,
|
||||
rawData: fcrs,
|
||||
} = useSelect(FcrApi.basePath, 'id', 'name');
|
||||
const { options: optionsFcr, isLoadingOptions: isLoadingFcrs } = useSelect(
|
||||
FcrApi.basePath,
|
||||
'id',
|
||||
'name'
|
||||
);
|
||||
|
||||
const kandangUrl = `${KandangApi.basePath}?${new URLSearchParams({
|
||||
search: '',
|
||||
@@ -164,6 +148,16 @@ const ProjectFlockForm = ({
|
||||
(id: string) => ProjectFlockApi.getNextPeriod(id)
|
||||
);
|
||||
|
||||
const {
|
||||
approvals,
|
||||
isLoading: approvalsLoading,
|
||||
refresh: refreshApprovals,
|
||||
} = useApprovalSteps({
|
||||
moduleUrl: `/production/project-flocks/${initialValues?.id}`,
|
||||
moduleName: 'PROJECT_FLOCKS',
|
||||
moduleId: initialValues?.id.toString() ?? '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isResponseSuccess(kandang)) {
|
||||
if (selectedLocation) {
|
||||
@@ -516,6 +510,7 @@ const ProjectFlockForm = ({
|
||||
if (isResponseError(approveProjectFlockRes)) {
|
||||
toast.error(approveProjectFlockRes?.message as string);
|
||||
}
|
||||
refreshApprovals();
|
||||
confirmModal.closeModal();
|
||||
setIsApproveLoading(false);
|
||||
};
|
||||
@@ -558,79 +553,8 @@ const ProjectFlockForm = ({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{formType == 'detail' && initialApprovals && (
|
||||
<div className='w-full flex items-center gap-2 my-4'>
|
||||
<Steps className='w-full'>
|
||||
{projectFlockSteps?.steps.map((step, idx) => {
|
||||
const approvalLogs = initialApprovals.find(
|
||||
(approve) => approve.step_number == step.step_number
|
||||
);
|
||||
return (
|
||||
<StepItem
|
||||
key={step.step_number}
|
||||
color={
|
||||
step.step_number <=
|
||||
(initialValues?.approval.step_number ?? 0)
|
||||
? 'success'
|
||||
: undefined
|
||||
}
|
||||
icon={
|
||||
<Tooltip
|
||||
color={
|
||||
step.step_number <=
|
||||
(initialValues?.approval.step_number ?? 0)
|
||||
? 'success'
|
||||
: undefined
|
||||
}
|
||||
position='bottom'
|
||||
content={
|
||||
<ul>
|
||||
{approvalLogs &&
|
||||
approvalLogs?.approvals?.map((approval, idx) => {
|
||||
return (
|
||||
<li className='mb-2' key={`key-logs-${idx}`}>
|
||||
<div className='flex flex-col w-full text-start text-base'>
|
||||
<span>Status: {approval.step_name}</span>
|
||||
<span>
|
||||
Oleh: {approval.action_by.name}
|
||||
</span>
|
||||
<span>
|
||||
Tanggal:{' '}
|
||||
{formatDate(
|
||||
approval.action_at,
|
||||
'DD-MM-yyyy HH:mm:ss'
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
}
|
||||
>
|
||||
{step.step_number <=
|
||||
(initialValues?.approval.step_number ?? 0) ? (
|
||||
<Icon
|
||||
icon='material-symbols:check-rounded'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
icon='material-symbols:check-rounded'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
)}
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
{step.step_name}
|
||||
</StepItem>
|
||||
);
|
||||
})}
|
||||
</Steps>
|
||||
</div>
|
||||
{approvals && !approvalsLoading && (
|
||||
<ApprovalSteps approvals={approvals} />
|
||||
)}
|
||||
{formType == 'detail' && (
|
||||
<div className='w-full flex flex-col sm:flex-row gap-2 py-4'>
|
||||
@@ -675,32 +599,12 @@ const ProjectFlockForm = ({
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Icon icon='mdi:home-import-outline' width={18} height={18} />
|
||||
Chickin
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Card
|
||||
className={{
|
||||
body: 'text-primary',
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(formik.values)}
|
||||
</Card>
|
||||
<Card
|
||||
className={{
|
||||
body: 'text-success',
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(formik.initialValues)}
|
||||
</Card>
|
||||
<Card
|
||||
className={{
|
||||
body: 'text-error',
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(formik.errors)}
|
||||
</Card>
|
||||
<form
|
||||
className='w-auto h-auto'
|
||||
onSubmit={formik.handleSubmit}
|
||||
|
||||
@@ -36,7 +36,6 @@ const ProjectFlockKandangTable = ({
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{JSON.stringify(initialKandangIdSet)}
|
||||
<Table<Kandang>
|
||||
data={listKandang}
|
||||
columns={[
|
||||
|
||||
@@ -246,6 +246,19 @@ export const APPROVAL_WORKFLOWS = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_FLOCK_KANDANGS',
|
||||
steps: [
|
||||
{
|
||||
step_number: 1,
|
||||
step_name: 'Pengajuan',
|
||||
},
|
||||
{
|
||||
step_number: 2,
|
||||
step_name: 'Disetujui',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'RECORDINGS',
|
||||
steps: [
|
||||
|
||||
@@ -39,11 +39,6 @@ export class ProjectFlockService extends BaseApiService<
|
||||
> {
|
||||
const path = `/approvals`;
|
||||
try {
|
||||
console.log({
|
||||
module_id: id,
|
||||
module_name: 'PROJECT_FLOCKS',
|
||||
group_step_number: true,
|
||||
});
|
||||
return await httpClient<SuccessApiResponse<BaseGroupedApproval[]>>(path, {
|
||||
method: 'GET',
|
||||
query: {
|
||||
|
||||
Vendored
+4
@@ -112,6 +112,10 @@ export type BaseGroupedApproval = {
|
||||
approvals: BaseApproval[];
|
||||
};
|
||||
|
||||
interface ModuleWithApproval {
|
||||
approval?: BaseApproval;
|
||||
}
|
||||
|
||||
export type Approvals = BaseApiResponse<BaseApproval>;
|
||||
|
||||
export type GroupedApprovals = BaseApiResponse<BaseGroupedApproval[]>;
|
||||
|
||||
Vendored
+14
@@ -2,3 +2,17 @@ export type ApprovalLine = {
|
||||
step_number: number;
|
||||
step_name: string;
|
||||
}[];
|
||||
|
||||
export interface ApprovalWorkflow {
|
||||
key: string;
|
||||
steps: ApprovalLine;
|
||||
}
|
||||
|
||||
export interface AppConfigData {
|
||||
approval_workflows: ApprovalWorkflow[];
|
||||
flags: string[];
|
||||
warehouse_types: string[];
|
||||
stock_log: string;
|
||||
supplier_categories: string[];
|
||||
customer_supplier_types: string[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user