diff --git a/src/components/helper/form/FormErrors.tsx b/src/components/helper/form/FormErrors.tsx new file mode 100644 index 00000000..a351227f --- /dev/null +++ b/src/components/helper/form/FormErrors.tsx @@ -0,0 +1,46 @@ +import Alert from '@/components/Alert'; +import Button from '@/components/Button'; +import { Icon } from '@iconify/react'; + +/** + * Alert Unique Error List + * @param formErrorList - Array of error messages + * @param onClose - Function to close the alert + */ +const AlertErrorList = ({ + formErrorList, + onClose, +}: { + formErrorList: string[]; + onClose: () => void; +}) => { + return ( + +
+
+ + + Terdapat {formErrorList.length} error pada form: + +
+ +
+ +
+ ); +}; + +export default AlertErrorList; diff --git a/src/components/pages/ApprovalSteps.tsx b/src/components/pages/ApprovalSteps.tsx index 6ae7c13a..27d03da3 100644 --- a/src/components/pages/ApprovalSteps.tsx +++ b/src/components/pages/ApprovalSteps.tsx @@ -309,7 +309,7 @@ const useApprovalSteps = ({ moduleId: string; params?: { page?: number; - limit: number; + limit: number | string; search?: string; group_step_number?: boolean; }; diff --git a/src/components/pages/inventory/adjustment/form/InventoryAdjustmentForm.tsx b/src/components/pages/inventory/adjustment/form/InventoryAdjustmentForm.tsx index f134369e..525b81bb 100644 --- a/src/components/pages/inventory/adjustment/form/InventoryAdjustmentForm.tsx +++ b/src/components/pages/inventory/adjustment/form/InventoryAdjustmentForm.tsx @@ -125,6 +125,7 @@ const InventoryAdjustmentForm = ({ const warehouseUrl = `${WarehouseApi.basePath}?${new URLSearchParams({ search: '', + limit: '100', }).toString()}`; const { data: warehouses, isLoading: isLoadingWarehouses } = useSWR( warehouseUrl, diff --git a/src/components/pages/production/chickin/form/ChickinForm.tsx b/src/components/pages/production/chickin/form/ChickinForm.tsx index 8c613737..b5b1dc4d 100644 --- a/src/components/pages/production/chickin/form/ChickinForm.tsx +++ b/src/components/pages/production/chickin/form/ChickinForm.tsx @@ -18,6 +18,7 @@ import { Icon } from '@iconify/react'; import Badge from '@/components/Badge'; import { CHICKINS_APPROVAL_LINE } from '@/config/approval-line'; import RequirePermission from '@/components/helper/RequirePermission'; +import { BaseApproval } from '@/types/api/api-general'; const ChickinFormKandang = ({ formType = 'add', initialValues, @@ -33,11 +34,16 @@ const ChickinFormKandang = ({ approvals, isLoading: approvalsLoading, refresh: refreshApprovals, + rawDataApprovals, } = useApprovalSteps({ latestApproval: initialValues?.chickin_approval, approvalLines: CHICKINS_APPROVAL_LINE, moduleName: 'CHICKINS', moduleId: initialValues?.id.toString() ?? '', + params: { + limit: 'limit', + group_step_number: false, + }, }); const afterSubmitFormChickin = () => { @@ -180,6 +186,7 @@ const ChickinFormKandang = ({ {openChickin && ( diff --git a/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx b/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx index 80846565..e800ee68 100644 --- a/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx +++ b/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx @@ -8,6 +8,7 @@ import PillBadge from '@/components/PillBadge'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { formatDate, formatNumber } from '@/lib/helper'; import { ChickinApi } from '@/services/api/production/chickin'; +import { BaseApproval } from '@/types/api/api-general'; import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; import { Icon } from '@iconify/react'; import { useState } from 'react'; @@ -16,9 +17,11 @@ import toast from 'react-hot-toast'; const ChickinLogsView = ({ initialValues, afterSubmit, + rawDataApprovals, }: { initialValues: ProjectFlockKandang; afterSubmit?: () => void; + rawDataApprovals: BaseApproval[]; }) => { const confirmModal = useModal(); const [isApproveLoading, setIsApproveLoading] = useState(false); @@ -60,9 +63,15 @@ const ChickinLogsView = ({ ) : ( (initialValues?.chickins || []).map((chickin, index) => { + const latestApproval = rawDataApprovals[0]; const isApproved = - initialValues.chickin_approval?.step_number === 2; - const isPending = initialValues.chickin_approval?.step_number === 1; + index == (initialValues?.chickins || []).length - 1 + ? latestApproval?.step_number === 2 + : true; + const isPending = + index == (initialValues?.chickins || []).length - 1 + ? latestApproval?.step_number === 1 + : false; const quantity = isApproved ? chickin.usage_qty : isPending @@ -82,7 +91,7 @@ const ChickinLogsView = ({ {/* Header with Status Badge */}
- Chick In #{index + 1} + Chick In #{index + 1} - {latestApproval?.step_number}
= diff --git a/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx b/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx index 20a8aff4..7e90c94b 100644 --- a/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx +++ b/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx @@ -6,6 +6,8 @@ import SelectInput, { useSelect, } from '@/components/input/SelectInput'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; +import { getUniqueFormikErrors } from '@/lib/formik-helper'; +import AlertErrorList from '@/components/helper/form/FormErrors'; import { AreaApi, FcrApi, @@ -64,6 +66,7 @@ const ProjectFlockForm = ({ const [projectFlockFormErrorMessage, setProjectFlockFormErrorMessage] = useState(''); + const [formErrorList, setFormErrorList] = useState([]); const [selectedArea, setSelectedArea] = useState(''); const [selectedLocation, setSelectedLocation] = useState(''); const [selectedCategory, setSelectedCategory] = useState(''); @@ -134,6 +137,7 @@ const ProjectFlockForm = ({ const kandangUrl = `${KandangApi.basePath}?${new URLSearchParams({ search: '', location_id: selectedLocation == '' ? '0' : selectedLocation, + limit: 'limit', }).toString()}`; const { data: kandang, @@ -638,6 +642,17 @@ const ProjectFlockForm = ({ return !isNonstockAlreadyInBudgets; }); + const handleValidateForm = async () => { + const errors = await formik.validateForm(); + + if (Object.keys(errors).length > 0) { + // Parse and display errors + const errorMessages = getUniqueFormikErrors(errors); + setFormErrorList(errorMessages); + return; // Stop submission + } + }; + return ( <>
@@ -697,7 +712,11 @@ const ProjectFlockForm = ({
{ + e.preventDefault(); + handleValidateForm(); + formik.handleSubmit(e); + }} onReset={formik.handleReset} > {/* Form Informasi Umum */} @@ -1063,6 +1082,14 @@ const ProjectFlockForm = ({
+ {/* Error List Alert */} + {formErrorList.length > 0 && ( + setFormErrorList([])} + /> + )} +
{formType !== 'detail' && ( ( + errors: FormikErrors, + parentKey: string = '' +): ErrorMessage[] { + const errorList: ErrorMessage[] = []; + + Object.keys(errors).forEach((key) => { + const value = errors[key as keyof typeof errors]; + const fullKey = parentKey ? `${parentKey}.${key}` : key; + + if (typeof value === 'string') { + // Direct error message + errorList.push({ key: fullKey, message: value }); + } else if (Array.isArray(value)) { + // Array of errors + value.forEach((item, index) => { + if (typeof item === 'string') { + errorList.push({ key: `${fullKey}[${index}]`, message: item }); + } else if (item && typeof item === 'object') { + // Nested object in array + const nestedErrors = parseFormikErrors( + item as FormikErrors, + `${fullKey}[${index}]` + ); + errorList.push(...nestedErrors); + } + }); + } else if (value && typeof value === 'object') { + // Nested object + const nestedErrors = parseFormikErrors( + value as FormikErrors, + fullKey + ); + errorList.push(...nestedErrors); + } + }); + + return errorList; +} + +/** + * Get unique error messages from Formik errors + * @param errors - Formik errors object + * @returns Array of unique error messages + */ +export function getUniqueFormikErrors(errors: FormikErrors): string[] { + const errorList = parseFormikErrors(errors); + return Array.from(new Set(errorList.map((e) => e.message))); +} + +/** + * Get all error messages from Formik errors + * @param errors - Formik errors object + * @returns Array of error messages + */ +export function getAllFormikErrors(errors: FormikErrors): ErrorMessage[] { + return parseFormikErrors(errors); +}