Skip to content
Merged
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
16 changes: 11 additions & 5 deletions src/components/SingleTermView/OverView/Details.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import {
} from "@mui/material";
import PropTypes from "prop-types";
import OpenInNewOutlinedIcon from '@mui/icons-material/OpenInNewOutlined';
import { formatTimestamp } from "../../../utils";

import { vars } from "../../../theme/variables";
const { gray800, gray500 } = vars;

const Details = ({loading, data }) => {
const Details = ({ loading, data, jsonData }) => {
const handleChipClick = (url) => {
window.open(url, '_blank');
};
Expand All @@ -34,6 +35,10 @@ const Details = ({loading, data }) => {
if (!data) {
return <div>No data available</div>;
}
const graphArray = jsonData["@graph"];
const lastGraphItem = graphArray[graphArray.length - 1]
const versionIRI = lastGraphItem?.["owl:versionIRI"]?.["@id"];
const versionInfo = formatTimestamp(lastGraphItem?.["owl:versionInfo"]);

return (
<>
Expand Down Expand Up @@ -75,7 +80,7 @@ const Details = ({loading, data }) => {
Existing IDs
</Typography>
<Box display="flex" flexWrap="wrap" gap=".5rem">
{data?.existingID && ( processExistingIds(data?.existingID).map((id) =>
{data?.existingID && (processExistingIds(data?.existingID).map((id) =>
<Chip className="rounded IDchip-outlined" variant="outlined" key={id} label={id} icon={<OpenInNewOutlinedIcon />} onClick={() => handleChipClick(id)} />
))}
</Box>
Expand Down Expand Up @@ -111,7 +116,7 @@ const Details = ({loading, data }) => {
Version
</Typography>
<Typography fontSize=".875rem" color={gray500}>
{data?.versionInfo}
{versionIRI.split('/version/')[1].split('/')[0]}
</Typography>
</Stack>
</Grid>
Expand Down Expand Up @@ -151,7 +156,7 @@ const Details = ({loading, data }) => {
Last modify timestamp
</Typography>
<Typography fontSize=".875rem" color={gray500}>
{data?.lastModifyTimestamp}
{versionInfo}
</Typography>
</Stack>
</Grid>
Expand All @@ -162,7 +167,8 @@ const Details = ({loading, data }) => {

Details.propTypes = {
loading: PropTypes.bool.isRequired,
data: PropTypes.object
data: PropTypes.object,
jsonData: PropTypes.object
};

export default Details;
23 changes: 16 additions & 7 deletions src/components/SingleTermView/OverView/OverView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,52 @@ import PropTypes from 'prop-types';
import Hierarchy from "./Hierarchy";
import Predicates from "./Predicates";
import RawDataViewer from "./RawDataViewer";
import {useCallback, useEffect, useMemo, useState} from "react";
import { getMatchTerms } from "../../../api/endpoints";
import { useCallback, useEffect, useMemo, useState } from "react";
import { getMatchTerms, getRawData } from "../../../api/endpoints";

const OverView = ({ searchTerm, isCodeViewVisible, selectedDataFormat }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

const [jsonData, setJsonData] = useState(null);

// eslint-disable-next-line react-hooks/exhaustive-deps
const fetchTerms = useCallback(
debounce((searchTerm) => {
if (searchTerm) {
getMatchTerms("base", searchTerm).then(data => {
console.log("data from api call: ", data)
setData(data?.results?.[0]);
setLoading(false);
});
}
}, 300),
[]
);


const fetchJSONFile = useCallback(() => {
getRawData("base", searchTerm, 'jsonld').then(rawResponse => {
setJsonData(rawResponse);
})
}, [searchTerm]);

useEffect(() => {
setLoading(true);
fetchTerms(searchTerm);
fetchJSONFile();
return () => {
fetchTerms.cancel();
};
}, [searchTerm, fetchTerms]);
}, [searchTerm, fetchTerms, fetchJSONFile]);

const memoData = useMemo(() => data, [data]);

return (
<Box p="2.5rem 5rem" sx={{
overflow: 'auto',
}}>
{isCodeViewVisible ? <RawDataViewer dataId={searchTerm} dataFormat={selectedDataFormat} /> :
<>
<Details data={memoData} loading={loading} />
<Details data={memoData} jsonData={jsonData} loading={loading} />
<Box p='5rem 0'>
<Divider />
<Grid container pt='5.25rem' spacing='2.75rem'>
Expand Down
50 changes: 31 additions & 19 deletions src/components/SingleTermView/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import ToggleButton from '@mui/material/ToggleButton';
import CustomBreadcrumbs from "../common/CustomBreadcrumbs";
import ForkRightIcon from '@mui/icons-material/ForkRight';
import { vars } from "../../theme/variables";
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
import OntologySearch from "./OntologySearch";
import ModeEditOutlineOutlinedIcon from '@mui/icons-material/ModeEditOutlineOutlined';
import RateReviewOutlinedIcon from '@mui/icons-material/RateReviewOutlined';
Expand Down Expand Up @@ -45,10 +44,18 @@ import CreateForkDialog from "./CreateForkDialog";
import TermDialog from "../TermEditor/TermDialog";
import { getSelectedTermLabel } from "../../api/endpoints/apiService";
import { GlobalDataContext } from "../../contexts/DataContext";
import { getRawData } from "../../api/endpoints";

const { gray200, brand700, gray600 } = vars;
const { gray200, gray600 } = vars;

const dataFormats = ['JSON-LD', 'Turtle', 'N3', 'OWL', 'CSV']
const dataFormats = ['JSON-LD', 'Turtle', 'N3', 'OWL', 'CSV'];
const formatExtensions = {
'JSON-LD': 'jsonld',
'Turtle': 'ttl',
'N3': 'n3',
'OWL': 'owl',
'CSV': 'csv'
};

const SingleTermView = () => {
const [open, setOpen] = useState(false);
Expand Down Expand Up @@ -93,6 +100,8 @@ const SingleTermView = () => {
const handleDataFormatMenuItemClick = (value) => {
setSelectedDataFormat(value);
setDataFormatAnchorEl(null);

downloadFormattedData(value);
};

const handleOpenRequestMergeDialog = () => {
Expand All @@ -118,15 +127,30 @@ const SingleTermView = () => {
setTabValue(newValue);
};

const downloadFormattedData = (dataFormat) => {
getRawData("base", searchTerm, formatExtensions[dataFormat]).then(rawResponse => {
const formattedData = JSON.stringify(rawResponse, null, 2);
const blob = new Blob([formattedData], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `data.${formatExtensions[dataFormat]}`;
a.click();
URL.revokeObjectURL(url);
}).catch(error => {
console.error('Error downloading data:', error);
});
}

const CodeOrTreeIcon = () => {
return isCodeViewVisible ? <CodeIcon /> : <AccountTreeOutlined />
}

const breadcrumbItems = [
{ label: '', href: '/', icon: HomeOutlinedIcon },
{ label: 'Term search', href: `/search?searchTerm=${searchTerm}` },
{ label: 'My organization 1', href: '#' },
{ label: 'ILX:0101901' },
{ label: 'Term search', href: `/search?searchTerm=${storedSearchTerm}` },
{ label: 'base', href: '#' },
{ label: searchTerm.toUpperCase().replace("_", ":") },
];

useEffect(() => {
Expand All @@ -148,19 +172,7 @@ const SingleTermView = () => {
<Box p="1.5rem 5rem 0rem 5rem">
<Grid container>
<Grid container xs={12} lg={12} direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" spacing=".75rem">
<CustomBreadcrumbs breadcrumbItems={breadcrumbItems} />
<ForkRightIcon fontSize="medium" htmlColor={brand700} />
<Typography color={brand700} fontSize="0.875rem" fontWeight={600}>
fork1
</Typography>
<Chip
icon={<FiberManualRecordIcon />}
label="Not merged"
variant="outlined"
className="rounded not-merged"
/>
</Stack>
<CustomBreadcrumbs breadcrumbItems={breadcrumbItems} />
<Stack direction="row" alignItems="center" gap={1}>
<Typography variant="caption" sx={{ fontSize: '0.875rem', color: gray600 }}>Active Ontology:</Typography>
<OntologySearch />
Expand Down
66 changes: 44 additions & 22 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,41 +45,63 @@ export const stableSort = (array, comparator) => {

export function compareArrays(originalArray, modifiedArray) {
if (!Array.isArray(originalArray) || !Array.isArray(modifiedArray)) {
console.warn("compareArrays received invalid input", { originalArray, modifiedArray });
return [];
console.warn("compareArrays received invalid input", { originalArray, modifiedArray });
return [];
}

return originalArray.filter(item => !modifiedArray.includes(item));
}

export function compareSentences(originalText, modifiedText) {
// Split texts into sentences
const originalSentences = originalText.match(/[^.!?]+[.!?]+/g) || [];
const modifiedSentences = modifiedText.match(/[^.!?]+[.!?]+/g) || [];
// Find sentences in original that are not in modified
const uniqueSentences = originalSentences.filter(sentence =>
!modifiedSentences.some(modSentence =>
sentence.trim().toLowerCase() === modSentence.trim().toLowerCase()
)
);
return uniqueSentences;
// Split texts into sentences
const originalSentences = originalText.match(/[^.!?]+[.!?]+/g) || [];
const modifiedSentences = modifiedText.match(/[^.!?]+[.!?]+/g) || [];

// Find sentences in original that are not in modified
const uniqueSentences = originalSentences.filter(sentence =>
!modifiedSentences.some(modSentence =>
sentence.trim().toLowerCase() === modSentence.trim().toLowerCase()
)
);
return uniqueSentences;
}

export function compareStrings(originalString, modifiedString) {
return originalString !== modifiedString ? [originalString] : []
return originalString !== modifiedString ? [originalString] : []
}

export function compareObjects(originalObject, modifiedObject) {
return Object.keys(originalObject).filter((key) => originalObject[key] !== modifiedObject[key])
}

export function getDataType(data) {
if (Array.isArray(data)) return "array"
if (typeof data === "string") return "string"
if (typeof data === "number") return "number"
if (typeof data === "boolean") return "boolean"
if (data === null) return "null"
if (typeof data === "object") return "object"
return "unknown"
if (Array.isArray(data)) return "array"
if (typeof data === "string") return "string"
if (typeof data === "number") return "number"
if (typeof data === "boolean") return "boolean"
if (data === null) return "null"
if (typeof data === "object") return "object"
return "unknown"
}

export function formatTimestamp(rawDate) {
if (!rawDate || typeof rawDate !== 'string') return 'Invalid date';

try {
const cleanedDateStr = rawDate.replace(',', '.');
const date = new Date(cleanedDateStr);

if (isNaN(date)) return 'Invalid date';

const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
const hh = String(date.getHours()).padStart(2, '0');
const min = String(date.getMinutes()).padStart(2, '0');

return `${yyyy}-${mm}-${dd} ${hh}:${min}`;
} catch (e) {
return 'Invalid date';
}
}