Merge branch 'dev/randy' into 'fix/submit-form'

[FIX/FE] Create Custom Hooks for Formik Error List

See merge request mbugroup/lti-web-client!158
This commit is contained in:
Rivaldi A N S
2026-01-12 10:34:36 +00:00
33 changed files with 301 additions and 266 deletions
+4 -1
View File
@@ -1,6 +1,7 @@
import Alert from '@/components/Alert';
import Button from '@/components/Button';
import { Icon } from '@iconify/react';
import { useState } from 'react';
/**
* Alert Unique Error List
@@ -14,8 +15,10 @@ const AlertErrorList = ({
formErrorList: string[];
onClose: () => void;
}) => {
if (formErrorList.length === 0) return null;
return (
<Alert color='error' className='flex flex-col gap-2 px-4 m-4'>
<Alert color='error' className='w-full flex flex-col gap-2 px-4 m-4'>
<div className='flex justify-between items-center gap-2 w-full'>
<div className='flex items-center gap-2'>
<Icon icon='material-symbols:error-outline' width={24} height={24} />
@@ -22,7 +22,6 @@ import HppExpeditionReportTable from './hpp-ekspedisi/HppExpeditionReportTable';
import ClosingKandangList from '@/components/pages/closing/ClosingKandangList';
import { ProjectFlock } from '@/types/api/production/project-flock';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
interface ClosingDetailProps {
id: number;
initialValue?: ClosingGeneralInformation;
@@ -56,7 +55,6 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
<ClosingSapronakCalculationTabContent
closingGeneralInformation={initialValue}
projectFlockId={id}
projectKandangId={kandangData?.id}
/>
),
},
@@ -5,13 +5,11 @@ import { ClosingGeneralInformation } from '@/types/api/closing';
interface ClosingSapronakCalculationTabContentProps {
projectFlockId?: number;
projectKandangId?: number;
closingGeneralInformation?: ClosingGeneralInformation;
}
const ClosingSapronakCalculationTabContent = ({
projectFlockId,
projectKandangId,
closingGeneralInformation,
}: ClosingSapronakCalculationTabContentProps) => {
return (
@@ -21,7 +19,6 @@ const ClosingSapronakCalculationTabContent = ({
<ClosingSapronakCalculationTable
closingGeneralInformation={closingGeneralInformation}
projectFlockId={projectFlockId}
projectKandangId={projectKandangId}
/>
</>
)}
@@ -14,21 +14,23 @@ import useSWR from 'swr';
import { ClosingApi } from '@/services/api/closing';
import { isResponseSuccess } from '@/lib/api-helper';
import { ClosingGeneralInformation } from '@/types/api/closing';
import { useSearchParams } from 'next/navigation';
interface ClosingSapronakCalculationTableProps {
projectFlockId: number;
projectKandangId?: number;
closingGeneralInformation?: ClosingGeneralInformation;
}
const ClosingSapronakCalculationTable = ({
projectFlockId,
closingGeneralInformation,
projectKandangId,
}: ClosingSapronakCalculationTableProps) => {
const searchParams = useSearchParams();
const kandangId = searchParams.get('kandangId');
const { data: sapronakCalculation, isLoading } = useSWR(
`/closing/sapronak-calculation/${projectFlockId}${projectKandangId ? `/${projectKandangId}` : ''}`,
() => ClosingApi.getPerhitunganSapronak(projectFlockId, projectKandangId),
`/closing/sapronak-calculation/${projectFlockId}${kandangId ? `/${kandangId}` : ''}`,
() => ClosingApi.getPerhitunganSapronak(projectFlockId, Number(kandangId)),
{
keepPreviousData: true,
}
@@ -180,7 +182,7 @@ const ClosingSapronakCalculationTable = ({
{/* Table DOC jika kategori Project Flock Growing */}
<Card
title={
closingGeneralInformation?.project_category === 'GROWING'
closingGeneralInformation?.project_type == 'GROWING'
? 'DOC'
: 'Pullet'
}
@@ -14,10 +14,8 @@ import { DashboardApi } from '@/services/api/dashboard';
import { useFormik } from 'formik';
import { ProjectFlockApi } from '@/services/api/production';
import { KandangApi, LocationApi } from '@/services/api/master-data';
import Alert from '@/components/Alert';
import {
DashboardFilterSchema,
DashboardFilterType,
getDashboardFilterSchema,
} from '@/components/pages/dashboard/filter/DashboardProductionFilter.schema';
@@ -31,7 +29,7 @@ import {
import DashboardStats from '@/components/pages/dashboard/chart/DashboardStats';
import { isResponseSuccess } from '@/lib/api-helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { getUniqueFormikErrors } from '@/lib/formik-helper';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
// Helper function to normalize values to array
const normalizeToArray = (
@@ -51,7 +49,6 @@ const DashboardProduction = () => {
);
const [endpointUrl, setEndpointUrl] = useState('/dashboards');
const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>([]);
const [formErrorList, setFormErrorList] = useState<string[]>([]);
// ===== FETCH DATA =====
const {
@@ -149,22 +146,8 @@ const DashboardProduction = () => {
formik.resetForm();
};
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
}
};
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleValidateForm();
formik.handleSubmit();
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
if (isLoadingDashboardProductionData) {
return (
@@ -482,13 +465,7 @@ const DashboardProduction = () => {
</div>
)}
{/* Error List Alert */}
{formErrorList.length > 0 && (
<AlertErrorList
formErrorList={formErrorList}
onClose={() => setFormErrorList([])}
/>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{/* Action Buttons */}
<div className='flex justify-between gap-4 py-4 mt-8 border-t border-gray-300 bg-gray-100'>
@@ -1,7 +1,7 @@
'use client';
import Button from '@/components/Button';
import Card from '@/components/Card';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { FormHeader } from '@/components/helper/form/FormHeader';
import DateInput from '@/components/input/DateInput';
import NumberInput from '@/components/input/NumberInput';
@@ -21,6 +21,7 @@ import {
} from '@/config/constant';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { formatDate, formatTitleCase } from '@/lib/helper';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import { FinanceApi } from '@/services/api/finance';
import { BankApi, CustomerApi, SupplierApi } from '@/services/api/master-data';
import {
@@ -104,6 +105,9 @@ const FormFinanceAdd = ({
},
});
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
// ===== Options =====
const {
options: partyOptions,
@@ -180,7 +184,7 @@ const FormFinanceAdd = ({
title={`${type === 'add' ? 'Tambah' : 'Ubah'} Data Keuangan`}
backUrl='/finance'
/>
<form className='flex flex-col gap-4' onSubmit={formik.handleSubmit}>
<form className='flex flex-col gap-4' onSubmit={handleFormSubmit}>
<SelectInput
label='Jenis Transaksi'
placeholder='Pilih jenis transaksi'
@@ -384,6 +388,7 @@ const FormFinanceAdd = ({
}
required
/>
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex justify-center gap-4'>
<Button
type='reset'
@@ -1,12 +1,10 @@
'use client';
import Button from '@/components/Button';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { FormHeader } from '@/components/helper/form/FormHeader';
import NumberInput from '@/components/input/NumberInput';
import SelectInput, {
OptionType,
useSelect,
} from '@/components/input/SelectInput';
import SelectInput, { useSelect } from '@/components/input/SelectInput';
import TextArea from '@/components/input/TextArea';
import TextInput from '@/components/input/TextInput';
import {
@@ -17,6 +15,7 @@ import {
FINANCE_INITIAL_BALANCE_TYPE_OPTIONS,
FINANCE_PARTY_TYPE_OPTIONS,
} from '@/config/constant';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { formatTitleCase } from '@/lib/helper';
import { FinanceApi } from '@/services/api/finance';
@@ -173,6 +172,9 @@ const FormFinanceAddInitialBalance = ({
[router]
);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl mx-auto'>
@@ -181,7 +183,7 @@ const FormFinanceAddInitialBalance = ({
title={`${type === 'add' ? 'Tambah' : 'Ubah'} Saldo Awal`}
backUrl='/finance'
/>
<form className='flex flex-col gap-4' onSubmit={formik.handleSubmit}>
<form className='flex flex-col gap-4' onSubmit={handleFormSubmit}>
<SelectInput
label='Jenis Pihak'
placeholder='Pilih jenis pihak'
@@ -352,6 +354,7 @@ const FormFinanceAddInitialBalance = ({
}
required
/>
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex justify-center gap-4'>
<Button
type='reset'
@@ -1,18 +1,17 @@
'use client';
import Button from '@/components/Button';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { FormHeader } from '@/components/helper/form/FormHeader';
import DateInput from '@/components/input/DateInput';
import NumberInput from '@/components/input/NumberInput';
import SelectInput, {
OptionType,
useSelect,
} from '@/components/input/SelectInput';
import SelectInput, { useSelect } from '@/components/input/SelectInput';
import TextArea from '@/components/input/TextArea';
import {
InjectionFormSchema,
InjectionFormValues,
} from '@/components/pages/finance/add/injection/FormFinanceInjection.schema';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { formatDate } from '@/lib/helper';
import { FinanceApi } from '@/services/api/finance';
@@ -128,6 +127,9 @@ const FormFinanceInjection = ({
[router]
);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl mx-auto'>
@@ -136,7 +138,7 @@ const FormFinanceInjection = ({
title={`${type === 'add' ? 'Tambah' : 'Ubah'} Injeksi Dana`}
backUrl='/finance'
/>
<form className='flex flex-col gap-4' onSubmit={formik.handleSubmit}>
<form className='flex flex-col gap-4' onSubmit={handleFormSubmit}>
<SelectInput
label='Bank'
placeholder='Pilih bank'
@@ -223,6 +225,7 @@ const FormFinanceInjection = ({
}
required
/>
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex justify-center gap-4'>
<Button
type='reset'
@@ -15,6 +15,7 @@ import { Icon } from '@iconify/react';
import { ColumnDef, ColumnSort, SortingState } from '@tanstack/react-table';
import { useCallback, useEffect, useState } from 'react';
import useSWR from 'swr';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
const InventoryAdjustmentTable = () => {
const {
@@ -1,26 +1,42 @@
import * as Yup from 'yup';
import { OptionType } from '@/components/input/SelectInput';
export const InventoryAdjustmentFormSchema = Yup.object({
product_category: Yup.object({
value: Yup.number().required('ID Kategori Produk wajib diisi!'),
label: Yup.string().required('Nama Kategori Produk wajib diisi!'),
}).nullable(),
product_category: Yup.mixed<OptionType>()
.nullable()
.test(
'is-valid-option',
'Kategori Produk wajib diisi!',
(value) => value !== null && value !== undefined
),
product_category_id: Yup.number().nullable(),
product: Yup.object({
value: Yup.number().required('ID Produk wajib diisi!'),
label: Yup.string().required('Nama Produk wajib diisi!'),
}).nullable(),
product: Yup.mixed<OptionType>()
.nullable()
.test(
'is-valid-option',
'Produk wajib diisi!',
(value) => value !== null && value !== undefined
),
product_id: Yup.number().nullable(),
product_id: Yup.number()
.nullable()
.required('Produk wajib diisi!')
.min(1, 'Produk wajib diisi!'),
warehouse: Yup.object({
value: Yup.number().required('ID Gudang wajib diisi!'),
label: Yup.string().required('Nama Gudang wajib diisi!'),
}).nullable(),
warehouse: Yup.mixed<OptionType>()
.nullable()
.test(
'is-valid-option',
'Warehouse wajib diisi!',
(value) => value !== null && value !== undefined
),
warehouse_id: Yup.number().nullable(),
warehouse_id: Yup.number()
.nullable()
.required('Warehouse wajib diisi!')
.min(1, 'Warehouse wajib diisi!'),
transaction_type: Yup.string()
.oneOf(['increase', 'decrease'], 'Tipe transaksi tidak valid')
@@ -26,6 +26,8 @@ import SelectInput, { OptionType } from '@/components/input/SelectInput';
import TextInput from '@/components/input/TextInput';
import { RadioGroup } from '@/components/input/RadioInput';
import TextArea from '@/components/input/TextArea';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
interface InventoryAdjustmentFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -245,6 +247,9 @@ const InventoryAdjustmentForm = ({
return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
// Render
return (
<>
@@ -266,7 +271,7 @@ const InventoryAdjustmentForm = ({
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -390,6 +395,7 @@ const InventoryAdjustmentForm = ({
readOnly={type === 'detail'}
/>
</div>
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex flex-row justify-between gap-2 flex-wrap'>
{type !== 'detail' && (
<div className='flex flex-row justify-end gap-2'>
@@ -405,11 +411,7 @@ const InventoryAdjustmentForm = ({
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={
!formik.isValid ||
formik.isSubmitting ||
formik.values.product == undefined
}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -48,8 +48,8 @@ import DeliveryOrderProductForm from '@/components/pages/marketing/form/repeater
import { SalesOrderProductFormValues } from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema';
import { DeliveryOrderProductFormValues } from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema';
import RequirePermission from '@/components/helper/RequirePermission';
import { getUniqueFormikErrors } from '@/lib/formik-helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
const MemoizedSalesOrderProductTable = memo(SalesOrderProductTable);
const MemoizedSalesOrderProductForm = memo(SalesOrderProductForm);
@@ -219,7 +219,6 @@ const MarketingForm = ({
const [deliveryFormState, setDeliveryFormState] = useState<'add' | 'edit'>(
'add'
);
const [formErrorList, setFormErrorList] = useState<string[]>([]);
const [deliveryOrderValues, setDeliveryOrderValues] = useState<
DeliveryOrderProductFormValues[]
>(
@@ -561,22 +560,8 @@ const MarketingForm = ({
);
}, [memoSalesOrder]);
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
}
};
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleValidateForm();
formik.handleSubmit();
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
@@ -686,13 +671,7 @@ const MarketingForm = ({
</div>
</div>
{/* Error List Alert */}
{formErrorList.length > 0 && (
<AlertErrorList
formErrorList={formErrorList}
onClose={() => setFormErrorList([])}
/>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{/* Form Actions */}
<div className='flex flex-row items-start justify-center gap-2 mt-4'>
@@ -16,8 +16,8 @@ import Badge from '@/components/Badge';
import { SalesProductToFieldValues } from '@/components/pages/marketing/form/MarketingForm';
import * as Yup from 'yup';
import { isResponseSuccess } from '@/lib/api-helper';
import { getUniqueFormikErrors } from '@/lib/formik-helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
const DeliveryOrderProductForm = ({
formState,
@@ -42,7 +42,6 @@ const DeliveryOrderProductForm = ({
null
);
const [currentInput, setCurrentInput] = useState<string>('');
const [formErrorList, setFormErrorList] = useState<string[]>([]);
const salesOrder = salesOrders.find(
(item) => item.id === initialValues?.marketing_product_id
@@ -168,21 +167,8 @@ const DeliveryOrderProductForm = ({
}
}, [initialValues]);
const handleValidateForm = () => {
formik.validateForm();
const formErrorList = getUniqueFormikErrors(formik.errors);
setFormErrorList(formErrorList);
if (formErrorList.length > 0) {
return;
}
};
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleBlurField(currentInput);
handleValidateForm();
formik.handleSubmit(e);
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
@@ -388,12 +374,7 @@ const DeliveryOrderProductForm = ({
/>
</div>
{formErrorList.length > 0 && (
<AlertErrorList
formErrorList={formErrorList}
onClose={() => setFormErrorList([])}
/>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex flex-row justify-end gap-3 mt-4'>
<Button type='reset' color='warning'>
@@ -24,8 +24,8 @@ import {
} from '@/lib/helper';
import PatternInput from '@/components/input/PatternInput';
import Alert from '@/components/Alert';
import { getUniqueFormikErrors } from '@/lib/formik-helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
const SalesOrderProductForm = ({
initialValues,
@@ -39,7 +39,6 @@ const SalesOrderProductForm = ({
}) => {
const [formErrorMessage, setFormErrorMessage] = useState('');
const [currentInput, setCurrentInput] = useState<string>('');
const [formErrorList, setFormErrorList] = useState<string[]>([]);
// ============ Formik ============
const formik = useFormik<SalesOrderProductFormValues>({
@@ -172,23 +171,8 @@ const SalesOrderProductForm = ({
}
};
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
}
};
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleBlurField(currentInput);
handleValidateForm();
formik.handleSubmit(e);
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
@@ -356,13 +340,7 @@ const SalesOrderProductForm = ({
/>
</div>
{/* Error List Alert */}
{formErrorList.length > 0 && (
<AlertErrorList
formErrorList={formErrorList}
onClose={() => setFormErrorList([])}
/>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex flex-row justify-end gap-3 mt-4'>
<Button type='reset' color='warning' onClick={handleResetForm}>
@@ -25,6 +25,8 @@ import {
} from '@/types/api/master-data/area';
import { AreaApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
interface AreaFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -118,6 +120,9 @@ const AreaForm = ({ type = 'add', initialValues }: AreaFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl'>
@@ -139,7 +144,7 @@ const AreaForm = ({ type = 'add', initialValues }: AreaFormProps) => {
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -199,6 +204,8 @@ const AreaForm = ({ type = 'add', initialValues }: AreaFormProps) => {
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{type !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -213,7 +220,7 @@ const AreaForm = ({ type = 'add', initialValues }: AreaFormProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -25,6 +25,8 @@ import {
} from '@/types/api/master-data/bank';
import { BankApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
interface BankFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -124,6 +126,9 @@ const BankForm = ({ type = 'add', initialValues }: BankFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl'>
@@ -145,7 +150,7 @@ const BankForm = ({ type = 'add', initialValues }: BankFormProps) => {
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -247,6 +252,8 @@ const BankForm = ({ type = 'add', initialValues }: BankFormProps) => {
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{type !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -261,7 +268,7 @@ const BankForm = ({ type = 'add', initialValues }: BankFormProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -28,6 +28,8 @@ import useSWR from 'swr';
import { UserApi } from '@/services/api/user';
import { TYPE_OPTIONS } from '@/config/constant';
import RequirePermission from '@/components/helper/RequirePermission';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
interface CustomerFormProps {
formType?: 'add' | 'edit' | 'detail';
@@ -191,6 +193,9 @@ const CustomerForm = ({
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
// Render
return (
<>
@@ -213,7 +218,7 @@ const CustomerForm = ({
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -358,6 +363,8 @@ const CustomerForm = ({
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{formType !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -372,7 +379,7 @@ const CustomerForm = ({
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -26,6 +26,8 @@ import {
} from '@/types/api/master-data/fcr';
import { FcrApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
interface FcrFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -158,6 +160,9 @@ const FcrForm = ({ type = 'add', initialValues }: FcrFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-5xl'>
@@ -179,7 +184,7 @@ const FcrForm = ({ type = 'add', initialValues }: FcrFormProps) => {
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -294,6 +299,8 @@ const FcrForm = ({ type = 'add', initialValues }: FcrFormProps) => {
)}
</div>
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex flex-row justify-between gap-2 flex-wrap'>
{type !== 'add' && (
<div className='flex flex-row justify-start gap-2'>
@@ -349,7 +356,7 @@ const FcrForm = ({ type = 'add', initialValues }: FcrFormProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -17,6 +17,8 @@ import TextInput from '@/components/input/TextInput';
import { cn } from '@/lib/helper';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
interface FlockCustomProps {
formType?: 'add' | 'edit' | 'detail';
@@ -86,6 +88,9 @@ const FlockForm = ({ formType = 'add', initialValues }: FlockCustomProps) => {
formikSetValues(formikInitialValue);
}, [formikSetValues, formikInitialValue]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
// Render
return (
<>
@@ -107,7 +112,7 @@ const FlockForm = ({ formType = 'add', initialValues }: FlockCustomProps) => {
</h1>
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -168,6 +173,8 @@ const FlockForm = ({ formType = 'add', initialValues }: FlockCustomProps) => {
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{formType !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -182,7 +189,7 @@ const FlockForm = ({ formType = 'add', initialValues }: FlockCustomProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -29,6 +29,8 @@ import { LocationApi, KandangApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { UserApi } from '@/services/api/user';
import NumberInput from '@/components/input/NumberInput';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
interface KandangFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -198,6 +200,9 @@ const KandangForm = ({ type = 'add', initialValues }: KandangFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl'>
@@ -219,7 +224,7 @@ const KandangForm = ({ type = 'add', initialValues }: KandangFormProps) => {
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -324,6 +329,8 @@ const KandangForm = ({ type = 'add', initialValues }: KandangFormProps) => {
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{type !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -338,7 +345,7 @@ const KandangForm = ({ type = 'add', initialValues }: KandangFormProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -27,6 +27,8 @@ import {
} from '@/types/api/master-data/location';
import { AreaApi, LocationApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
interface LocationFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -160,6 +162,9 @@ const LocationForm = ({ type = 'add', initialValues }: LocationFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl'>
@@ -181,7 +186,7 @@ const LocationForm = ({ type = 'add', initialValues }: LocationFormProps) => {
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -268,6 +273,8 @@ const LocationForm = ({ type = 'add', initialValues }: LocationFormProps) => {
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{type !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -282,7 +289,7 @@ const LocationForm = ({ type = 'add', initialValues }: LocationFormProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -29,6 +29,8 @@ import { NonstockApi, SupplierApi, UomApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { flags } from '@/types/api/api-general';
import { SUPPLIER_FLAG_OPTIONS } from '@/config/constant';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
interface NonstockFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -213,6 +215,9 @@ const NonstockForm = ({ type = 'add', initialValues }: NonstockFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl'>
@@ -234,7 +239,7 @@ const NonstockForm = ({ type = 'add', initialValues }: NonstockFormProps) => {
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -337,6 +342,8 @@ const NonstockForm = ({ type = 'add', initialValues }: NonstockFormProps) => {
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{type !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -351,7 +358,7 @@ const NonstockForm = ({ type = 'add', initialValues }: NonstockFormProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -11,7 +11,6 @@ import TextInput from '@/components/input/TextInput';
import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import { getUniqueFormikErrors } from '@/lib/formik-helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import {
@@ -27,6 +26,7 @@ import {
} from '@/types/api/master-data/product-category';
import { ProductCategoryApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
interface ProductCategoryFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -41,7 +41,6 @@ const ProductCategoryForm = ({
const deleteModal = useModal();
const [formErrorMessage, setFormErrorMessage] = useState('');
const [formErrorList, setFormErrorList] = useState<string[]>([]);
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const createProductCategoryHandler = useCallback(
@@ -132,21 +131,8 @@ const ProductCategoryForm = ({
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
const handleValidateForm = async () => {
const errors = await formik.validateForm();
if (Object.keys(errors).length > 0) {
const errorMessages = getUniqueFormikErrors(errors);
setFormErrorList(errorMessages);
return;
}
};
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleValidateForm();
formik.handleSubmit(e);
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
@@ -184,13 +170,7 @@ const ProductCategoryForm = ({
</div>
)}
{/* Error List Alert */}
{formErrorList.length > 0 && (
<AlertErrorList
formErrorList={formErrorList}
onClose={() => setFormErrorList([])}
/>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex flex-col gap-4'>
<TextInput
@@ -39,6 +39,7 @@ import {
} from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { PRODUCT_FLAG_OPTIONS } from '@/config/constant';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
interface ProductFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -50,7 +51,6 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
const deleteModal = useModal();
const [productFormErrorMessage, setProductFormErrorMessage] = useState('');
const [formErrorList, setFormErrorList] = useState<string[]>([]);
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const createProductHandler = useCallback(
@@ -204,21 +204,8 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
const handleValidateForm = async () => {
const errors = await formik.validateForm();
if (Object.keys(errors).length > 0) {
const errorMessages = getUniqueFormikErrors(errors);
setFormErrorList(errorMessages);
return;
}
};
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleValidateForm();
formik.handleSubmit(e);
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
@@ -254,13 +241,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
</div>
)}
{/* Error List Alert */}
{formErrorList.length > 0 && (
<AlertErrorList
formErrorList={formErrorList}
onClose={() => setFormErrorList([])}
/>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='grid grid-cols-1 gap-4'>
<TextInput
@@ -9,6 +9,7 @@ import {
ProductionStandardRepeaterFormSchemaValues,
ProductionStandardFormValues,
createProductionStandardRepeaterFormSchema,
ProductionStandardFormSchema,
} from '@/components/pages/master-data/production-standard/form/ProductionStandardForm.schema';
import Table, { TABLE_DEFAULT_STYLING } from '@/components/Table';
import { FLOCK_CATEGORY_OPTIONS } from '@/config/constant';
@@ -31,6 +32,8 @@ import { useModal } from '@/components/Modal';
import RequirePermission from '@/components/helper/RequirePermission';
import Tooltip from '@/components/Tooltip';
import Alert from '@/components/Alert';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
type TableRowsType = {
customRow: boolean;
@@ -207,6 +210,7 @@ const ProductionStandardForm = ({
initialValues: formikInitialValues as ProductionStandardFormValues,
// Only enable reinitialize for edit/detail mode, not add mode
enableReinitialize: formType !== 'add',
validationSchema: ProductionStandardFormSchema,
onSubmit: (values) => {
switch (formType) {
case 'add':
@@ -723,7 +727,8 @@ const ProductionStandardForm = ({
router.push('/master-data/production-standard');
};
// ===== Function =====
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
@@ -1210,9 +1215,26 @@ const ProductionStandardForm = ({
return null;
}}
/>
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{productionStandardFormErrorMessage && (
<Alert color='error' className='w-full'>
<div className='flex items-center gap-2 stretch'>
<Icon icon='mdi:alert' />
<span>{productionStandardFormErrorMessage}</span>
</div>
<Icon
icon='mdi:close'
onClick={() => setProductionStandardFormErrorMessage('')}
className='ms-auto'
/>
</Alert>
)}
<form
className='flex justify-between mt-6 gap-2 flex-wrap'
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
>
{formType === 'detail' && (
<div className='gap-2 flex items-center'>
@@ -1293,19 +1315,6 @@ const ProductionStandardForm = ({
</div>
)}
</form>
{productionStandardFormErrorMessage && (
<Alert color='error' className='w-full'>
<div className='flex items-center gap-2 stretch'>
<Icon icon='mdi:alert' />
<span>{productionStandardFormErrorMessage}</span>
</div>
<Icon
icon='mdi:close'
onClick={() => setProductionStandardFormErrorMessage('')}
className='ms-auto'
/>
</Alert>
)}
</div>
<ConfirmationModal
@@ -25,6 +25,8 @@ import TextArea from '@/components/input/TextArea';
import { cn } from '@/lib/helper';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import RequirePermission from '@/components/helper/RequirePermission';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
interface SupplierCustomProps {
formType?: 'add' | 'edit' | 'detail';
@@ -199,6 +201,9 @@ const SupplierForm = ({
formik.setFieldValue('category', val);
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
// Render
return (
<>
@@ -221,7 +226,7 @@ const SupplierForm = ({
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -444,6 +449,8 @@ const SupplierForm = ({
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{formType !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -458,7 +465,7 @@ const SupplierForm = ({
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -25,6 +25,8 @@ import {
} from '@/types/api/master-data/uom';
import { UomApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
interface UomFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -118,6 +120,9 @@ const UomForm = ({ type = 'add', initialValues }: UomFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl'>
@@ -139,7 +144,7 @@ const UomForm = ({ type = 'add', initialValues }: UomFormProps) => {
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -199,6 +204,8 @@ const UomForm = ({ type = 'add', initialValues }: UomFormProps) => {
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{type !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -213,7 +220,7 @@ const UomForm = ({ type = 'add', initialValues }: UomFormProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -33,6 +33,8 @@ import {
} from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { WAREHOUSE_TYPE_OPTIONS } from '@/config/constant';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
interface WarehouseFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -323,6 +325,9 @@ const WarehouseForm = ({ type = 'add', initialValues }: WarehouseFormProps) => {
formikSetValues(formikInitialValues);
}, [formikSetValues, formikInitialValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
<section className='w-full max-w-xl'>
@@ -344,7 +349,7 @@ const WarehouseForm = ({ type = 'add', initialValues }: WarehouseFormProps) => {
</header>
<form
onSubmit={formik.handleSubmit}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
className='w-full mt-8 flex flex-col gap-6'
>
@@ -474,6 +479,8 @@ const WarehouseForm = ({ type = 'add', initialValues }: WarehouseFormProps) => {
</div>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
{type !== 'detail' && (
<div
className={cn('flex flex-row justify-end gap-2', {
@@ -488,7 +495,7 @@ const WarehouseForm = ({ type = 'add', initialValues }: WarehouseFormProps) => {
type='submit'
color='primary'
isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting}
disabled={formik.isSubmitting}
className='px-4'
>
Submit
@@ -209,20 +209,6 @@ const ProjectFlockDetail = ({
</Badge>
</div>
{/* <div className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2'>
<Icon width={14} height={14} icon={'mdi:clock'} /> History
</div>
<div className='col-span-2'>
<Button variant='outline' className='py-1 text-sm'>
See History{' '}
<Icon
icon='mdi:arrow-top-right-thin'
width={11}
height={11}
/>
</Button>
</div> */}
{/* BARIS 1 */}
<div
className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2
@@ -252,6 +238,18 @@ const ProjectFlockDetail = ({
</div>
<div className='col-span-2'>{projectFlock?.fcr?.name}</div>
<div
className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2
relative
before:content-[""] before:absolute before:left-[5px] before:top-[90%] before:bottom-[-100%] before:w-[1px] before:border-1 before:border-dashed before:border-gray-400'
>
<Icon width={14} height={14} icon='mdi:circle-slice-8' />{' '}
Standard
</div>
<div className='col-span-2'>
{projectFlock?.production_standard?.name ?? '-'}
</div>
{/* BARIS 3 (Terakhir - TIDAK PERLU garis di bawahnya) */}
<div className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2'>
<Icon width={14} height={14} icon='mdi:circle-slice-8' />{' '}
@@ -6,7 +6,6 @@ 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,
@@ -47,6 +46,7 @@ import { Nonstock } from '@/types/api/master-data/nonstock';
import { useUiStore } from '@/stores/ui/ui.store';
import RequirePermission from '@/components/helper/RequirePermission';
import DrawerHeader from '@/components/helper/drawer/DrawerHeader';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
interface ProjectFlockFormProps {
formType?: 'add' | 'edit' | 'detail';
@@ -66,7 +66,6 @@ const ProjectFlockForm = ({
const [projectFlockFormErrorMessage, setProjectFlockFormErrorMessage] =
useState('');
const [formErrorList, setFormErrorList] = useState<string[]>([]);
const [selectedArea, setSelectedArea] = useState('');
const [selectedLocation, setSelectedLocation] = useState('');
const [selectedCategory, setSelectedCategory] = useState('');
@@ -642,16 +641,8 @@ 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
}
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
return (
<>
@@ -712,11 +703,7 @@ const ProjectFlockForm = ({
<form
className='w-auto h-auto'
onSubmit={(e) => {
e.preventDefault();
handleValidateForm();
formik.handleSubmit(e);
}}
onSubmit={handleFormSubmit}
onReset={formik.handleReset}
>
{/* Form Informasi Umum */}
@@ -1082,13 +1069,7 @@ const ProjectFlockForm = ({
</div>
</div>
{/* Error List Alert */}
{formErrorList.length > 0 && (
<AlertErrorList
formErrorList={formErrorList}
onClose={() => setFormErrorList([])}
/>
)}
<AlertErrorList formErrorList={formErrorList} onClose={close} />
<div className='flex flex-row justify-center gap-2 flex-wrap my-6 px-4'>
{formType !== 'detail' && (
+62
View File
@@ -0,0 +1,62 @@
import { getUniqueFormikErrors } from '@/lib/formik-helper';
import { FormikProps } from 'formik';
import { useState } from 'react';
interface UseFormikErrorListOptions {
onBeforeSubmit?: (e: React.FormEvent<HTMLFormElement>) => boolean | void;
onAfterValidation?: () => void | Promise<void>;
}
export const useFormikErrorList = <T>(
formik: FormikProps<T>,
options?: UseFormikErrorListOptions
) => {
const [formErrorList, setFormErrorList] = useState<string[]>([]);
const handleValidateForm = async () => {
const errors = await formik.validateForm();
if (Object.keys(errors).length > 0) {
const errorMessages = getUniqueFormikErrors(errors);
setFormErrorList(errorMessages);
return false;
}
return true;
};
const handleFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// Call onBeforeSubmit callback
if (options?.onBeforeSubmit) {
const shouldContinue = options.onBeforeSubmit(e);
if (shouldContinue === false) {
return; // Cancel submit
}
}
// Validate form
const isValid = await handleValidateForm();
// Call onAfterValidation callback if validation passed
if (options?.onAfterValidation) {
await options.onAfterValidation();
}
// Submit form
formik.handleSubmit();
};
const close = () => {
setFormErrorList([]);
};
return {
formErrorList,
setFormErrorList,
close,
handleValidateForm,
handleFormSubmit,
};
};
+1
View File
@@ -63,6 +63,7 @@ export type BaseClosing = {
location_id: number;
location_name: string;
project_category: 'GROWING' | 'LAYING';
project_type?: 'GROWING' | 'LAYING'; // berubah dari BE?
period: number;
closing_date?: string;
shed_label: string;
+1
View File
@@ -5,6 +5,7 @@ import { Kandang } from '@/types/api/master-data/kandang';
import { Location } from '@/types/api/master-data/location';
import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
import { Nonstock } from '@/types/api/master-data/nonstock';
import { ProductionStandard } from '@/types/api/master-data/production-standard';
export type BaseProjectFlock = {
id: number;