142 lines
3.6 KiB
TypeScript
142 lines
3.6 KiB
TypeScript
/**
|
|
* useCSVHandling Hook - Manages CSV upload and download operations
|
|
*/
|
|
|
|
import { useState, useCallback } from 'react';
|
|
import axios from 'axios';
|
|
import { useAppDispatch } from '../stores/hooks';
|
|
import type { AsyncThunk } from '@reduxjs/toolkit';
|
|
import { logger } from '../lib/logger';
|
|
|
|
interface UseCSVHandlingReturn {
|
|
csvFile: File | null;
|
|
setCsvFile: (file: File | null) => void;
|
|
isModalActive: boolean;
|
|
setIsModalActive: (active: boolean) => void;
|
|
isUploading: boolean;
|
|
isDownloading: boolean;
|
|
downloadCSV: () => Promise<void>;
|
|
uploadCSV: () => Promise<void>;
|
|
error: string | null;
|
|
}
|
|
|
|
interface UseCSVHandlingOptions {
|
|
endpoint: string;
|
|
uploadAction: AsyncThunk<
|
|
{ imported: number },
|
|
File,
|
|
{ rejectValue: unknown }
|
|
>;
|
|
setRefetchAction: (value: boolean) => { type: string; payload: boolean };
|
|
fileName?: string;
|
|
}
|
|
|
|
/**
|
|
* Hook for handling CSV upload and download operations
|
|
*
|
|
* @param options - Configuration options
|
|
* @returns CSV handling state and functions
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* const {
|
|
* csvFile,
|
|
* setCsvFile,
|
|
* isModalActive,
|
|
* setIsModalActive,
|
|
* downloadCSV,
|
|
* uploadCSV
|
|
* } = useCSVHandling({
|
|
* endpoint: 'users',
|
|
* uploadAction: uploadCsv,
|
|
* setRefetchAction: setRefetch
|
|
* })
|
|
* ```
|
|
*/
|
|
export function useCSVHandling(
|
|
options: UseCSVHandlingOptions,
|
|
): UseCSVHandlingReturn {
|
|
const { endpoint, uploadAction, setRefetchAction, fileName } = options;
|
|
const dispatch = useAppDispatch();
|
|
|
|
const [csvFile, setCsvFile] = useState<File | null>(null);
|
|
const [isModalActive, setIsModalActive] = useState(false);
|
|
const [isUploading, setIsUploading] = useState(false);
|
|
const [isDownloading, setIsDownloading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const downloadCSV = useCallback(async () => {
|
|
setIsDownloading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await axios({
|
|
url: `${endpoint}?filetype=csv`,
|
|
method: 'GET',
|
|
responseType: 'blob',
|
|
});
|
|
|
|
const contentType = (response.headers['content-type'] as string) || 'text/csv';
|
|
const blob = new Blob([response.data], { type: contentType });
|
|
const link = document.createElement('a');
|
|
link.href = window.URL.createObjectURL(blob);
|
|
link.download = fileName || `${endpoint}.csv`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
window.URL.revokeObjectURL(link.href);
|
|
} catch (err) {
|
|
const message =
|
|
err instanceof Error ? err.message : 'Failed to download CSV';
|
|
setError(message);
|
|
logger.error(
|
|
'CSV download error:',
|
|
err instanceof Error ? err : { error: err },
|
|
);
|
|
} finally {
|
|
setIsDownloading(false);
|
|
}
|
|
}, [endpoint, fileName]);
|
|
|
|
const uploadCSV = useCallback(async () => {
|
|
if (!csvFile) {
|
|
setError('No file selected');
|
|
return;
|
|
}
|
|
|
|
setIsUploading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
await dispatch(uploadAction(csvFile)).unwrap();
|
|
dispatch(setRefetchAction(true));
|
|
setCsvFile(null);
|
|
setIsModalActive(false);
|
|
} catch (err) {
|
|
const message =
|
|
err instanceof Error ? err.message : 'Failed to upload CSV';
|
|
setError(message);
|
|
logger.error(
|
|
'CSV upload error:',
|
|
err instanceof Error ? err : { error: err },
|
|
);
|
|
} finally {
|
|
setIsUploading(false);
|
|
}
|
|
}, [csvFile, dispatch, uploadAction, setRefetchAction]);
|
|
|
|
return {
|
|
csvFile,
|
|
setCsvFile,
|
|
isModalActive,
|
|
setIsModalActive,
|
|
isUploading,
|
|
isDownloading,
|
|
downloadCSV,
|
|
uploadCSV,
|
|
error,
|
|
};
|
|
}
|
|
|
|
export default useCSVHandling;
|