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
34 changes: 3 additions & 31 deletions src/components/CellCards/CellCard/CellCardPanel.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback } from "react";
import PropTypes from "prop-types";
import { useNavigate, useLocation } from "react-router-dom";
import { Box, Container, Stack, Skeleton, Alert, AlertTitle, Button, Typography } from "@mui/material";
import { Box, Container, Stack, Alert, AlertTitle, Button, Typography } from "@mui/material";
import CellCard from "./CellCard";
import EmptyState from "../../common/EmptyState";
import { useContextTerm } from "../../../hooks/useContextOntology";
Expand All @@ -14,9 +14,6 @@ import {
} from "../config/gridConfig";
import { useTermRecordAvailability } from "../../../hooks/useTermRecordAvailability";

// A three-column skeleton, so the (unavoidable) whole-ontology load reads as the page arriving
// rather than as a blank panel. Cold entry pays a ~16MB fetch + a 39,788-node parse before the
// first cell can render; navigating from the grid hits the memoized parse and skips both.
// PageContainer fixes the page height and expects each tab to scroll internally (OverView does the
// same). Without this the card runs on underneath the site footer.
//
Expand All @@ -29,26 +26,6 @@ const Scroll = ({ children }) => (

Scroll.propTypes = { children: PropTypes.node };

const LoadingSkeleton = () => (
<Container
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", lg: "26.5rem minmax(0, 1fr) 26.5rem" },
gap: 4,
py: 3,
}}
>
{[0, 1, 2].map((col) => (
<Stack key={col} gap={1}>
<Skeleton variant="text" width="45%" height={28} />
{Array.from({ length: col === 1 ? 8 : 6 }).map((_, i) => (
<Skeleton key={i} variant="text" />
))}
</Stack>
))}
</Container>
);

/**
* The Cell Card tab: resolves the term against the context ontology and renders the card.
*
Expand Down Expand Up @@ -93,13 +70,8 @@ const CellCardPanel = ({ term, group }) => {
[data, goToCell, group, ontologySlug]
);

if (loading) {
return (
<Scroll>
<LoadingSkeleton />
</Scroll>
);
}
// The page's loading overlay covers the ontology load (a ~16MB fetch + parse on cold entry).
if (loading) return null;

if (error) {
return (
Expand Down
4 changes: 3 additions & 1 deletion src/components/SingleTermView/CreateForkDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ HeaderRightSideContent.propTypes = {
isSaving: PropTypes.bool,
};

const CreateForkDialog = ({ open, handleClose, user, searchTerm, termLabel, group }) => {
const CreateForkDialog = ({ open, handleClose, onForkCreated, user, searchTerm, termLabel, group }) => {
const navigate = useNavigate();
const [ownerNotSupportedOpen, setOwnerNotSupportedOpen] = useState(false);
const [isSaving, setIsSaving] = useState(false);
Expand All @@ -45,6 +45,7 @@ const CreateForkDialog = ({ open, handleClose, user, searchTerm, termLabel, grou
if (result.ok) {
handleClose();
navigate(`/${groupname}/${searchTerm}/overview`);
onForkCreated?.();
} else {
setSaveError(`Fork creation failed (status ${result.status}). Please try again.`);
}
Expand Down Expand Up @@ -140,6 +141,7 @@ const CreateForkDialog = ({ open, handleClose, user, searchTerm, termLabel, grou
CreateForkDialog.propTypes = {
open: PropTypes.bool,
handleClose: PropTypes.func,
onForkCreated: PropTypes.func,
user: PropTypes.object,
searchTerm: PropTypes.string,
termLabel: PropTypes.string,
Expand Down
5 changes: 5 additions & 0 deletions src/components/SingleTermView/Discussion/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@
import { useState, useRef, useEffect } from "react";
import CommentEditor from "./CommentEditor";
import { getTermDiscussions } from "../../../api/endpoints/apiService";
import { useReportLoading } from "../../../contexts/pageLoading";

const { gray25, gray200, gray700 } = vars;

const Discussion = ({ term }) => {

Check failure on line 13 in src/components/SingleTermView/Discussion/index.jsx

View workflow job for this annotation

GitHub Actions / unit_test-lint

'term' is missing in props validation
const [comments, setComments] = useState([]);
const commentsEndRef = useRef(null);
const [discussions, setDiscussions] = useState([]);
const [showMockWarning, setShowMockWarning] = useState(true);
const [loading, setLoading] = useState(true);
useReportLoading(loading);

const getDiscussions = async () => {
try {
const data = await getTermDiscussions("base", term)
setDiscussions(Array.isArray(data) ? data : [])
} catch (error) {
setDiscussions([])
} finally {
setLoading(false)
}
}

Expand Down
47 changes: 47 additions & 0 deletions src/components/SingleTermView/ExistingForkDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import PropTypes from "prop-types";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from "@mui/material";
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";

const ExistingForkDialog = ({ open, handleClose, groupname, termLabel, onUseExisting, onStartFromScratch }) => (
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="existing-fork-dialog-title"
aria-describedby="existing-fork-dialog-description"
maxWidth="sm"
fullWidth
>
<DialogTitle id="existing-fork-dialog-title">You already have a fork of this term</DialogTitle>
<DialogContent>
<DialogContentText id="existing-fork-dialog-description">
&quot;{termLabel}&quot; is already forked under {groupname}. Continue working on that fork, or start
a scratch version from the current curated term instead.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button variant="outlined" onClick={handleClose}>Cancel</Button>
<Button variant="outlined" onClick={onStartFromScratch}>Start from scratch</Button>
<Button variant="contained" endIcon={<ArrowForwardIcon />} onClick={onUseExisting}>
Use existing fork
</Button>
</DialogActions>
</Dialog>
);

ExistingForkDialog.propTypes = {
open: PropTypes.bool.isRequired,
handleClose: PropTypes.func.isRequired,
groupname: PropTypes.string,
termLabel: PropTypes.string,
onUseExisting: PropTypes.func.isRequired,
onStartFromScratch: PropTypes.func.isRequired,
};

export default ExistingForkDialog;
8 changes: 2 additions & 6 deletions src/components/SingleTermView/History/HistoryPanel.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import PropTypes from 'prop-types';
import HistoryItem from "./HistoryItem";
import { Box, List, CircularProgress } from "@mui/material";
import { Box, List } from "@mui/material";
import { vars } from "../../../theme/variables";

const { gray50 } = vars;
Expand All @@ -27,11 +27,7 @@ const HistoryPanel = ({ /*searchTerm, group = "base",*/ versionsData, versionsLo
);
}, [versionsData]);

if (versionsLoading) {
return <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
<CircularProgress />
</Box>
}
if (versionsLoading) return null;

if (!versions.length) return <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
No version history found
Expand Down
2 changes: 2 additions & 0 deletions src/components/SingleTermView/OntologySearch.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import FolderSharedOutlinedIcon from '@mui/icons-material/FolderSharedOutlined';
import { vars } from "../../theme/variables";
import { getOrganizationsOntologies, getOntologyTerms } from "../../api/endpoints/apiService";
import { GlobalDataContext } from "../../contexts/DataContext";
import { useReportLoading } from "../../contexts/pageLoading";

const { brand600, gray50, gray300, gray400, white, gray700, gray200, paperShadow } = vars;

Expand Down Expand Up @@ -90,6 +91,7 @@ const OntologySearch = ({ placeholder, fullWidth = false, disabled, extra, userG
const [selectedValue, setSelectedValue] = useState(null);
const [ontologies, setOntologies] = useState([]);
const [loading, setLoading] = useState(false);
useReportLoading(loading);
const autocompleteRef = useRef(null);
const popperRef = useRef(null);

Expand Down
8 changes: 2 additions & 6 deletions src/components/SingleTermView/OverView/Details.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
Box,
Chip, CircularProgress,
Chip,
Grid,
Stack,
Tooltip,
Expand Down Expand Up @@ -55,11 +55,7 @@ const Details = ({ loading, data, jsonData, group = "base", termVersion, onMutat
return { synonyms, related };
};

if (loading) {
return <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<CircularProgress />
</Box>
}
if (loading) return null;

if (!data) {
return <div>No data available</div>;
Expand Down
7 changes: 1 addition & 6 deletions src/components/SingleTermView/OverView/Hierarchy.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
Divider,
Stack,
Typography,
CircularProgress
} from "@mui/material";
import { vars } from "../../../theme/variables";
import { RestartAlt, TargetCross } from "../../../Icons";
Expand Down Expand Up @@ -141,11 +140,7 @@ const Hierarchy = ({
[items, searchTerm]
);

if (loading) {
return <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<CircularProgress />
</Box>
}
if (loading) return null;


return (
Expand Down
4 changes: 4 additions & 0 deletions src/components/SingleTermView/OverView/OverviewSections.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Details from "./Details";
import Hierarchy from "./Hierarchy";
import Predicates from "./Predicates";
import { useObservable } from "./overviewStore";
import { useReportLoading } from "../../../contexts/pageLoading";
import { focusNodeFromJsonLd } from "../../../parsers/predicateMutations";

const SUBCLASS_OF_IRI = "http://www.w3.org/2000/01/rdf-schema#subClassOf";
Expand All @@ -16,6 +17,7 @@ const SUBCLASS_OF_IRI = "http://www.w3.org/2000/01/rdf-schema#subClassOf";

export const DetailsSection = memo(function DetailsSection({ subject, group, termVersion, onMutate, reserveHeight }) {
const { loading, data, jsonData } = useObservable(subject);
useReportLoading(loading);
return (
<Box sx={{ minHeight: loading ? reserveHeight : undefined }}>
<Details loading={loading} data={data} jsonData={jsonData} group={group} termVersion={termVersion} onMutate={onMutate} />
Expand All @@ -41,6 +43,7 @@ export const HierarchySection = memo(function HierarchySection({
reserveHeight,
}) {
const { loading, options, treeChildren, treeSuperclasses } = useObservable(subject);
useReportLoading(loading);
const selectedValue = useObservable(selectedSubject);
// Direct superclasses are the only editable part of the hierarchy: they are
// rdfs:subClassOf triples ON the focus node. Children are the same predicate
Expand Down Expand Up @@ -89,6 +92,7 @@ export const PredicatesSection = memo(function PredicatesSection({
reserveHeight,
}) {
const { loading, data, focusId } = useObservable(subject);
useReportLoading(loading);
return (
<Box sx={{ minHeight: loading ? reserveHeight : undefined }}>
<Predicates
Expand Down
7 changes: 1 addition & 6 deletions src/components/SingleTermView/OverView/Predicates.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import RemoveIcon from "@mui/icons-material/Remove";
import ObjectInput from "./ObjectInput";
import PredicatesAccordion from "./PredicatesAccordion";
import CreatePredicateDialog from "./CreatePredicateDialog";
import CircularProgress from '@mui/material/CircularProgress';
import AddOutlinedIcon from '@mui/icons-material/AddOutlined';
import CheckOutlinedIcon from "@mui/icons-material/CheckOutlined";
import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined";
Expand Down Expand Up @@ -119,11 +118,7 @@ const Predicates = ({ data, isGraphVisible, loading, focusId, group, onMutate })
setCreateDialog({ open: false, initialLabel: "" });
};

if (loading) {
return <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<CircularProgress />
</Box>
}
if (loading) return null;

return (
<Box display="flex" flexDirection="column" gap=".75rem">
Expand Down
21 changes: 14 additions & 7 deletions src/components/SingleTermView/OverView/RawDataViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useState, useEffect } from 'react';
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
import { a11yLight } from 'react-syntax-highlighter/dist/esm/styles/hljs';
import { getRawData, getTermVersion } from '../../../api/endpoints/apiService';
import { useReportLoading } from '../../../contexts/pageLoading';

import { vars } from '../../../theme/variables';
const { gray25, gray200, gray500 } = vars;
Expand All @@ -26,8 +27,8 @@ const formatExtensions = {

const RawDataViewer = ({ dataId, dataFormat, group = "base", versionHash }) => {
const [formattedData, setFormattedData] = useState(null);
// eslint-disable-next-line no-unused-vars
const [loading, setLoading] = useState(true);
useReportLoading(loading);

useEffect(() => {
setFormattedData(null);
Expand All @@ -37,10 +38,16 @@ const RawDataViewer = ({ dataId, dataFormat, group = "base", versionHash }) => {
const request = versionHash
? getTermVersion(group, dataId, versionHash)
: getRawData(group, dataId, formatExtensions[dataFormat]);
request.then(rawResponse => {
setFormattedData(JSON.stringify(rawResponse, null, 2));
setLoading(false);
});
let active = true;
request
.then(rawResponse => {
if (active) setFormattedData(JSON.stringify(rawResponse, null, 2));
})
.catch(error => console.error('Error loading raw data:', error))
.finally(() => {
if (active) setLoading(false);
});
return () => { active = false; };
}, [dataId, dataFormat, group, versionHash]);

return (
Expand All @@ -66,8 +73,8 @@ const RawDataViewer = ({ dataId, dataFormat, group = "base", versionHash }) => {
>
{formattedData}
</SyntaxHighlighter>
) : (
<div>Loading...</div>
) : !loading && (
<div>No data available</div>
)}
</div>
);
Expand Down
8 changes: 2 additions & 6 deletions src/components/SingleTermView/Variants/VariantsPanel.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { Box, CircularProgress } from '@mui/material';
import { Box } from '@mui/material';
import VariantsTable from './VariantsTable';
import ErrorModal from '../../common/ErrorModal';

Expand Down Expand Up @@ -49,11 +49,7 @@ const mapVersionsToRows = (data) => {
const VariantsPanel = ({ searchTerm, group = "base", versionsData, versionsLoading, versionsError, onDismissError }) => {
const variants = React.useMemo(() => mapVersionsToRows(versionsData), [versionsData]);

if (versionsLoading) {
return <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '400px' }}>
<CircularProgress />
</Box>
}
if (versionsLoading) return null;

if (versionsError) {
return <ErrorModal
Expand Down
Loading
Loading