diff --git a/src/components/pages/production/recording/RecordingTable.tsx b/src/components/pages/production/recording/RecordingTable.tsx index cea30502..8104d162 100644 --- a/src/components/pages/production/recording/RecordingTable.tsx +++ b/src/components/pages/production/recording/RecordingTable.tsx @@ -48,6 +48,7 @@ import { useUiStore } from '@/stores/ui/ui.store'; import { usePathname } from 'next/navigation'; import { Color } from '@/types/theme'; import ButtonFilter from '@/components/helper/ButtonFilter'; +import Dropdown from '@/components/Dropdown'; // ===== STATUS BADGE UTILITIES ===== const statusTextMap: Record = { @@ -352,6 +353,9 @@ const RecordingTable = () => { const [isRejectLoading, setIsRejectLoading] = useState(false); const [, setApprovalNotes] = useState(''); + const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] = + useState(false); + const singleDeleteModal = useModal(); const approveModal = useModal(); const rejectModal = useModal(); @@ -686,6 +690,14 @@ const RecordingTable = () => { }); }, [selectedRowIds, recordings, isRecordingApproved]); + const exportToExcelHandler = async () => { + setIsLoadingExportingToExcel(true); + + await RecordingApi.exportToExcel(getTableFilterQueryString()); + + setIsLoadingExportingToExcel(false); + }; + useEffect(() => { if (isResponseSuccess(recordings) && recordings.data) { const newSelection: Record = {}; @@ -1313,6 +1325,50 @@ const RecordingTable = () => { onClick={handleFilterModalOpen} className='px-3 py-2.5' /> + + + + Export +
+ +
+ + } + className={{ + content: + 'mt-1 rounded-xl border border-base-content/5 shadow-sm overflow-hidden', + }} + > + +
diff --git a/src/services/api/production.ts b/src/services/api/production.ts index d481081d..1f2a0373 100644 --- a/src/services/api/production.ts +++ b/src/services/api/production.ts @@ -12,6 +12,8 @@ import { NextDayRecording, } from '@/types/api/production/recording'; import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; +import { httpClient } from '@/services/http/client'; +import { formatDate } from '@/lib/helper'; export const ProjectFlockKandangApi = new BaseApiService< ProjectFlockKandang, @@ -88,6 +90,30 @@ export class RecordingService extends BaseApiService< } ); } + + async exportToExcel(initialQueryString: string) { + const params = new URLSearchParams(initialQueryString); + + params.set('export', 'excel'); + + const queryString = `?${params.toString()}`; + + const res = await httpClient(`${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 = `recording-${formatDate(Date.now(), 'DD-MM-YYYY')}.xlsx`; + link.setAttribute('download', fileName); + + document.body.appendChild(link); + link.click(); + link.remove(); + } } export const RecordingApi = new RecordingService('/production/recordings'); diff --git a/src/services/http/base.ts b/src/services/http/base.ts index 83bb5ee6..267e7622 100644 --- a/src/services/http/base.ts +++ b/src/services/http/base.ts @@ -9,6 +9,13 @@ export type RequestOptions = { auth?: AuthMode; // 'cookie' | 'bearer' | 'none' token?: string; // required if auth === 'bearer' timeoutMs?: number; + responseType?: + | 'arraybuffer' + | 'blob' + | 'document' + | 'json' + | 'text' + | 'stream'; }; export class HttpError extends Error { diff --git a/src/services/http/client.ts b/src/services/http/client.ts index 42e71978..c70a82ea 100644 --- a/src/services/http/client.ts +++ b/src/services/http/client.ts @@ -40,6 +40,7 @@ export async function httpClient( data: opts.body, timeout: opts.timeoutMs ?? 10_000, withCredentials: isCookieAuth && !isBearerAuth, + responseType: opts.responseType, headers: { ...(isFormData ? {} : { 'Content-Type': 'application/json' }), ...(opts.headers ?? {}),