diff --git a/.gitignore b/.gitignore index 5b26e50d317fa3e212552d36eae77bb055775365..12063c9603192d68c47b3bf4aeec6619db4cacf0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules # next.js build files .next +next-env.d.ts # environment variables .env .env.* diff --git a/Dockerfile b/Dockerfile index 7f7a9d606e484bb8804a787646c68558954016ef..7c511c845e2069f43fbaa726d73855909eb01d4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,12 @@ FROM refinedev/node:18 AS base +RUN npm install -g --update npm + FROM base AS deps RUN apk add --no-cache libc6-compat + COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ RUN \ diff --git a/next-env.d.ts b/next-env.d.ts deleted file mode 100644 index a4a7b3f5cfa2f97534b4aa6588e12bd0b5df180b..0000000000000000000000000000000000000000 --- a/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// <reference types="next" /> -/// <reference types="next/image-types/global" /> - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/next.config.js b/next.config.js index e7e879c5b29d294bb3cf4cdca3c1b84de0c671b8..9a289c5774e4658d8946ac1f7a0430b881ea1711 100644 --- a/next.config.js +++ b/next.config.js @@ -4,4 +4,10 @@ module.exports = { i18n, transpilePackages: ["@refinedev/nextjs-router"], output: "standalone", + eslint: { + ignoreDuringBuilds: true, + }, + typescript: { + ignoreBuildErrors: true, + }, }; diff --git a/package-lock.json b/package-lock.json index 776ada37ac2dfbd5772862ebc35a289b264de4f2..00b8d68a9e0ec414abf10b17f66c0010c477968b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "nookies": "^2.5.2", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-json-view-lite": "^1.5.0", "styled-components": "^6.1.11", "unstorage": "^1.10.1" }, @@ -10343,6 +10344,18 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/react-json-view-lite": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.5.0.tgz", + "integrity": "sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-markdown": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-6.0.3.tgz", diff --git a/package.json b/package.json index edeb12c2b747bb1f0ecb9b61dadde8116b8911a1..3c4c6a389294ea00ecda66673d8a90e80823178f 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "nookies": "^2.5.2", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-json-view-lite": "^1.5.0", "styled-components": "^6.1.11", "unstorage": "^1.10.1" }, diff --git a/pages/fdo/show/[prefix]/[suffix].tsx b/pages/fdo/show/[prefix]/[suffix].tsx index 8412158bd4dc5b874112f05e9d7316b3daadd3e5..ce5ee795c35012b8ddc6cba7d46e1321b4ee90f7 100644 --- a/pages/fdo/show/[prefix]/[suffix].tsx +++ b/pages/fdo/show/[prefix]/[suffix].tsx @@ -3,10 +3,13 @@ import React from 'react' import { ErrorComponent } from '../../../../src/components/ErrorComponent' import { GetServerSideProps } from 'next' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -import { useShow, useTranslate, useParsed, useCustom, useApiUrl } from '@refinedev/core' +import { useTranslate, useParsed, useCustom, useApiUrl } from '@refinedev/core' import { Show } from '@refinedev/mui' import { Typography } from '@mui/material' import CircularProgress from '@mui/material/CircularProgress' +import AssignmentIcon from '@mui/icons-material/Assignment' +import Avatar from '@mui/material/Avatar' +import { blue } from '@mui/material/colors' import Box from '@mui/material/Box' import Stack from '@mui/material/Stack' import Chip from '@mui/material/Chip' @@ -23,8 +26,115 @@ import ContentPaste from '@mui/icons-material/ContentPaste' import Cloud from '@mui/icons-material/Cloud' import VerifiedIcon from '@mui/icons-material/Verified' import ReportIcon from '@mui/icons-material/Report' +import { JsonView, darkStyles, defaultStyles } from 'react-json-view-lite'; +import { styled, ThemeProvider } from '@mui/material/styles' +import Details from '../../../../src/components/fdos/details' +import IFdo from '../../../../src/interfaces/IFdo' +import { FDO_COMMUNITY_TYPE_EVEBS as EVEBS, FDO_COMMUNITY_MD_PROFILE_EDC as EDC, FDO_COMMUNITY_MD_PROFILE_AAS as AAS, HANDLE_SYSTEM_BASE_URI as HS_BASE_URI } from '../../../../src/constants' +import 'react-json-view-lite/dist/index.css'; -const resolvePid = (pid: string) => `https://hdl.handle.net/${pid}` +const resolvePid = (pid: string) => `${HS_BASE_URI}/${pid}` + +const getFdoDetails = (data: object) => { + console.log(data) + if(!data) return undefined + + const fdoDetails = { + isFdo: data["isFdo"], + typePid: data["fdoType"], + profilePid: data["fdoProfile"], + dataPid: data["dataPid"], + metadataPid: data["metadataPid"], + repository: data.attributes["0.TYPE/DOIPService"], + attributes: data["attributes"], + } + + return fdoDetails +} + +interface IJsonMetadata { + metadata: object +} + +interface IEvebsDetails { + metadataJson: IJsonMetadata + dataspace: string + dataspaceIcon: string + technology: string + technologyIcon: string +} + +const getEvebsDetails = (typePid, repository, metadataRecord) => { + console.log(metadataRecord); + const row = (category: string, classification: string, logo: string) => { + return { category, classification, logo } + } + const techInfo = metadataRecord.fdoProfile === AAS ? "BaSyx AAS" : "EDC"; + const techLogo = metadataRecord.fdoProfile === AAS ? "/images/aas.png" : "/images/eclipse-logo.png"; + const dataspaceInfo = metadataRecord.fdoProfile === AAS ? "RWTH" : "MDS"; + const dataspaceLogo = metadataRecord.fdoProfile === AAS ? "/images/rwth.png" : "/images/mds.png"; + + return [ + row('Type', <Link href={resolvePid(typePid)}>{ "EVEBS-FDO" }</Link>, ''), + row('Repository', <Link href="#">{repository}</Link>, + repository.toLowerCase().indexOf("cordra") > -1 ? "/images/cordra-primary-blue.png" : "/images/la_logo.png"), + + row('Dataspace', dataspaceInfo, dataspaceLogo), + row('Technology', techInfo, techLogo), + ] +} + +const EvebsDetails = ({typePid, repository, metadataPid}) => { + const apiUrl = useApiUrl() + const t = useTranslate() + const { data, isLoading, isError, error } = useCustom({ + url: `${HS_BASE_URI}/${metadataPid}?locatt=payloadIndex:0`, + method: 'get', + errorNotification: () => false, + queryOptions: { + retry: false + } + }) + const metadataRecord = useCustom({ + url: `${apiUrl}/fdo/${metadataPid}`, + method: 'get', + errorNotification: () => false, + queryOptions: { + retry: false + } + }); + + const metadata = data?.data; + + console.log(isLoading, data, repository, typePid, metadata); + return isLoading || metadataRecord.isLoading ? <Item>loading json</Item> : ( + <> + <Item sx={{ width: '50%' }} > + <Typography variant="h5" gutterBottom> {metadata?.displayName || metadata.idShort || metadata.id} </Typography> <Typography variant="subtitle1" gutterBottom > {metadata?.description[0].text} </Typography> + <Box> + <Details rows={getEvebsDetails(typePid, repository, metadataRecord.data.data)}></Details> + </Box> + </Item> + <Item> + <Box> + <Typography variant="h6" gutterBottom>Metadata (JSON)</Typography> + <JsonView data={metadata} shouldExpandNode={lvl => lvl<3} style={defaultStyles} /> + </Box> + </Item> + </> + ) +} + +const Item = styled(Paper)(({ theme }) => ({ + backgroundColor: '#fff', + ...theme.typography.body2, + padding: theme.spacing(1), + textAlign: 'left', + color: theme.palette.text.secondary, + ...theme.applyStyles('dark', { + backgroundColor: '#1A2027' + }) +})) const ShowFDO = () => { const { params } = useParsed() @@ -47,83 +157,96 @@ const ShowFDO = () => { const handleUrl = resolvePid(showId) const dataPid = data?.data?.dataPid const metadataPid = data?.data?.metadataPid - const profilePid = '21.T11969/141bf451b18a79d0fe66' + + const fdoDetails = getFdoDetails(data?.data); + const isEvebs = fdoDetails?.typePid === EVEBS return ( <Show isLoading={isLoading} - title={<Typography variant="h5">{ showId }{ !isLoading && (data?.data?.isFdo ? <Chip label="FDO" color="success" variant="outlined" /> : <Chip label="Not an FDO" color="error" variant="outlined" />)}</Typography>} + title={<Typography variant="h5">{ showId } + + { !isLoading && (data?.data?.isFdo ? <Chip label="FDO" color="success" variant="outlined" sx={{marginLeft: 1}}/> : <Chip label="Not an FDO" color="error" variant="outlined" sx={{marginLeft: 1}}/>)} + </Typography>} > { isLoading && <Box sx={{ textAlign: 'center' }}><CircularProgress/></Box>} - <Paper sx={{ width: 320, maxWidth: '100%' }}> - <MenuList> - <MenuItem> - <ListItemIcon> - <RadioButtonUncheckedIcon/> - </ListItemIcon> - <ListItemText> - <Link href={`${handleUrl}?noredirect`}>Handle Record</Link> - </ListItemText> - </MenuItem> - <MenuItem> - <ListItemIcon> - <StorageIcon/> - </ListItemIcon> - <ListItemText> - <Link href={handleUrl}>To Repository</Link> - </ListItemText> - </MenuItem> - { data?.data?.isFdo && (<> - <Divider /> - <MenuItem> - <ListItemIcon> - { dataPid - ? <VerifiedIcon color="success"/> - : <ReportIcon color="error"/> - } - </ListItemIcon> - <ListItemText> - { metadataPid - ? <Link href={resolvePid(dataPid)}>Data</Link> - : 'Data not available.' - } - </ListItemText> - </MenuItem> - <MenuItem> - <ListItemIcon> - { metadataPid - ? <VerifiedIcon color="success"/> - : <ReportIcon color="error"/> - } - </ListItemIcon> - <ListItemText> - { metadataPid - ? <Link href={resolvePid(metadataPid)}>Metadata</Link> - : 'Metadata not available.' - } - </ListItemText> - </MenuItem> - <MenuItem> - <ListItemIcon> - { profilePid - ? <VerifiedIcon color="success"/> - : <ReportIcon color="error"/> - } - </ListItemIcon> - <ListItemText> - { profilePid - ? <Link href={resolvePid(profilePid)}>Profile</Link> - : 'Profile not available.' - } - </ListItemText> - </MenuItem> - </>)} - </MenuList> - </Paper> + <div> + <Stack + direction="row" + // divider={<Divider orientation="vertical" flexItem />} + spacing={2} + > + <Item> + <MenuList> + <MenuItem> + <ListItemIcon> + <RadioButtonUncheckedIcon/> + </ListItemIcon> + <ListItemText> + <Link href={`${handleUrl}?noredirect`}>Handle Record</Link> + </ListItemText> + </MenuItem> + <MenuItem> + <ListItemIcon> + <StorageIcon/> + </ListItemIcon> + <ListItemText> + <Link href={handleUrl}>To Repository</Link> + </ListItemText> + </MenuItem> + { data?.data?.isFdo && (<> + <Divider /> + <MenuItem> + <ListItemIcon> + { dataPid + ? <VerifiedIcon color="success"/> + : <ReportIcon color="error"/> + } + </ListItemIcon> + <ListItemText> + { metadataPid + ? <Link href={resolvePid(dataPid)}>Data</Link> + : 'Data not available.' + } + </ListItemText> + </MenuItem> + <MenuItem> + <ListItemIcon> + { metadataPid + ? <VerifiedIcon color="success"/> + : <ReportIcon color="error"/> + } + </ListItemIcon> + <ListItemText> + { metadataPid + ? <Link href={resolvePid(metadataPid)}>Metadata</Link> + : 'Metadata not available.' + } + </ListItemText> + </MenuItem> + <MenuItem> + <ListItemIcon> + { fdoDetails.profilePid + ? <VerifiedIcon color="success"/> + : <ReportIcon color="error"/> + } + </ListItemIcon> + <ListItemText> + { fdoDetails.profilePid + ? <Link href={resolvePid(fdoDetails.profilePid)}>Profile</Link> + : 'Profile not available.' + } + </ListItemText> + </MenuItem> + </>)} + </MenuList> + </Item> + { isEvebs && <EvebsDetails {...fdoDetails}/> } + </Stack> + </div><br/><br/> </Show> - ) } diff --git a/src/components/fdos/details.tsx b/src/components/fdos/details.tsx index 04a91d9816bdb734403fb308da87a6170d2123a4..29a44b95a865d8dbef0ae1f781671f9822046466 100644 --- a/src/components/fdos/details.tsx +++ b/src/components/fdos/details.tsx @@ -8,6 +8,7 @@ import TableHead from '@mui/material/TableHead' import TableRow from '@mui/material/TableRow' import Paper from '@mui/material/Paper' import Typography from '@mui/material/Typography' +import Link from '@mui/material/Link' const StyledTableCell = styled(TableCell)(({ theme }) => ({ [`&.${tableCellClasses.head}`]: { @@ -15,7 +16,7 @@ const StyledTableCell = styled(TableCell)(({ theme }) => ({ color: theme.palette.common.white }, [`&.${tableCellClasses.body}`]: { - fontSize: 16, + fontSize: 14, backgroundColor: theme.palette.common.white, ...theme.applyStyles('dark', { backgroundColor: '#262c32' @@ -31,11 +32,6 @@ const StyledTableRow = styled(TableRow)(({ theme }) => ({ } })) -const typeInfo = 'CETTS' -const repoInfo = 'LinkAhead' -const dataSpInfo = 'RWTH' -const techInfo = 'EDC' - function createData ( category: string, classification: string, @@ -45,36 +41,26 @@ function createData ( return { category, classification, logo } } -const rows = [ - createData('Type:', typeInfo, ''), - createData('Repository:', repoInfo, ''), - createData('Dataspace:', dataSpInfo, ''), - createData('Technology:', techInfo, '') -] -export default function CustomizedTables () { +export default function CustomizedTables ({rows}) { return ( - <TableContainer component={Paper}> - <Table aria-label="simple table"> - <TableHead> - {/* <TableRow> - <StyledTableCell>Info1 </StyledTableCell> - <StyledTableCell align="right">Info2</StyledTableCell> - <StyledTableCell align="right">Info3</StyledTableCell> - </TableRow> */} - </TableHead> + <Table aria-label="simple table" sx={{border: 0}}> <TableBody > {rows.map((row) => ( <StyledTableRow key={row.category}> <StyledTableCell component="th" scope="row" sx={{ border: 0 }}> - <Typography variant="h6" gutterBottom> {row.category}</Typography> + <Typography sx={{fontSize: 14}} gutterBottom> {row.category}</Typography> </StyledTableCell> <StyledTableCell align="left">{row.classification}</StyledTableCell> - <StyledTableCell align="left" sx={{ border: 0 }}>{row.logo}</StyledTableCell> + <StyledTableCell align="right" sx={{ maxWidth: 140 }}>{row.logo && <img + style={{ maxHeight: '35px' }} + src={row.logo} + title={row.category} + /> + }</StyledTableCell> </StyledTableRow> ))} </TableBody> </Table> - </TableContainer> ) } diff --git a/src/constants.js b/src/constants.js index c891c2a258058c222a7b36ecc91c542f31c7d6da..cee9f3c530d5c6813729221bdc2258bead1e18c5 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,3 +1,7 @@ import { version } from './version' export const API_URL = process.env.NEXT_PUBLIC_API_URL || '/api' export const FDO_MANAGER_WEBUI_VERSION = version +export const FDO_COMMUNITY_TYPE_EVEBS = process.env.NEXT_PUBLIC_FDO_COMMUNITY_TYPE_EVEBS || "21.T11966/f7f29218c8d5832ab5b5" +export const FDO_COMMUNITY_MD_PROFILE_AAS = process.env.NEXT_PUBLIC_FDO_COMMUNITY_MD_PROFILE_AAS || "21.T11966/49321f913c960ec3c943" +export const FDO_COMMUNITY_MD_PROFILE_EDC = process.env.NEXT_PUBLIC_FDO_COMMUNITY_MD_PROFILE_EDC || "21.T11966/3f1cfd3bb60bee84c22b" +export const HANDLE_SYSTEM_BASE_URI = process.env.NEXT_PUBLIC_HANDLE_SYSTEM_BASE_URI || "https://hdl.handle.net" diff --git a/src/providers/dataProvider.tsx b/src/providers/dataProvider.tsx index ad9336c3ff689963788784e108d9cab37b8e00ee..10d87ca601378b2f146b00a3f3446ecf48a68bb9 100644 --- a/src/providers/dataProvider.tsx +++ b/src/providers/dataProvider.tsx @@ -4,7 +4,12 @@ import { Configuration, ProfilesApi, RepositoriesApi, FDOsApi, InfoApi, LoggingA import axios from 'axios' const getNewlyCreated = async (newLocation: string) => { - return await axios.get(newLocation) + const response = await axios.get(newLocation) + console.log(response); + if (response.data.data) { + return response.data.data; + } + return response.data; } const apiDataProvider = (apiUrl: string): DataProvider => { @@ -66,7 +71,7 @@ const apiDataProvider = (apiUrl: string): DataProvider => { meta }) => { if (method === 'get') { - return { data: (await getNewlyCreated(url)).data.data } + return { data: (await getNewlyCreated(url)) } } else { throw new Error('Not implemented') } @@ -81,7 +86,7 @@ const apiDataProvider = (apiUrl: string): DataProvider => { if (response.status === 201) { const newLocation = response.headers.location const newData = await getNewlyCreated(newLocation) - return { data: newData.data.data } + return { data: newData } } else { throw new Error('Create with anything else than 201 not implemented.') }