Skip to content
Snippets Groups Projects
Commit 46f615a6 authored by Adrian Orłów's avatar Adrian Orłów
Browse files

feat: add publications list modal download partial

parent e5fec157
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!179feat: Add publications list modal download csv (MIN-242)
import { twMerge } from 'tailwind-merge'; import spinnerIcon from '@/assets/vectors/icons/spinner.svg';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector'; import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { closeModal } from '@/redux/modal/modal.slice'; import { closeModal } from '@/redux/modal/modal.slice';
import { Icon } from '@/shared/Icon';
import { filteredSizeSelector } from '@/redux/publications/publications.selectors'; import { filteredSizeSelector } from '@/redux/publications/publications.selectors';
import { MODAL_ROLE } from './PublicationsModalLayout.constants'; import { Button } from '@/shared/Button';
import { Icon } from '@/shared/Icon';
import Image from 'next/image';
import { twMerge } from 'tailwind-merge';
import { PublicationsSearch } from '../PublicationsSearch'; import { PublicationsSearch } from '../PublicationsSearch';
import { MODAL_ROLE } from './PublicationsModalLayout.constants';
import { useDownloadPublicationsAsCSVFile } from './utils/useDownloadPublicationsAsCSVFile';
type ModalLayoutProps = { type ModalLayoutProps = {
children: React.ReactNode; children: React.ReactNode;
...@@ -14,6 +18,7 @@ type ModalLayoutProps = { ...@@ -14,6 +18,7 @@ type ModalLayoutProps = {
export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Element => { export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Element => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const numberOfPublications = useAppSelector(filteredSizeSelector); const numberOfPublications = useAppSelector(filteredSizeSelector);
const { downloadPublicationsAsCSVFile, isLoading } = useDownloadPublicationsAsCSVFile();
const handleCloseModal = (): void => { const handleCloseModal = (): void => {
dispatch(closeModal()); dispatch(closeModal());
...@@ -27,8 +32,22 @@ export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Ele ...@@ -27,8 +32,22 @@ export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Ele
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
<div className={twMerge('flex h-5/6 w-10/12 flex-col overflow-hidden rounded-lg')}> <div className={twMerge('flex h-5/6 w-10/12 flex-col overflow-hidden rounded-lg')}>
<div className="flex items-center justify-between bg-white p-[24px] text-xl"> <div className="flex items-center justify-between bg-white p-[24px] text-xl">
<div className="font-semibold"> <div className="flex items-center gap-4">
<div>Publications ({numberOfPublications} results)</div> <div className="font-semibold">
<div>Publications ({numberOfPublications} results)</div>
</div>
<Button onClick={downloadPublicationsAsCSVFile} disabled={isLoading}>
{isLoading && (
<Image
src={spinnerIcon}
alt="spinner icon"
height={12}
width={12}
className="animate-spin"
/>
)}
Download CSV
</Button>
</div> </div>
<div className="flex flex-row flex-nowrap items-center"> <div className="flex flex-row flex-nowrap items-center">
<PublicationsSearch /> <PublicationsSearch />
......
import { publicationsResponseSchema } from '@/models/publicationsResponseSchema';
import { apiPath } from '@/redux/apiPath';
import { PUBLICATIONS_FETCHING_ERROR_PREFIX } from '@/redux/publications/publications.constatns';
import { axiosInstance } from '@/services/api/utils/axiosInstance';
import { Publication, PublicationsResponse } from '@/types/models';
import { getErrorMessage } from '@/utils/getErrorMessage';
import { showToast } from '@/utils/showToast';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
interface Args {
length: number;
}
export const getBasePublications = async ({ length }: Args): Promise<Publication[]> => {
try {
const response = await axiosInstance.get<PublicationsResponse>(
apiPath.getPublications({ params: { length } }),
);
const isDataValid = validateDataUsingZodSchema(response.data, publicationsResponseSchema);
return isDataValid ? response.data.data : [];
} catch (error) {
const errorMessage = getErrorMessage({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX });
showToast({
type: 'error',
message: errorMessage,
});
return [];
}
};
import { FIRST_ARRAY_ELEMENT } from '@/constants/common';
import { Publication } from '@/types/models';
import { StandarizedPublication } from '@/types/publications';
interface Options {
modelNameIdMap: Record<number, string>;
}
export const getStandarizedPublication = (
publication: Publication,
options: Options,
): StandarizedPublication => {
const {
publication: { article },
elements,
} = publication;
const { modelNameIdMap } = options;
const { title, authors, journal, year, pubmedId } = article;
const mainElement = elements?.[FIRST_ARRAY_ELEMENT];
return {
pubmedId,
journal,
title,
year: year ? `${year}` : '',
authors: authors.join(','),
modelName: mainElement ? modelNameIdMap[mainElement.modelId] : '',
elementId: '', // TODO:
};
};
import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { modelsNameMapSelector } from '@/redux/models/models.selectors';
import { StandarizedPublication } from '@/types/publications';
import { useState } from 'react';
import { getBasePublications } from './getBasePublications';
import { getStandarizedPublication } from './getStandarizedPublications';
export type DownloadPublicationsAsCSVFile = () => Promise<void>;
interface UseDownloadPublicationsAsCSVFileResult {
downloadPublicationsAsCSVFile: DownloadPublicationsAsCSVFile;
isLoading: boolean;
}
export const useDownloadPublicationsAsCSVFile = (): UseDownloadPublicationsAsCSVFileResult => {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [publications, setPublications] = useState<StandarizedPublication[]>([]);
const modelNameIdMap = useAppSelector(modelsNameMapSelector);
const getPublicationsAsList = async (): Promise<StandarizedPublication[]> => {
if (publications.length > SIZE_OF_EMPTY_ARRAY) {
return publications;
}
const basePublications = await getBasePublications({
length: 100, // TODO: change for number of all elements
});
const standarizedPublications = await Promise.all(
basePublications.map(async publication =>
getStandarizedPublication(publication, {
modelNameIdMap,
}),
),
);
setPublications(standarizedPublications);
return standarizedPublications;
};
const downloadPublicationsAsCSVFile = async (): Promise<void> => {
setIsLoading(true);
const data = await getPublicationsAsList();
setIsLoading(false);
console.log({ data });
};
return {
downloadPublicationsAsCSVFile,
isLoading,
};
};
export interface StandarizedPublication {
pubmedId: string;
year: string;
journal: string;
authors: string;
title: string;
modelName: string;
elementId: string;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment