From a75d84556a658e4dc39c5e07e58e6eba69cc8392 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 25 Feb 2026 15:56:12 +0700 Subject: [PATCH] refactor(FE): Refactor date validation to use shared state and cleanup --- src/components/pages/finance/FinanceTable.tsx | 31 +++--- .../TransferToLayingFilterModal.tsx | 102 ++++++++++++++++-- .../production/uniformity/UniformityTable.tsx | 95 ++++++++++++++-- .../finance/filter/DebtSupplierFilter.ts | 14 ++- .../report/finance/tab/CustomerPaymentTab.tsx | 9 +- .../report/finance/tab/DebtSupplierTab.tsx | 78 +++++++++++++- .../tab/PurchasesPerSupplierTab.tsx | 9 +- .../marketing/tab/DailyMarketingTab.tsx | 89 ++++++++++++--- 8 files changed, 371 insertions(+), 56 deletions(-) diff --git a/src/components/pages/finance/FinanceTable.tsx b/src/components/pages/finance/FinanceTable.tsx index f83fa469..10d959b9 100644 --- a/src/components/pages/finance/FinanceTable.tsx +++ b/src/components/pages/finance/FinanceTable.tsx @@ -189,6 +189,7 @@ const FinanceTable = () => { const [selectedFinance, setSelectedFinance] = useState(null); const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [dateErrorShown, setDateErrorShown] = useState(false); + const [hasDateError, setHasDateError] = useState(false); // ===== Formik for Filter ===== const filterFormik = useFormik({ @@ -335,10 +336,7 @@ const FinanceTable = () => { const endDateObj = new Date(endDate); if (endDateObj < startDate) { - filterFormik.setFieldError( - 'end_date', - 'Tanggal akhir tidak boleh masa lampau' - ); + setHasDateError(true); if (!dateErrorShown) { toast.error('Tanggal akhir tidak boleh masa lampau', { duration: Infinity, @@ -346,12 +344,14 @@ const FinanceTable = () => { setDateErrorShown(true); } } else { - filterFormik.setFieldError('end_date', undefined); + setHasDateError(false); if (dateErrorShown) { toast.dismiss(); setDateErrorShown(false); } } + } else { + setHasDateError(false); } }; @@ -366,10 +366,7 @@ const FinanceTable = () => { const endDate = new Date(value); if (endDate < startDateObj) { - filterFormik.setFieldError( - 'end_date', - 'Tanggal akhir tidak boleh masa lampau' - ); + setHasDateError(true); if (!dateErrorShown) { toast.error('Tanggal akhir tidak boleh masa lampau', { duration: Infinity, @@ -380,7 +377,7 @@ const FinanceTable = () => { } } - filterFormik.setFieldError('end_date', undefined); + setHasDateError(false); if (dateErrorShown) { toast.dismiss(); setDateErrorShown(false); @@ -661,22 +658,20 @@ const FinanceTable = () => { name='start_date' label='Periode Tanggal (Mulai)' value={filterFormik.values.start_date} + errorMessage={filterFormik.errors.start_date} onChange={startDateChangeHandler} - errorMessage={ - filterFormik.errors.end_date - ? filterFormik.errors.end_date - : undefined + isError={ + filterFormik.touched.start_date && Boolean(filterFormik.errors.start_date) } /> { + return () => { + if (dateErrorShown) { + toast.dismiss(); + } + }; + }, [dateErrorShown]); + + // ===== CLEANUP TOAST WHEN MODAL CLOSES ===== + useEffect(() => { + const dialogElement = ref.current; + const handleModalClose = () => { + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + }; + + dialogElement?.addEventListener('close', handleModalClose); + + return () => { + dialogElement?.removeEventListener('close', handleModalClose); + }; + }, [ref, dateErrorShown]); + // Flock Source const { setInputValue: setFlockSourceInputValue, @@ -138,24 +169,77 @@ const TransferToLayingFilterModal = ({ name='startDate' placeholder='Tanggal Awal' value={formik.values.startDate || ''} - onChange={formik.handleChange} + errorMessage={formik.errors.startDate} + onChange={(e) => { + const value = e.target.value; + formik.setFieldValue('startDate', value); + + if (value && formik.values.endDate) { + const startDate = new Date(value); + const endDateObj = new Date(formik.values.endDate); + + if (endDateObj < startDate) { + setHasDateError(true); + if (!dateErrorShown) { + toast.error('Tanggal akhir tidak boleh masa lampau', { + duration: Infinity, + }); + setDateErrorShown(true); + } + } else { + setHasDateError(false); + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + } + } else { + setHasDateError(false); + } + }} onBlur={formik.handleBlur} + isError={ + formik.touched.startDate && Boolean(formik.errors.startDate) + } />
{ + const value = e.target.value; + formik.setFieldValue('endDate', value); + + if (value && formik.values.startDate) { + const startDateObj = new Date(formik.values.startDate); + const endDate = new Date(value); + + if (endDate < startDateObj) { + setHasDateError(true); + if (!dateErrorShown) { + toast.error('Tanggal akhir tidak boleh masa lampau', { + duration: Infinity, + }); + setDateErrorShown(true); + } + return; + } + } + + setHasDateError(false); + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + }} onBlur={formik.handleBlur} - isError={formik.touched.endDate && !!formik.errors.endDate} + isError={ + (formik.touched.endDate && Boolean(formik.errors.endDate)) || hasDateError + } /> - {formik.touched.endDate && formik.errors.endDate && ( - - {formik.errors.endDate} - - )} { useState(''); const [, setFilterErrors] = useState>({}); + // ===== DATE ERROR STATE ===== + const [dateErrorShown, setDateErrorShown] = useState(false); + const [hasDateError, setHasDateError] = useState(false); + const { setInputValue: setFilterLocationInputValue, options: filterLocationOptions, @@ -792,6 +796,23 @@ const UniformityTable = () => { } }, [uniformities, rowSelection]); + useEffect(() => { + return () => { + if (dateErrorShown) { + toast.dismiss(); + } + }; + }, [dateErrorShown]); + + useEffect(() => { + return () => { + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + }; + }, [filterModal.open, dateErrorShown]); + // ===== TABLE COLUMNS DEFINITION ===== const uniformityColumns: ColumnDef[] = useMemo( () => [ @@ -1245,9 +1266,38 @@ const UniformityTable = () => { placeholder='Tanggal Mulai' value={filterFormik.values.start_date} errorMessage={filterFormik.errors.start_date} - onChange={(e) => - filterFormik.setFieldValue('start_date', e.target.value) - } + onChange={(e) => { + const value = e.target.value; + filterFormik.setFieldValue('start_date', value); + + if (value && filterFormik.values.end_date) { + const startDate = new Date(value); + const endDateObj = new Date( + filterFormik.values.end_date + ); + + if (endDateObj < startDate) { + setHasDateError(true); + if (!dateErrorShown) { + toast.error( + 'Tanggal akhir tidak boleh masa lampau', + { + duration: Infinity, + } + ); + setDateErrorShown(true); + } + } else { + setHasDateError(false); + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + } + } else { + setHasDateError(false); + } + }} isError={ filterFormik.touched.start_date && Boolean(filterFormik.errors.start_date) @@ -1259,13 +1309,38 @@ const UniformityTable = () => { placeholder='Tanggal Akhir' value={filterFormik.values.end_date} errorMessage={filterFormik.errors.end_date} - onChange={(e) => - filterFormik.setFieldValue('end_date', e.target.value) - } - isError={ - filterFormik.touched.end_date && - Boolean(filterFormik.errors.end_date) - } + onChange={(e) => { + const value = e.target.value; + filterFormik.setFieldValue('end_date', value); + + if (value && filterFormik.values.start_date) { + const startDateObj = new Date( + filterFormik.values.start_date + ); + const endDate = new Date(value); + + if (endDate < startDateObj) { + setHasDateError(true); + if (!dateErrorShown) { + toast.error( + 'Tanggal akhir tidak boleh masa lampau', + { + duration: Infinity, + } + ); + setDateErrorShown(true); + } + return; + } + } + + setHasDateError(false); + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + }} + isError={hasDateError} /> diff --git a/src/components/pages/report/finance/filter/DebtSupplierFilter.ts b/src/components/pages/report/finance/filter/DebtSupplierFilter.ts index 1c1c2fac..0ecd299e 100644 --- a/src/components/pages/report/finance/filter/DebtSupplierFilter.ts +++ b/src/components/pages/report/finance/filter/DebtSupplierFilter.ts @@ -11,7 +11,19 @@ export type DebtSupplierFilterType = { export const DebtSupplierFilterSchema: yup.ObjectSchema = yup.object({ startDate: yup.string().optional().notRequired(), - endDate: yup.string().optional().notRequired(), + endDate: yup + .string() + .optional() + .notRequired() + .test( + 'is-greater-than-start', + 'Tanggal akhir tidak boleh masa lampau', + function (value) { + const startDate = this.parent.startDate; + if (!startDate || !value) return true; + return new Date(value) >= new Date(startDate); + } + ), supplierIds: yup .array() .of( diff --git a/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx b/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx index 1e89ca43..4b596155 100644 --- a/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx +++ b/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx @@ -844,19 +844,26 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
diff --git a/src/components/pages/report/finance/tab/DebtSupplierTab.tsx b/src/components/pages/report/finance/tab/DebtSupplierTab.tsx index 56bf3259..d5341854 100644 --- a/src/components/pages/report/finance/tab/DebtSupplierTab.tsx +++ b/src/components/pages/report/finance/tab/DebtSupplierTab.tsx @@ -87,6 +87,10 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { }); const [isSubmitted, setIsSubmitted] = useState(false); + // ===== DATE ERROR STATE ===== + const [dateErrorShown, setDateErrorShown] = useState(false); + const [hasDateError, setHasDateError] = useState(false); + const filterModal = useModal(); const { @@ -106,6 +110,7 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { const handleFilterModalOpen = () => { filterModal.openModal(); + formik.validateForm(); }; // ===== FORMIK SETUP ===== @@ -349,6 +354,23 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { }; }, [tabId, clearTabActions]); + useEffect(() => { + return () => { + if (dateErrorShown) { + toast.dismiss(); + } + }; + }, [dateErrorShown]); + + useEffect(() => { + return () => { + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + }; + }, [filterModal.open, dateErrorShown]); + const getTableColumns = (supplier?: DebtSupplier): ColumnDef[] => [ { id: 'no', @@ -723,7 +745,31 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { name='startDate' value={formik.values.startDate || ''} onChange={(e) => { - formik.setFieldValue('startDate', e.target.value || null); + const value = e.target.value; + formik.setFieldValue('startDate', value || null); + + if (value && formik.values.endDate) { + const startDate = new Date(value); + const endDateObj = new Date(formik.values.endDate); + + if (endDateObj < startDate) { + setHasDateError(true); + if (!dateErrorShown) { + toast.error('Tanggal akhir tidak boleh masa lampau', { + duration: Infinity, + }); + setDateErrorShown(true); + } + } else { + setHasDateError(false); + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + } + } else { + setHasDateError(false); + } }} className={{ wrapper: 'w-full' }} isError={ @@ -737,10 +783,36 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { name='endDate' value={formik.values.endDate || ''} onChange={(e) => { - formik.setFieldValue('endDate', e.target.value || null); + const value = e.target.value; + formik.setFieldValue('endDate', value || null); + + if (value && formik.values.startDate) { + const startDateObj = new Date(formik.values.startDate); + const endDate = new Date(value); + + if (endDate < startDateObj) { + setHasDateError(true); + if (!dateErrorShown) { + toast.error('Tanggal akhir tidak boleh masa lampau', { + duration: Infinity, + }); + setDateErrorShown(true); + } + return; + } + } + + setHasDateError(false); + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } }} className={{ wrapper: 'w-full' }} - isError={formik.touched.endDate && !!formik.errors.endDate} + isError={ + (formik.touched.endDate && !!formik.errors.endDate) || + hasDateError + } errorMessage={formik.errors.endDate} isNestedModal /> diff --git a/src/components/pages/report/logistic-stock/tab/PurchasesPerSupplierTab.tsx b/src/components/pages/report/logistic-stock/tab/PurchasesPerSupplierTab.tsx index 6023a123..d0fb8d47 100644 --- a/src/components/pages/report/logistic-stock/tab/PurchasesPerSupplierTab.tsx +++ b/src/components/pages/report/logistic-stock/tab/PurchasesPerSupplierTab.tsx @@ -842,18 +842,25 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
diff --git a/src/components/pages/report/marketing/tab/DailyMarketingTab.tsx b/src/components/pages/report/marketing/tab/DailyMarketingTab.tsx index 219a380d..0f68d3b0 100644 --- a/src/components/pages/report/marketing/tab/DailyMarketingTab.tsx +++ b/src/components/pages/report/marketing/tab/DailyMarketingTab.tsx @@ -80,6 +80,10 @@ const DailyMarketingTab = ({ tabId }: DailyMarketingTabProps) => { // ===== FILTER STATE ===== const [filterParams, setFilterParams] = useState({}); + // ===== DATE ERROR STATE ===== + const [dateErrorShown, setDateErrorShown] = useState(false); + const [hasDateError, setHasDateError] = useState(false); + const filterModal = useModal(); // ===== OPTIONS ===== @@ -448,6 +452,23 @@ const DailyMarketingTab = ({ tabId }: DailyMarketingTabProps) => { }; }, [tabId, clearTabActions]); + useEffectHook(() => { + return () => { + if (dateErrorShown) { + toast.dismiss(); + } + }; + }, [dateErrorShown]); + + useEffectHook(() => { + return () => { + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + }; + }, [filterModal.open, dateErrorShown]); + const getTableColumns = (): ColumnDef[] => { const tableColumns: ColumnDef[] = [ { @@ -791,34 +812,76 @@ const DailyMarketingTab = ({ tabId }: DailyMarketingTabProps) => { placeholder='Pilih Tanggal Awal' value={formik.values.start_date || ''} onChange={(e) => { - formik.setFieldValue('start_date', e.target.value || null); + const value = e.target.value; + formik.setFieldValue('start_date', value || null); + + if (value && formik.values.end_date) { + const startDate = new Date(value); + const endDateObj = new Date(formik.values.end_date); + + if (endDateObj < startDate) { + setHasDateError(true); + if (!dateErrorShown) { + toast.error('Tanggal akhir tidak boleh masa lampau', { + duration: Infinity, + }); + setDateErrorShown(true); + } + } else { + setHasDateError(false); + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } + } + } else { + setHasDateError(false); + } }} className={{ wrapper: 'w-full' }} + errorMessage={formik.errors.start_date} isError={ !!formik.errors.start_date && formik.touched.start_date } /> - {formik.errors.start_date && formik.touched.start_date && ( -
- {formik.errors.start_date} -
- )} { - formik.setFieldValue('end_date', e.target.value || null); + const value = e.target.value; + formik.setFieldValue('end_date', value || null); + + if (value && formik.values.start_date) { + const startDateObj = new Date(formik.values.start_date); + const endDate = new Date(value); + + if (endDate < startDateObj) { + setHasDateError(true); + if (!dateErrorShown) { + toast.error('Tanggal akhir tidak boleh masa lampau', { + duration: Infinity, + }); + setDateErrorShown(true); + } + return; + } + } + + setHasDateError(false); + if (dateErrorShown) { + toast.dismiss(); + setDateErrorShown(false); + } }} className={{ wrapper: 'w-full' }} - isError={!!formik.errors.end_date && formik.touched.end_date} + errorMessage={formik.errors.end_date} + isError={ + (formik.errors.end_date && formik.touched.end_date) || + hasDateError + } /> - {formik.errors.end_date && formik.touched.end_date && ( -
- {formik.errors.end_date} -
- )}