From ddfd1206a739d2f12a3c9c094b8b4aa97d2933cc Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Wed, 22 Apr 2026 11:06:15 +0700 Subject: [PATCH] feat: implement recording export progress input --- .../production/recording/RecordingTable.tsx | 177 ++++++++++++++++++ src/services/api/production.ts | 27 +++ 2 files changed, 204 insertions(+) diff --git a/src/components/pages/production/recording/RecordingTable.tsx b/src/components/pages/production/recording/RecordingTable.tsx index 8104d162..b31d1bb9 100644 --- a/src/components/pages/production/recording/RecordingTable.tsx +++ b/src/components/pages/production/recording/RecordingTable.tsx @@ -1,5 +1,6 @@ 'use client'; +import axios from 'axios'; import React, { useCallback, useState, @@ -18,6 +19,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import { OptionType } from '@/components/input/SelectInput'; import SelectInput, { useSelect } from '@/components/input/SelectInput'; +import DateInput from '@/components/input/DateInput'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import PopoverButton from '@/components/popover/PopoverButton'; import PopoverContent from '@/components/popover/PopoverContent'; @@ -50,6 +52,43 @@ import { Color } from '@/types/theme'; import ButtonFilter from '@/components/helper/ButtonFilter'; import Dropdown from '@/components/Dropdown'; +const getExportErrorMessage = async ( + error: unknown, + fallbackMessage: string +) => { + if (axios.isAxiosError(error)) { + const responseData = error.response?.data; + + if (responseData instanceof Blob) { + try { + const parsed = JSON.parse(await responseData.text()) as { + message?: string; + }; + return parsed.message || fallbackMessage; + } catch { + return fallbackMessage; + } + } + + if ( + responseData && + typeof responseData === 'object' && + 'message' in responseData && + typeof responseData.message === 'string' + ) { + return responseData.message; + } + + return error.message || fallbackMessage; + } + + if (error instanceof Error) { + return error.message; + } + + return fallbackMessage; +}; + // ===== STATUS BADGE UTILITIES ===== const statusTextMap: Record = { APPROVED: 'Disetujui', @@ -355,10 +394,14 @@ const RecordingTable = () => { const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] = useState(false); + const [isExportProgressLoading, setIsExportProgressLoading] = useState(false); + const [exportProgressStartDate, setExportProgressStartDate] = useState(''); + const [exportProgressEndDate, setExportProgressEndDate] = useState(''); const singleDeleteModal = useModal(); const approveModal = useModal(); const rejectModal = useModal(); + const exportProgressInputModal = useModal(); const { data: recordings, @@ -698,6 +741,60 @@ const RecordingTable = () => { setIsLoadingExportingToExcel(false); }; + const resetExportProgressForm = useCallback(() => { + setExportProgressStartDate(''); + setExportProgressEndDate(''); + }, []); + + const exportProgressStartDateChangeHandler = useCallback( + (e: React.ChangeEvent) => { + setExportProgressStartDate(e.target.value); + }, + [] + ); + + const exportProgressEndDateChangeHandler = useCallback( + (e: React.ChangeEvent) => { + setExportProgressEndDate(e.target.value); + }, + [] + ); + + const exportProgressInputToExcelClickHandler = useCallback(() => { + resetExportProgressForm(); + exportProgressInputModal.openModal(); + }, [exportProgressInputModal, resetExportProgressForm]); + + const submitExportProgressInputHandler = useCallback(async () => { + if (!exportProgressStartDate || !exportProgressEndDate) { + return; + } + + setIsExportProgressLoading(true); + + try { + await RecordingApi.exportInputProgressToExcel( + exportProgressStartDate, + exportProgressEndDate + ); + + exportProgressInputModal.closeModal(); + resetExportProgressForm(); + toast.success('Ekspor berhasil'); + } catch (error) { + toast.error( + await getExportErrorMessage(error, 'Gagal mengekspor input progress') + ); + } finally { + setIsExportProgressLoading(false); + } + }, [ + exportProgressEndDate, + exportProgressInputModal, + exportProgressStartDate, + resetExportProgressForm, + ]); + useEffect(() => { if (isResponseSuccess(recordings) && recordings.data) { const newSelection: Record = {}; @@ -1368,6 +1465,16 @@ const RecordingTable = () => { Export to Excel + + @@ -1551,6 +1658,76 @@ const RecordingTable = () => { }} /> + +
+
+

+ Ekspor Input Progress +

+ +
+ +
+ + + +
+ +
+ + +
+
+
+ (`${this.basePath}${queryString}`, { + method: 'GET', + responseType: 'blob', + }); + + const url = window.URL.createObjectURL(new Blob([res])); + const link = document.createElement('a'); + link.href = url; + + const fileName = `input-progres-recording-${formatDate(startDate, 'DD-MM-YYYY')}-ke-${formatDate(endDate, 'DD-MM-YYYY')}.xlsx`; + link.setAttribute('download', fileName); + + document.body.appendChild(link); + link.click(); + link.remove(); + } } export const RecordingApi = new RecordingService('/production/recordings');