Skip to content
Snippets Groups Projects
Commit 465f16fb authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge branch '289-add-support-for-matomo' into 'development'

Resolve "add support for matomo"

Closes #289

See merge request !264
parents b8e2d93b 19160c28
No related branches found
No related tags found
1 merge request!264Resolve "add support for matomo"
Pipeline #96751 passed
Showing
with 215 additions and 49 deletions
minerva-front (19.0.0~alpha.0) stable; urgency=medium
* Feature: support for matomo (#289)
-- Piotr Gawron <piotr.gawron@uni.lu> Fri, 18 Oct 2024 13:00:00 +0200
minerva-front (18.0.0~beta.5) stable; urgency=medium
* Small improvements: when ToS is defined ask user to accept it (#298)
* Small improvements: there is a waiting spinner after clicking on download
button (#297)
* Small improvements: when exporting map as image provide default selections
in for format and submap (#295)
* Small improvements: dropdown sections in exporting map as image are
unfolded permanently (#296)
* Bugfix: missing links added (#299)
-- Piotr Gawron <piotr.gawron@uni.lu> Fri, 04 Oct 2024 13:00:00 +0200
minerva-front (18.0.0~beta.4) stable; urgency=medium
* Bugfix: connectivity issue should report a problem with network instead of
submitting error report(#293)
* Bugfix: source map for js was missing (#292)
* Bugfix: sometimes project don't have link to disease or organism, this
crashed listing of projects after log in (#290)
* Bugfix: show proper message when there is a problem with overlay data
instead error report form (#291)
-- Piotr Gawron <piotr.gawron@uni.lu> Wed, 02 Oct 2024 13:00:00 +0200
minerva-front (18.0.0~beta.3) stable; urgency=medium
* Bugfix: link to download project source was invalid
* Bugfix: change background to empty after overlay is loaded so there is no
blank background (#285)
* Bugfix: license info styling (#280)
* Bugfix: list of plugins did not contain version (#287)
-- Piotr Gawron <piotr.gawron@uni.lu> Thu, 26 Sep 2024 13:00:00 +0200
minerva-front (18.0.0~beta.2) stable; urgency=medium
* Feature: minerva frontend - first version
* Bugfix: when opening overlay provide loading info (#285)
......
......@@ -3,6 +3,7 @@ const nextConfig = {
reactStrictMode: true,
basePath: process.env.APP_PREFIX ? process.env.APP_PREFIX + '/index.html' : '',
assetPrefix: process.env.APP_PREFIX ? process.env.APP_PREFIX : '',
productionBrowserSourceMaps: true,
output: 'export',
images: {
unoptimized: true,
......
// const root = 'https://minerva-dev.lcsb.uni.lu';
const root = 'https://lux1.atcomp.pl';
window.config = {
BASE_API_URL: 'https://lux1.atcomp.pl/minerva/api',
BASE_NEW_API_URL: 'https://lux1.atcomp.pl/minerva/new_api/',
BASE_MAP_IMAGES_URL: 'https://lux1.atcomp.pl/',
DEFAULT_PROJECT_ID: 'pdmap_appu_test',
ADMIN_PANEL_URL: 'https://lux1.atcomp.pl/minerva/admin.xhtml',
BASE_API_URL: `${root}/minerva/api`,
BASE_NEW_API_URL: `${root}/minerva/new_api/`,
BASE_MAP_IMAGES_URL: `${root}/`,
DEFAULT_PROJECT_ID: 'sample',
ADMIN_PANEL_URL: `${root}/minerva/admin.xhtml`,
};
......@@ -8,19 +8,21 @@ interface AppWrapperProps {
children: ReactNode;
}
export const AppWrapper = ({ children }: AppWrapperProps): JSX.Element => (
<MapInstanceProvider>
<Provider store={store}>
<>
<Toaster
position="top-center"
visibleToasts={1}
style={{
width: '700px',
}}
/>
{children}
</>
</Provider>
</MapInstanceProvider>
);
export const AppWrapper = ({ children }: AppWrapperProps): JSX.Element => {
return (
<MapInstanceProvider>
<Provider store={store}>
<>
<Toaster
position="top-center"
visibleToasts={1}
style={{
width: '700px',
}}
/>
{children}
</>
</Provider>
</MapInstanceProvider>
);
};
......@@ -4,6 +4,7 @@ import dynamic from 'next/dynamic';
import { AccessDeniedModal } from '@/components/FunctionalArea/Modal/AccessDeniedModal/AccessDeniedModal.component';
import { AddCommentModal } from '@/components/FunctionalArea/Modal/AddCommentModal/AddCommentModal.component';
import { LicenseModal } from '@/components/FunctionalArea/Modal/LicenseModal';
import { ToSModal } from '@/components/FunctionalArea/Modal/ToSModal/ToSModal.component';
import { EditOverlayModal } from './EditOverlayModal';
import { LoginModal } from './LoginModal';
import { ErrorReportModal } from './ErrorReportModal';
......@@ -63,6 +64,11 @@ export const Modal = (): React.ReactNode => {
<AccessDeniedModal />
</ModalLayout>
)}
{isOpen && modalName === 'terms-of-service' && (
<ModalLayout>
<ToSModal />
</ModalLayout>
)}
{isOpen && modalName === 'select-project' && (
<ModalLayout>
<AccessDeniedModal />
......
......@@ -30,6 +30,7 @@ export const ModalLayout = ({ children }: ModalLayoutProps): JSX.Element => {
modalName === 'login' && 'h-auto w-[400px]',
modalName === 'access-denied' && 'h-auto w-[400px]',
modalName === 'select-project' && 'h-auto w-[400px]',
modalName === 'terms-of-service' && 'h-auto w-[400px]',
modalName === 'add-comment' && 'h-auto w-[400px]',
modalName === 'error-report' && 'h-auto w-[800px]',
['edit-overlay', 'logged-in-menu'].includes(modalName) && 'h-auto w-[432px]',
......@@ -46,7 +47,7 @@ export const ModalLayout = ({ children }: ModalLayoutProps): JSX.Element => {
<div> {modalTitle} </div>
)}
{modalName !== 'logged-in-menu' && (
{modalName !== 'logged-in-menu' && modalName !== 'terms-of-service' && (
<button type="button" onClick={handleCloseModal} aria-label="close button">
<Icon name="close" className="fill-font-500" />
</button>
......
import React from 'react';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { Button } from '@/shared/Button';
import { getSessionValid, logout, updateUser } from '@/redux/user/user.thunks';
import { closeModal } from '@/redux/modal/modal.slice';
import { userSelector } from '@/redux/user/user.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { termsOfServiceValSelector } from '@/redux/configuration/configuration.selectors';
export const ToSModal: React.FC = () => {
const dispatch = useAppDispatch();
const { userData } = useAppSelector(userSelector);
const termsOfService = useAppSelector(termsOfServiceValSelector);
const updateUserTosHandler = async (): Promise<void> => {
// eslint-disable-next-line no-console
console.log('update');
if (userData) {
const user = { ...userData, termsOfUseConsent: true };
await dispatch(updateUser(user));
await dispatch(getSessionValid());
dispatch(closeModal());
}
};
const logoutHandler = async (): Promise<void> => {
await dispatch(logout());
dispatch(closeModal());
};
return (
<div className="w-[400px] border border-t-[#E1E0E6] bg-white p-[24px]">
<div>
I agree to the minerva{' '}
<a href={termsOfService} target="_blank" className="underline">
Terms of Service.
</a>
</div>
<div className="mt-4 grid grid-cols-2 gap-2">
<div>
<Button
className="ring-transparent hover:ring-transparent"
variantStyles="secondary"
onClick={updateUserTosHandler}
>
OK
</Button>
</div>
<div className="text-center">
<Button className="block w-full" onClick={logoutHandler}>
I disagree
</Button>
</div>
</div>
</div>
);
};
......@@ -23,7 +23,7 @@ describe('NavBar - component', () => {
expect(screen.getByTestId('nav-buttons')).toBeInTheDocument();
expect(screen.getByTestId('nav-logos-and-powered-by')).toBeInTheDocument();
expect(screen.getByAltText('luxembourg logo')).toBeInTheDocument();
expect(screen.getByAltText('logo')).toBeInTheDocument();
expect(screen.getByAltText('University of Luxembourg logo')).toBeInTheDocument();
expect(screen.getByAltText('Minerva logo')).toBeInTheDocument();
});
});
......@@ -101,14 +101,18 @@ export const NavBar = (): JSX.Element => {
/>
</div>
<div className="flex flex-col items-center gap-[20px]" data-testid="nav-logos-and-powered-by">
<Image
className="rounded rounded-e rounded-s bg-white-pearl pb-[7px]"
src={luxembourgLogoImg}
alt="luxembourg logo"
height={41}
width={48}
/>
<Image src={logoImg} alt="logo" height={48} width={48} />
<a href="https://www.uni.lu/en/" target="_blank">
<Image
className="rounded rounded-e rounded-s bg-white-pearl pb-[7px]"
src={luxembourgLogoImg}
alt="University of Luxembourg logo"
height={41}
width={48}
/>
</a>
<a href="https://minerva.uni.lu/" target="_blank">
<Image src={logoImg} alt="Minerva logo" height={48} width={48} />
</a>
<span className="h-16 w-14 text-center text-[8px] leading-4">
Powered by: MINERVA Platform{' '}
<a href={MINERVA_WEBSITE_URL} target="_blank">
......
import { useSelect } from 'downshift';
import { IconButton } from '@/shared/IconButton';
import { twMerge } from 'tailwind-merge';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { userSelector } from '@/redux/user/user.selectors';
import { openToSModal } from '@/redux/modal/modal.slice';
import { termsOfServiceValSelector } from '@/redux/configuration/configuration.selectors';
import { useUserActions } from '../hooks/useUserActions';
export const AuthenticatedUser = (): React.ReactNode => {
......@@ -10,6 +15,14 @@ export const AuthenticatedUser = (): React.ReactNode => {
items: actions,
});
const dispatch = useAppDispatch();
const { userData } = useAppSelector(userSelector);
const termsOfService = useAppSelector(termsOfServiceValSelector);
if (userData && !userData.termsOfUseConsent && termsOfService) {
dispatch(openToSModal());
}
return (
<>
<IconButton
......
......@@ -48,7 +48,7 @@ describe('AvailablePluginsDrawer - component', () => {
},
});
const pluginLabel = screen.getByText(currentPlugin.name);
const pluginLabel = screen.getByText(`${currentPlugin.name} (${currentPlugin.version})`);
expect(pluginLabel).toBeInTheDocument();
},
);
......
......@@ -43,7 +43,7 @@ describe('LoadPlugin - component', () => {
it('renders plugin name', () => {
renderComponent({ plugin });
const title = screen.getByText(plugin.name);
const title = screen.getByText(`${plugin.name} (${plugin.version})`);
expect(title).toBeInTheDocument();
});
......
......@@ -22,7 +22,9 @@ export const LoadPlugin = ({ plugin }: Props): JSX.Element => {
return (
<div className="flex w-full items-center justify-between text-sm">
<span className="text-cetacean-blue">{plugin.name}</span>
<span className="text-cetacean-blue">
{plugin.name} ({plugin.version})
</span>
<Button
variantStyles="secondary"
className="h-10 self-end rounded-e rounded-s text-xs font-medium"
......
......@@ -12,12 +12,14 @@ type CollapsibleSectionProps = {
title: string;
children: React.ReactNode;
onOpened?(): void;
dangerouslySetExpanded?: boolean;
};
export const CollapsibleSection = ({
title,
children,
onOpened,
dangerouslySetExpanded,
}: CollapsibleSectionProps): React.ReactNode => {
const handleOnChange = (ids: ID[]): void => {
const hasBeenOpened = ids.length > ZERO;
......@@ -29,7 +31,7 @@ export const CollapsibleSection = ({
return (
<Accordion allowZeroExpanded onChange={handleOnChange}>
<AccordionItem>
<AccordionItem dangerouslySetExpanded={dangerouslySetExpanded}>
<AccordionItemHeading>
<AccordionItemButton>{title}</AccordionItemButton>
</AccordionItemHeading>
......
import { useContext } from 'react';
import { useContext, useState } from 'react';
import { Button } from '@/shared/Button';
import Image from 'next/image';
import spinnerIcon from '@/assets/vectors/icons/spinner.svg';
import { ExportContext } from '../ExportCompound.context';
export const DownloadElements = (): React.ReactNode => {
const { handleDownloadElements } = useContext(ExportContext);
const [downloadingElements, setDownloadingElements] = useState<boolean>(false);
const handleDownloadElementsWrapper = async (): Promise<void> => {
setDownloadingElements(true);
await handleDownloadElements();
setDownloadingElements(false);
};
return (
<div className="mt-6">
<Button onClick={handleDownloadElements}>Download</Button>
<Button onClick={handleDownloadElementsWrapper}>
{downloadingElements && (
<Image
src={spinnerIcon}
alt="spinner icon"
height={12}
width={12}
className="mr-2 animate-spin"
/>
)}
Download
</Button>
</div>
);
};
import { useContext } from 'react';
import { useContext, useState } from 'react';
import { Button } from '@/shared/Button';
import Image from 'next/image';
import spinnerIcon from '@/assets/vectors/icons/spinner.svg';
import { ExportContext } from '../ExportCompound.context';
export const DownloadNetwork = (): React.ReactNode => {
const { handleDownloadNetwork } = useContext(ExportContext);
const [downloadingNetwork, setDownloadingNetwork] = useState<boolean>(false);
const handleDownloadNetworkWrapper = async (): Promise<void> => {
setDownloadingNetwork(true);
await handleDownloadNetwork();
setDownloadingNetwork(false);
};
return (
<div className="mt-6">
<Button onClick={handleDownloadNetwork}>Download</Button>
<Button onClick={handleDownloadNetworkWrapper}>
{downloadingNetwork && (
<Image
src={spinnerIcon}
alt="spinner icon"
height={12}
width={12}
className="mr-2 animate-spin"
/>
)}
Download
</Button>
</div>
);
};
......@@ -52,8 +52,7 @@ export const Export = ({ children }: ExportProps): JSX.Element => {
includedCompartmentPathways,
excludedCompartmentPathways,
});
dispatch(downloadElements(body));
await dispatch(downloadElements(body));
}, [modelIds, annotations, includedCompartmentPathways, excludedCompartmentPathways, dispatch]);
const handleDownloadNetwork = useCallback(async () => {
......@@ -65,7 +64,7 @@ export const Export = ({ children }: ExportProps): JSX.Element => {
excludedCompartmentPathways,
});
dispatch(downloadNetwork(data));
await dispatch(downloadNetwork(data));
}, [modelIds, annotations, includedCompartmentPathways, excludedCompartmentPathways, dispatch]);
const handleDownloadGraphics = useCallback(async () => {
......
......@@ -54,8 +54,8 @@ export const EXPORT_CONTEXT_DEFAULT_VALUE: ExportContextType = {
setModels: () => {},
setImageSize: () => {},
setImageFormats: () => {},
handleDownloadElements: () => {},
handleDownloadNetwork: () => {},
handleDownloadElements: () => Promise.resolve(),
handleDownloadNetwork: () => Promise.resolve(),
handleDownloadGraphics: () => {},
data: {
annotations: [],
......
......@@ -8,8 +8,8 @@ export type ExportContextType = {
setModels: React.Dispatch<React.SetStateAction<CheckboxItem[]>>;
setImageSize: React.Dispatch<React.SetStateAction<ImageSize>>;
setImageFormats: React.Dispatch<React.SetStateAction<CheckboxItem[]>>;
handleDownloadElements: () => void;
handleDownloadNetwork: () => void;
handleDownloadElements: () => Promise<void>;
handleDownloadNetwork: () => Promise<void>;
handleDownloadGraphics: () => void;
data: {
annotations: CheckboxItem[];
......
......@@ -40,8 +40,6 @@ describe('ImageFormat - component', () => {
},
});
expect(screen.queryByTestId('checkbox-filter')).not.toBeVisible();
const navigationButton = screen.getByTestId('accordion-item-button');
act(() => {
......
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