mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
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:
@@ -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'>
|
||||
|
||||
+4
-23
@@ -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
|
||||
|
||||
+24
-15
@@ -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' && (
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
Vendored
+1
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user