diff --git a/src/components/FunctionalArea/Modal/LicenseModal/LicenseModal.component.tsx b/src/components/FunctionalArea/Modal/LicenseModal/LicenseModal.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bdb79d2d85249e45549562b2d4a2d7bbef9856d8 --- /dev/null +++ b/src/components/FunctionalArea/Modal/LicenseModal/LicenseModal.component.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { projectSelector } from '@/redux/project/project.selectors'; + +export const LicenseModal = (): React.ReactNode => { + const project = useAppSelector(projectSelector).data; + + let licenseDescription = ''; + if (project) { + licenseDescription = project.license + ? `<a href="${project.license.url}" target="_blank">Link</a><br/><br/>${project.license.content}` + : `<a href="${project.customLicenseUrl}" target="_blank">Link: ${project.customLicenseUrl}</a>`; + } + return ( + <div className="w-full overflow-auto border border-t-[#E1E0E6] bg-white p-[24px]"> + <div dangerouslySetInnerHTML={{ __html: licenseDescription }} /> + </div> + ); +}; diff --git a/src/components/FunctionalArea/Modal/LicenseModal/index.ts b/src/components/FunctionalArea/Modal/LicenseModal/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b05258c696c04adae86a585264c23777a9b95356 --- /dev/null +++ b/src/components/FunctionalArea/Modal/LicenseModal/index.ts @@ -0,0 +1 @@ +export { LicenseModal } from './LicenseModal.component'; diff --git a/src/components/FunctionalArea/Modal/Modal.component.tsx b/src/components/FunctionalArea/Modal/Modal.component.tsx index 10ee738f82da351681b36bb019c243f5376e6133..8b97ece7a85b1931420cb95c9c781dd9a72d6067 100644 --- a/src/components/FunctionalArea/Modal/Modal.component.tsx +++ b/src/components/FunctionalArea/Modal/Modal.component.tsx @@ -3,6 +3,7 @@ import { modalSelector } from '@/redux/modal/modal.selector'; 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 { EditOverlayModal } from './EditOverlayModal'; import { LoginModal } from './LoginModal'; import { ErrorReportModal } from './ErrorReportModal'; @@ -41,6 +42,11 @@ export const Modal = (): React.ReactNode => { <ErrorReportModal /> </ModalLayout> )} + {isOpen && modalName === 'license' && ( + <ModalLayout> + <LicenseModal /> + </ModalLayout> + )} {isOpen && modalName === 'publications' && <PublicationsModal />} {isOpen && modalName === 'edit-overlay' && ( <ModalLayout> diff --git a/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx b/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx index 5a04b6fabcedce598ad481a6059e5ab30ee8c502..9bf8267cdfde579330b19eb9cb221e79b0c2ad30 100644 --- a/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx +++ b/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx @@ -1,7 +1,7 @@ import { apiPath } from '@/redux/apiPath'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppSelector } from '@/redux/hooks/useAppSelector'; -import { openPublicationsModal } from '@/redux/modal/modal.slice'; +import { openLicenseModal, openPublicationsModal } from '@/redux/modal/modal.slice'; import { mainMapModelDescriptionSelector } from '@/redux/models/models.selectors'; import { diseaseLinkSelector, @@ -9,6 +9,7 @@ import { organismLinkSelector, organismNameSelector, projectNameSelector, + projectSelector, versionSelector, } from '@/redux/project/project.selectors'; import { DrawerHeading } from '@/shared/DrawerHeading'; @@ -23,11 +24,18 @@ export const ProjectInfoDrawer = (): JSX.Element => { const organismLink = useAppSelector(organismLinkSelector); const organismName = useAppSelector(organismNameSelector); const projectName = useAppSelector(projectNameSelector); + const project = useAppSelector(projectSelector).data; const version = useAppSelector(versionSelector); const description = useAppSelector(mainMapModelDescriptionSelector); const sourceDownloadLink = window.location.hostname + apiPath.getSourceFile(); + let licenseName: string = ''; + if (project) { + licenseName = project.license ? project.license.name : project.customLicenseName; + } + const licenseExists = licenseName !== ''; + useEffect(() => { // dispatch(getPublications()); }, [dispatch]); @@ -36,6 +44,10 @@ export const ProjectInfoDrawer = (): JSX.Element => { dispatch(openPublicationsModal()); }; + const onLicenseClick = (): void => { + dispatch(openLicenseModal(licenseName)); + }; + return ( <div data-testid="export-drawer" className="h-full max-h-full"> <DrawerHeading title="Project info" /> @@ -76,6 +88,13 @@ export const ProjectInfoDrawer = (): JSX.Element => { </a> </li> )} + {licenseExists && ( + <li className="mt-2 text-hyperlink-blue"> + <button type="button" onClick={onLicenseClick} className="text-base font-semibold"> + License: {licenseName} + </button> + </li> + )} {organismName && ( <li className="mt-2 text-hyperlink-blue"> <span className="text-black">Organism: </span> diff --git a/src/models/disease.ts b/src/models/disease.ts index 65079e66740c8eb2f4488a9f5798339cc9a4abe4..7152a107591c97797fb840aae640a4f49ac191d0 100644 --- a/src/models/disease.ts +++ b/src/models/disease.ts @@ -1,9 +1,9 @@ import { z } from 'zod'; export const disease = z.object({ - link: z.string(), + id: z.number().int().positive(), + link: z.string().optional(), type: z.string(), resource: z.string(), - id: z.number(), annotatorClassName: z.string(), }); diff --git a/src/models/licenseSchema.ts b/src/models/licenseSchema.ts new file mode 100644 index 0000000000000000000000000000000000000000..3111314e390f0b65e684f8fdd1b83178de6b0d47 --- /dev/null +++ b/src/models/licenseSchema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; + +export const licenseSchema = z.object({ + id: z.number().int().positive(), + name: z.string(), + url: z.string(), + content: z.string(), +}); diff --git a/src/models/organism.ts b/src/models/organism.ts index 4b003eefff187e85f7b27831e55d5c09ea3b5c62..f583456293d3886270327f55285aaa67d8cd90a6 100644 --- a/src/models/organism.ts +++ b/src/models/organism.ts @@ -1,9 +1,9 @@ import { z } from 'zod'; export const organism = z.object({ - link: z.string(), + id: z.number().int().positive(), + link: z.string().optional(), type: z.string(), resource: z.string(), - id: z.number(), annotatorClassName: z.string(), }); diff --git a/src/models/projectSchema.ts b/src/models/projectSchema.ts index ab00d3b824d4501f070c205e2756b54bb02fadf8..39862947ce2adb12652bc9c91eca96ee77ad35a8 100644 --- a/src/models/projectSchema.ts +++ b/src/models/projectSchema.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { licenseSchema } from '@/models/licenseSchema'; import { disease } from './disease'; import { organism } from './organism'; import { overviewImageView } from './overviewImageView'; @@ -23,4 +24,7 @@ export const projectSchema = z.object({ creationDate: z.string(), overviewImageViews: z.array(overviewImageView), topOverviewImage: overviewImageView.nullable(), + license: z.optional(licenseSchema).nullable(), + customLicenseName: z.string(), + customLicenseUrl: z.string(), }); diff --git a/src/redux/modal/modal.reducers.ts b/src/redux/modal/modal.reducers.ts index f2eecb87c1850948c3364ca310c3215e44704001..4556c17cc91238adaa87cb538d3d8548ea4376e4 100644 --- a/src/redux/modal/modal.reducers.ts +++ b/src/redux/modal/modal.reducers.ts @@ -103,3 +103,9 @@ export const openEditOverlayModalReducer = ( state.modalTitle = action.payload.name; state.editOverlayState = action.payload; }; + +export const openLicenseModalReducer = (state: ModalState, action: PayloadAction<string>): void => { + state.isOpen = true; + state.modalName = 'license'; + state.modalTitle = `License: ${action.payload}`; +}; diff --git a/src/redux/modal/modal.slice.ts b/src/redux/modal/modal.slice.ts index 33263ed31bc07d01d342fc24c7381091616602d0..3e945e4a1e01b8d6cba771860700e4e2e9177927 100644 --- a/src/redux/modal/modal.slice.ts +++ b/src/redux/modal/modal.slice.ts @@ -14,6 +14,7 @@ import { openErrorReportModalReducer, openAccessDeniedModalReducer, openSelectProjectModalReducer, + openLicenseModalReducer, } from './modal.reducers'; const modalSlice = createSlice({ @@ -33,6 +34,7 @@ const modalSlice = createSlice({ openErrorReportModal: openErrorReportModalReducer, openAccessDeniedModal: openAccessDeniedModalReducer, openSelectProjectModal: openSelectProjectModalReducer, + openLicenseModal: openLicenseModalReducer, }, }); @@ -50,6 +52,7 @@ export const { openErrorReportModal, openAccessDeniedModal, openSelectProjectModal, + openLicenseModal, } = modalSlice.actions; export default modalSlice.reducer; diff --git a/src/types/modal.ts b/src/types/modal.ts index 867b764c5570ec0ab54a9e69771c981b2d0b9fa1..1030c46b8b181cbc81f54e7e74054064825b6e16 100644 --- a/src/types/modal.ts +++ b/src/types/modal.ts @@ -4,6 +4,7 @@ export type ModalName = | 'overview-images' | 'mol-art' | 'login' + | 'license' | 'publications' | 'edit-overlay' | 'error-report'