diff --git a/src/components/Dashboard/Ontologies/OntologyCard.jsx b/src/components/Dashboard/Ontologies/OntologyCard.jsx
new file mode 100644
index 0000000..babe13b
--- /dev/null
+++ b/src/components/Dashboard/Ontologies/OntologyCard.jsx
@@ -0,0 +1,67 @@
+import PropTypes from 'prop-types';
+import {Box, Chip, Grid, Stack, Typography} from "@mui/material";
+
+import { vars } from "../../../theme/variables";
+const {gray500, gray700, gray200, brand600, brand200, brand50 } = vars;
+
+const OntologyCard = ({ ontology, onSelect }) => {
+ const handleClick = () => {
+ // The cell card overview component is not built yet - the parent shows a
+ // placeholder dialog for now.
+ onSelect(ontology);
+ };
+
+ return (
+
+
+
+
+ {ontology?.label}
+ {ontology?.badge && }
+
+
+
+ {ontology?.description ? ontology?.description : '-'}
+
+
+
+ );
+};
+
+OntologyCard.propTypes = {
+ ontology: PropTypes.object.isRequired,
+ onSelect: PropTypes.func.isRequired
+};
+
+export default OntologyCard;
diff --git a/src/components/Dashboard/Ontologies/index.jsx b/src/components/Dashboard/Ontologies/index.jsx
new file mode 100644
index 0000000..c8231db
--- /dev/null
+++ b/src/components/Dashboard/Ontologies/index.jsx
@@ -0,0 +1,135 @@
+import OntologyCard from "./OntologyCard";
+import { useCallback, useContext, useEffect, useState } from "react";
+import { ListIcon, TableChartIcon } from "../../../Icons";
+import CustomPagination from "../../common/CustomPagination";
+import CustomViewButton from "../../common/CustomViewButton";
+import CustomSingleSelect from "../../common/CustomSingleSelect";
+import MessageDialog from "../../common/MessageDialog";
+import { getOrganizationsOntologies } from "../../../api/endpoints/apiService";
+import {Box, ButtonGroup, CircularProgress, Grid, Stack, Typography} from "@mui/material";
+import { GlobalDataContext } from "../../../contexts/DataContext";
+
+import { vars } from "../../../theme/variables";
+const { gray600 } = vars;
+
+const NOT_IMPLEMENTED_MESSAGE = "The cell card overview is not yet implemented";
+
+const Ontologies = () => {
+ const { user } = useContext(GlobalDataContext);
+ const groupname = user?.groupname;
+ const [loading, setLoading] = useState(false);
+ const [numberOfVisiblePages, setNumberOfVisiblePages] = useState(8);
+ const [listView, setListView] = useState('list');
+ const [ontologies, setOntologies] = useState([]);
+ const [page, setPage] = useState(1);
+ const [slicedOntologies, setSlicedOntologies] = useState([]);
+ const [message, setMessage] = useState('');
+
+ const handleNumberOfPagesChange = (v) => {
+ setNumberOfVisiblePages(v);
+ setPage(1);
+ };
+
+ const fetchOntologies = useCallback(async () => {
+ if (!groupname) return;
+ setLoading(true);
+ getOrganizationsOntologies(groupname).then(data => {
+ // Same transform as OntologySearch: derive a readable label, fall back to
+ // an identifier parsed from the spec URI when there's no title.
+ const parsed = (Array.isArray(data) ? data : []).map((ontology, index) => {
+ let fallbackLabel = `Unknown Ontology ${ontology.id || index + 1}`;
+ if (ontology.uri) {
+ const uriParts = ontology.uri.split('/');
+ const specIndex = uriParts.findIndex(part => part === 'spec');
+ if (specIndex > 0) {
+ fallbackLabel = `Ontology ${uriParts[specIndex - 1]}`;
+ }
+ }
+ return {
+ label: ontology.title || fallbackLabel,
+ badge: groupname,
+ id: ontology.id || `ontology-${index}`,
+ description: ontology.uri,
+ url: ontology.url,
+ };
+ });
+ setOntologies(parsed);
+ setPage(1);
+ setLoading(false);
+ }).catch(err => {
+ console.log(err);
+ setLoading(false);
+ });
+ }, [groupname]);
+
+ useEffect(() => {
+ fetchOntologies();
+ }, [fetchOntologies]);
+
+ useEffect(() => {
+ const start = (page - 1) * numberOfVisiblePages;
+ const end = start + numberOfVisiblePages;
+ setSlicedOntologies(ontologies.slice(start, end));
+ }, [ontologies, page, numberOfVisiblePages]);
+
+ const handlePageChange = (event, value) => {
+ setPage(value);
+ };
+
+ const handleSelectOntology = () => {
+ // Placeholder until the cell card overview component is developed.
+ setMessage(NOT_IMPLEMENTED_MESSAGE);
+ };
+
+ return (
+
+
+
+ My ontologies
+
+
+
+ Show on page:
+
+
+
+ setListView('list')}
+ icon={}
+ />
+ setListView('table')}
+ icon={}
+ />
+
+
+
+ {loading ?
+
+ :
+ {
+ slicedOntologies.map((ontology, index) => (
+
+ ))
+ }
+ }
+
+ {message && setMessage('')} message={message} />}
+
+ );
+};
+
+export default Ontologies;
diff --git a/src/components/Dashboard/index.jsx b/src/components/Dashboard/index.jsx
index 15de8a4..2f4838b 100644
--- a/src/components/Dashboard/index.jsx
+++ b/src/components/Dashboard/index.jsx
@@ -2,6 +2,7 @@ import {Box} from "@mui/material";
import { useState } from "react";
import EditBulkTermsDialog from "./EditBulkTerms/EditBulkTermsDialog";
import Variants from "./Variants";
+import Ontologies from "./Ontologies";
import TermsChange from "./TermsChange";
import Organizations from "./Organizations";
import User from "./User";
@@ -22,6 +23,7 @@ const Dashboard = () => {
+
diff --git a/src/components/SingleOrganization/AddNewOntologyDialog.jsx b/src/components/SingleOrganization/AddNewOntologyDialog.jsx
index afe39ed..5a38be3 100644
--- a/src/components/SingleOrganization/AddNewOntologyDialog.jsx
+++ b/src/components/SingleOrganization/AddNewOntologyDialog.jsx
@@ -8,7 +8,7 @@ import CustomizedDialog from "../common/CustomizedDialog";
import ImportFileTab from "./../TermEditor/ImportFileTab";
import BasicTabs from "../common/CustomTabs";
import { useState, useCallback } from "react";
-import { createNewOntology } from "../../api/endpoints/apiService";
+import { createNewOntology, getOntologyTerms } from "../../api/endpoints/apiService";
import { API_CONFIG } from "../../config";
import { GlobalDataContext } from "../../contexts/DataContext";
import { useContext } from "react";
@@ -49,7 +49,8 @@ const AddNewOntologyDialog = ({ open, handleClose, onOntologyAdded, organization
const [files, setFiles] = useState([]);
const [url, setUrl] = useState('');
const [tabValue, setTabValue] = useState(0);
- const { user, refreshOntologies } = useContext(GlobalDataContext);
+ const [setAsActive, setSetAsActive] = useState(false);
+ const { user, refreshOntologies, setOntologyData } = useContext(GlobalDataContext);
// group used both for the POST endpoint and the immutable URI prefix shown to the user
const groupForUri = organizationName || user?.groupname || "base";
@@ -64,6 +65,7 @@ const AddNewOntologyDialog = ({ open, handleClose, onOntologyAdded, organization
setFiles([]);
setUrl('');
setTabValue(0);
+ setSetAsActive(false);
setOpenStatusDialog(false);
setNewOntologyResponse({
title: "",
@@ -118,6 +120,28 @@ const AddNewOntologyDialog = ({ open, handleClose, onOntologyAdded, organization
// If ontology was created successfully, trigger refresh
if (result.created) {
refreshOntologies();
+
+ // Honor the "Set as active ontology" checkbox: push the freshly
+ // created ontology into global context so the active-ontology
+ // selector reflects it. The object shape mirrors the transformed
+ // ontologies produced in OntologySearch (label/badge/id/description/url).
+ if (setAsActive) {
+ const ontologyUri = `${API_CONFIG.INTERLEX_URL}/${groupname}/ontologies/uris/${ontologyName}/spec`;
+ const activeOntology = {
+ label: title,
+ badge: groupname,
+ selected: true,
+ id: result.newOntologyID || ontologyUri,
+ description: ontologyUri,
+ url: result.location,
+ };
+ setOntologyData(activeOntology);
+ // Enrich with member terms once fetched, same as OntologySearch.onSetActive.
+ getOntologyTerms(ontologyUri)
+ .then((terms) => setOntologyData({ ...activeOntology, terms }))
+ .catch(() => setOntologyData({ ...activeOntology, terms: [] }));
+ }
+
if (onOntologyAdded) {
onOntologyAdded();
}
@@ -382,7 +406,12 @@ const AddNewOntologyDialog = ({ open, handleClose, onOntologyAdded, organization
}}
/>
-
+ setSetAsActive(e.target.checked)}
+ />
{
{versionHash && (
-
+ navigate(`/${group}/${term}/overview`)}
+ closeText="Back to current term"
+ >
Viewing a historical version of this term (identity graph {versionHash}). This snapshot is read-only.