feat: implement recording export progress input

This commit is contained in:
ValdiANS
2026-04-22 11:06:15 +07:00
parent 75910960c5
commit ddfd1206a7
2 changed files with 204 additions and 0 deletions
@@ -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<string, string> = {
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<HTMLInputElement>) => {
setExportProgressStartDate(e.target.value);
},
[]
);
const exportProgressEndDateChangeHandler = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
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<string, boolean> = {};
@@ -1368,6 +1465,16 @@ const RecordingTable = () => {
<Icon icon='heroicons:table-cells' width={20} height={20} />
Export to Excel
</Button>
<Button
variant='ghost'
color='none'
onClick={exportProgressInputToExcelClickHandler}
className='w-full p-3 justify-start text-sm text-base-content/50 font-semibold text-nowrap'
>
<Icon icon='heroicons:table-cells' width={20} height={20} />
Ekspor Input Progress (Excel)
</Button>
</Dropdown>
</div>
</div>
@@ -1551,6 +1658,76 @@ const RecordingTable = () => {
}}
/>
<Modal
ref={exportProgressInputModal.ref}
className={{
modalBox: 'max-w-lg rounded-lg p-0',
}}
closeOnBackdrop
>
<div className='flex flex-col'>
<div className='flex items-center justify-between border-b border-base-content/10 p-4'>
<h4 className='text-sm font-semibold text-base-content'>
Ekspor Input Progress
</h4>
<Button
variant='ghost'
color='none'
onClick={() => {
exportProgressInputModal.closeModal();
resetExportProgressForm();
}}
className='p-1'
>
<Icon icon='mdi:close' width={20} height={20} />
</Button>
</div>
<div className='flex flex-col gap-4 p-4'>
<DateInput
name='export_progress_start_date'
label='Tanggal Mulai'
value={exportProgressStartDate}
onChange={exportProgressStartDateChangeHandler}
isNestedModal
required
/>
<DateInput
name='export_progress_end_date'
label='Tanggal Selesai'
value={exportProgressEndDate}
onChange={exportProgressEndDateChangeHandler}
isNestedModal
required
/>
</div>
<div className='flex justify-end gap-3 border-t border-base-content/10 p-4'>
<Button
variant='outline'
color='none'
onClick={() => {
exportProgressInputModal.closeModal();
resetExportProgressForm();
}}
className='px-3 py-2.5'
>
Batal
</Button>
<Button
color='success'
onClick={submitExportProgressInputHandler}
isLoading={isExportProgressLoading}
disabled={!exportProgressStartDate || !exportProgressEndDate}
className='px-3 py-2.5'
>
Submit
</Button>
</div>
</div>
</Modal>
<ConfirmationModalWithNotes
ref={approveModal.ref}
type='success'
+27
View File
@@ -116,6 +116,33 @@ export class RecordingService extends BaseApiService<
link.click();
link.remove();
}
async exportInputProgressToExcel(startDate: string, endDate: string) {
const params = new URLSearchParams();
params.set('export', 'excel');
params.set('type', 'progress');
params.set('start_date', formatDate(startDate, 'YYYY-MM-DD'));
params.set('end_date', formatDate(endDate, 'YYYY-MM-DD'));
const queryString = `?${params.toString()}`;
const res = await httpClient<Blob>(`${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');