Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance the code structure and UI of the network information #333

Open
wants to merge 1 commit into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
"test": "npm run test:playwright && npm run test:unit",
"test:unit": "jest unittest",
"test:playwright": "npx playwright test --trace on",
"build": "REACT_APP_GIT_COMMIT=$(git rev-parse HEAD) REACT_APP_BUILD_TIMESTAMP=$(git show -s --format=%cI HEAD) webpack --mode production",
"build": "set \"REACT_APP_GIT_COMMIT=%COMMIT_HASH%\" && set \"REACT_APP_BUILD_TIMESTAMP=%BUILD_TIMESTAMP%\" && webpack --mode production",
"build:netlify": "BUILD=netlify REACT_APP_GIT_COMMIT=$(git rev-parse HEAD) REACT_APP_BUILD_TIMESTAMP=$(git show -s --format=%cI HEAD) webpack --mode production",
"dev": "REACT_APP_GIT_COMMIT=$(git rev-parse --short HEAD) REACT_APP_BUILD_TIMESTAMP=$(git show -s --format=%cI HEAD) webpack serve --open --mode development",
"dev": "set \"REACT_APP_GIT_COMMIT=%COMMIT_HASH%\" && set \"REACT_APP_BUILD_TIMESTAMP=%BUILD_TIMESTAMP%\" && webpack serve --open --mode development",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain the reasoning for changing these scripts?

"lint": "eslint --ext .js,.ts,.jsx,.tsx src",
"format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'"
},
Expand Down
314 changes: 166 additions & 148 deletions src/components/TableBrowser/NetworkInfoPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,44 @@
import React from 'react'
import {
Typography,
Box,
Paper,
Grid,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Chip,
Card,
CardContent,
Divider,
Chip,
Paper,
useMediaQuery,
useTheme,
} from '@mui/material'

import { useNetworkSummaryStore } from '../../store/NetworkSummaryStore'
import { useWorkspaceStore } from '../../store/WorkspaceStore'
import parse from 'html-react-parser'
import React from 'react'

export function NetworkPropertyTable(): React.ReactElement {
const currentNetworkId = useWorkspaceStore(
(state) => state.workspace.currentNetworkId,
)
const networkInfo = useNetworkSummaryStore(
(state) => state.summaries[currentNetworkId],
)
const properties = networkInfo?.properties ?? []
return (
<TableContainer component={Paper} sx={{ height: 200, overflow: 'scroll' }}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Key</TableCell>
<TableCell>Value</TableCell>
</TableRow>
</TableHead>
<TableBody>
{properties.map((row) => (
<TableRow
key={row.predicateString}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell>{row.predicateString}</TableCell>
<TableCell>{row.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
type NdexNetworkProperty = {
predicateString: string
value: string | number | boolean | string[] | number[] | boolean[]
}

export default function NetworkInfoPanel(props: {
height: number
}): React.ReactElement {
const theme = useTheme()
const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm')) // Check if screen is small
const currentNetworkId = useWorkspaceStore(
(state) => state.workspace.currentNetworkId,
)
const networkInfo = useNetworkSummaryStore(
(state) => state.summaries[currentNetworkId],
)
const properties = networkInfo?.properties ?? []

const containsHtmlAnchor = (text: string) => {
return /<a\s+href=/i.test(text)
}
const properties: NdexNetworkProperty[] = networkInfo?.properties ?? []

const containsHtmlAnchor = (text: string) => /<a\s+href=/i.test(text)
const linkifyPlainTextUrls = (text: string) => {
const urlRegex = /(https?:\/\/[^\s]+)/g
return text.replace(
Expand All @@ -73,121 +48,164 @@ export default function NetworkInfoPanel(props: {
)
}

const capitalizeFirstLetter = (string: string): string => {
return string.charAt(0).toUpperCase() + string.slice(1)
}
const capitalizeFirstLetter = (string: string): string =>
string.charAt(0).toUpperCase() + string.slice(1)

return (
<Box sx={{ height: props.height, overflow: 'auto', pl: 1, pr: 1 }}>
<Box sx={{ mt: 1, display: 'flex', alignItems: 'center' }}>
<Typography variant="h6">{networkInfo?.name ?? ''}</Typography>
{networkInfo?.visibility != null ? (
<Chip sx={{ ml: 1 }} size="small" label={networkInfo?.visibility} />
) : null}
{networkInfo?.version != null ? (
<Chip
sx={{ ml: 1 }}
size="small"
label={`Version: ${networkInfo?.version}`}
/>
) : null}
</Box>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography
sx={{ ml: 1, mr: 4, fontSize: 14, color: 'gray' }}
variant="subtitle1"
>
{`Owner: ${networkInfo?.owner}`}
</Typography>
const renderTable = (data: NdexNetworkProperty[]) => (
<TableContainer
sx={{
maxHeight: isSmallScreen ? 200 : 'none',
backgroundColor: '#fafafa',
borderRadius: 2,
}}
>
<Table>
<TableBody>
{data.map((row, index) => (
<TableRow
key={index}
sx={{
'&:nth-of-type(odd)': { backgroundColor: '#f9f9f9' },
'&:hover': { backgroundColor: '#e0e0e0' },
}}
>
<TableCell>
<Typography sx={{ fontWeight: 'bold', fontSize: 14 }}>
{capitalizeFirstLetter(row.predicateString)}
</Typography>
</TableCell>
<TableCell>
<Typography sx={{ fontSize: 14 }}>
{containsHtmlAnchor(row.value.toString())
? parse(row.value.toString())
: parse(linkifyPlainTextUrls(row.value.toString()))}
</Typography>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)

<Typography
sx={{ mr: 4, fontSize: 14, color: 'gray' }}
variant="subtitle1"
>
{`Created: ${networkInfo?.creationTime.toLocaleString()}`}
</Typography>
<Typography
sx={{ mr: 1, fontSize: 14, color: 'gray' }}
variant="subtitle1"
>
{`Modified: ${networkInfo?.modificationTime.toLocaleString()}`}
</Typography>
</Box>
<Divider />
<Box sx={{ p: 1 }}>
<Box>
return (
<Box
sx={{
height: props.height,
overflow: 'auto',
px: 2,
backgroundColor: '#f4f4f4',
}}
>
{/* Header Section */}
<Grid
container
spacing={2}
alignItems="center"
justifyContent={isSmallScreen ? 'center' : 'space-between'}
sx={{ mt: 2 }}
>
<Grid item xs={12} sm="auto">
<Typography
sx={{ fontSize: 14, fontWeight: 'bold' }}
variant="subtitle1"
variant="h6"
sx={{ fontWeight: 'bold', textTransform: 'uppercase' }}
>
Description:
{networkInfo?.name ?? ''}
</Typography>
<Typography variant="body2">
{parse(networkInfo?.description ?? '')}
{properties
.filter(
(prop) =>
prop.predicateString.startsWith('rights') ||
prop.predicateString.startsWith('reference'),
)
.map((prop, index) => {
let displayValue: React.ReactNode

const valueString = prop.value.toString()

if (containsHtmlAnchor(valueString)) {
displayValue = parse(valueString)
} else {
displayValue = parse(linkifyPlainTextUrls(valueString))
}

return (
<div key={index}>
<span style={{ fontWeight: 'bold' }}>
{capitalizeFirstLetter(prop.predicateString)}:
</span>{' '}
{displayValue}
</div>
)
})}
</Typography>

</Grid>
<Grid item xs={12} sm="auto">
{networkInfo?.visibility && (
<Chip
sx={{
backgroundColor:
networkInfo?.visibility === 'PUBLIC' ? '#4caf50' : '#f44336',
color: 'white',
}}
size="small"
label={networkInfo?.visibility}
/>
)}
{networkInfo?.version && (
<Chip
sx={{ ml: 1 }}
size="small"
label={`Version: ${networkInfo?.version}`}
/>
)}
</Grid>
</Grid>
<Divider sx={{ my: 2 }} />
{/* Info Section */}
<Grid container spacing={2}>
<Grid item xs={12} sm={4}>
<Typography
sx={{ fontSize: 14, fontWeight: 'bold' }}
variant="subtitle1"
>
Properties:
sx={{ fontSize: 14, color: 'text.secondary' }}
>{`Owner: ${networkInfo?.owner}`}</Typography>
</Grid>
<Grid item xs={12} sm={4}>
<Typography sx={{ fontSize: 14, color: 'text.secondary' }}>
{`Created: ${networkInfo?.creationTime.toLocaleString()}`}
</Typography>
<Typography variant="body2" component="div">
{properties
.filter(
(prop) =>
!prop.predicateString.startsWith('__') &&
prop.predicateString !== 'description' &&
prop.predicateString !== 'reference' &&
prop.predicateString !== 'rights' &&
prop.predicateString !== 'rightsHolder',
)
.map((prop, index) => {
let displayValue: React.ReactNode

const valueString = prop.value.toString()

if (containsHtmlAnchor(valueString)) {
displayValue = parse(valueString)
} else {
displayValue = parse(linkifyPlainTextUrls(valueString))
}

return (
<div key={index}>
{capitalizeFirstLetter(prop.predicateString)}:{' '}
{displayValue}
</div>
)
})}
</Grid>
<Grid item xs={12} sm={4}>
<Typography sx={{ fontSize: 14, color: 'text.secondary' }}>
{`Modified: ${networkInfo?.modificationTime.toLocaleString()}`}
</Typography>
</Box>
</Grid>
</Grid>
<Box sx={{ mt: 3 }}>
{/* Description Section */}
<Card sx={{ mb: 3, borderRadius: 2, boxShadow: 1 }}>
<CardContent>
<Typography
sx={{
fontSize: 16,
fontWeight: 'bold',
mb: 2,
textTransform: 'uppercase',
}}
>
Description
</Typography>
<Paper elevation={1} sx={{ p: 2, backgroundColor: '#fff' }}>
{parse(networkInfo?.description ?? '')}
</Paper>
{renderTable(
properties.filter((prop) =>
['rights', 'rightsHolder', 'reference', 'description'].includes(
prop.predicateString,
),
),
)}
</CardContent>
</Card>
{/* Properties Section */}
<Card sx={{ borderRadius: 2, boxShadow: 2 }}>
<CardContent>
<Typography
sx={{
fontSize: 16,
fontWeight: 'bold',
mb: 2,
textTransform: 'uppercase',
}}
>
Properties
</Typography>
{renderTable(
properties.filter(
(prop) =>
![
'rights',
'rightsHolder',
'reference',
'description',
].includes(prop.predicateString) &&
!prop.predicateString.startsWith('__'),
),
)}
</CardContent>
</Card>
</Box>
</Box>
)
Expand Down