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
11 changes: 9 additions & 2 deletions src/components/Auth/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ const Login = () => {
}
};

const handleFormSubmit = (e) => {
e.preventDefault();
if (formData.username.trim() && formData.password.trim()) {
loginUser();
}
};

return (
<Box className="authArea">
{isLoading ? <Box sx={{ height: 1, width: 1, display: "flex", alignItems: "center", justifyContent: "center" }}>
Expand All @@ -243,7 +250,7 @@ const Login = () => {
<Typography variant="h4">Log in to your account</Typography>
<Typography variant="body1">Welcome! Please enter your details.</Typography>
{errors.auth && <Alert severity="error" sx={{ mt: 2 }}>{errors.auth}</Alert>}
<form className="authForm">
<form className="authForm" onSubmit={handleFormSubmit}>
<Grid container spacing={2.5}>
<Grid item xs={12}>
<CustomFormField
Expand Down Expand Up @@ -293,7 +300,7 @@ const Login = () => {
</Grid>
<Grid item xs={12}>
<FormControl>
<Button variant="contained" color="primary" onClick={loginUser}>
<Button variant="contained" color="primary" type="submit">
Sign in
</Button>
</FormControl>
Expand Down
56 changes: 28 additions & 28 deletions src/components/CurieEditor/CuriesTabPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,33 @@ const fieldStyle = {
}
};

const tableCellStyle = {
minWidth: '37.5rem',
const tableCellBaseStyle = {
color: gray600,
fontWeight: 400
};

const prefixCellStyle = {
...tableCellBaseStyle,
width: '25%'
};

const namespaceCellStyle = {
...tableCellBaseStyle,
width: '75%'
};

const CuriesTabPanel = (props) => {
const { curieValue, error, loading, rows, editMode, onCurieAmountChange, onAddRow, onDeleteRow, onChangeRow } = props;
const [rowIndex, setRowIndex] = React.useState(-1);
const [columnIndex, setColumnIndex] = React.useState(-1);
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('prefix');
const [sortTriggered, setSortTriggered] = React.useState(false);

const sortedRows = React.useMemo(() => {
// Ensure rows is always an array
// Ensure rows is always an array and apply natural sorting by default
const safeRows = Array.isArray(rows) ? rows : [];

if (sortTriggered) {
return stableSort(safeRows, getComparator(order, orderBy));
}
return safeRows;
}, [rows, order, orderBy, sortTriggered]);
return stableSort(safeRows, getComparator(order, orderBy));
}, [rows, order, orderBy]);

React.useEffect(() => {
onCurieAmountChange?.(rows.length)
Expand All @@ -65,12 +69,6 @@ const CuriesTabPanel = (props) => {
setColumnIndex(-1);
}

const handleSort = (dir) => {
setSortTriggered(true);
setOrder(dir);
handleExit();
}

if (error) {
return <div>error</div>;
}
Expand All @@ -84,17 +82,28 @@ const CuriesTabPanel = (props) => {
rows={rows}
order={order}
orderBy={orderBy}
setOrder={handleSort}
setOrder={setOrder}
setOrderBy={setOrderBy}
headCells={editMode ? headCellsEditMode : headCells}
>
{editMode && (
<TableRow>
<TableCell align="left" sx={{ borderBottom: 'none !important', ...prefixCellStyle }} onClick={() => onAddRow(curieValue)}>
<IconButton sx={{ padding: '0.625rem', border: `1px solid ${gray300}` }}>
<AddOutlinedIcon fontSize="small" sx={{ fill: gray700 }} />
</IconButton>
</TableCell>
<TableCell sx={{ borderBottom: 'none !important', ...namespaceCellStyle }}></TableCell>
<TableCell sx={{ borderBottom: 'none !important' }}></TableCell>
</TableRow>
)}
{Array.isArray(sortedRows) && sortedRows.map((row, index) => {
return (
<TableRow tabIndex={-1} key={`${row.prefix}_${row.namespace}`}>
<TableCell
align="left"
onClick={() => { setRowIndex(index); setColumnIndex(0); }}
sx={{ border: rowIndex === index && columnIndex === 0 && editMode ? `2px solid ${brand500} !important` : 'inherit', ...tableCellStyle }}
sx={{ border: rowIndex === index && columnIndex === 0 && editMode ? `2px solid ${brand500} !important` : 'inherit', ...prefixCellStyle }}
>
{
rowIndex === index && columnIndex === 0 && editMode ?
Expand All @@ -115,7 +124,7 @@ const CuriesTabPanel = (props) => {
<TableCell
align="left"
onClick={() => { setRowIndex(index); setColumnIndex(1); }}
sx={{ border: rowIndex === index && columnIndex === 1 && editMode ? `2px solid ${brand500} !important` : 'inherit', ...tableCellStyle }}
sx={{ border: rowIndex === index && columnIndex === 1 && editMode ? `2px solid ${brand500} !important` : 'inherit', ...namespaceCellStyle }}
>
{
rowIndex === index && columnIndex === 1 && editMode ?
Expand Down Expand Up @@ -143,15 +152,6 @@ const CuriesTabPanel = (props) => {
</TableRow>
);
})}
{editMode && (
<TableRow>
<TableCell align="left" sx={{ borderBottom: 'none !important' }} onClick={() => onAddRow(curieValue)}>
<IconButton sx={{ padding: '0.625rem', border: `1px solid ${gray300}` }}>
<AddOutlinedIcon fontSize="small" sx={{ fill: gray700 }} />
</IconButton>
</TableCell>
</TableRow>
)}
</CustomTable>
)}
</ClickAwayListener>
Expand Down
15 changes: 11 additions & 4 deletions src/components/CurieEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,15 @@ const CurieEditor = () => {
});
data = transformCuriesResponse(response);
} else if (type === 'latest') {
// "Latest" tab - stay empty for now
data = [];
// "Latest" tab - get curies from "base" groupname (same as curated)
const response = await getOrganizationsCuries('base').catch(error => {
if (error?.response?.status === 501) {
console.warn('Curies endpoint not implemented yet (501) for base, using empty array');
return [{}]; // Return array with empty object to match expected structure
}
throw error;
});
data = transformCuriesResponse(response);
}

setCuries(prev => ({ ...prev, [type]: data }));
Expand All @@ -91,7 +98,7 @@ const CurieEditor = () => {
}, [user]);

const handleAddNewCurieRow = (curieValue) => {
setCuries(prev => ({ ...prev, [curieValue]: [...prev[curieValue], newRowObj] }));
setCuries(prev => ({ ...prev, [curieValue]: [newRowObj, ...prev[curieValue]] }));
};

const handleDeleteCurieRow = (curieValue, rowPrefix, rowNamespace) => {
Expand Down Expand Up @@ -174,7 +181,7 @@ const CurieEditor = () => {
curieValue={tab}
error={error}
loading={loading}
editMode
editMode={tab === 'base'} // Only allow editing for 'base' (My curies) tab
rows={curies[tab]}
onCurieAmountChange={handleCurieAmountChange}
onAddRow={handleAddNewCurieRow}
Expand Down
48 changes: 43 additions & 5 deletions src/components/Dashboard/EditBulkTerms/EditBulkTermsDialog.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useContext, useEffect } from "react";
import PropTypes from "prop-types";
import EditTerms from "./EditTerms";
import SearchTerms from "./SearchTerms";
Expand All @@ -12,6 +12,7 @@ import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import SearchTermsData from "../../../static/SearchTermsData.json";
import { patchTerm } from "../../../api/endpoints";
import { GlobalDataContext } from "../../../contexts/DataContext";

const initialSearchConditions = { attribute: '', value: '', condition: 'where', relation: SearchTermsData.objectOptions[0].value }

Expand Down Expand Up @@ -70,6 +71,7 @@ HeaderRightSideContent.propTypes = {
};

const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) => {
const { activeOntology, user } = useContext(GlobalDataContext);
const [searchConditions, setSearchConditions] = useState([initialSearchConditions]);
const [ontologyTerms, setOntologyTerms] = useState([]);
const [ontologyAttributes, setOntologyAttributes] = useState([]);
Expand All @@ -78,6 +80,36 @@ const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) =
const [originalTerms, setOriginalTerms] = useState([]);
const [batchUpdateResults, setBatchUpdateResults] = useState(null);
const [isUpdating, setIsUpdating] = useState(false);

// Prefill selectedOntology with activeOntology when dialog opens
useEffect(() => {
if (open && activeOntology && !selectedOntology) {
setSelectedOntology(activeOntology);
}
}, [open, activeOntology, selectedOntology]);

// Helper function to replace "base" with user groupname in term data
const replaceBaseWithUserGroup = (obj, userGroupname) => {
if (!obj || !userGroupname) return obj;

const replaceInValue = (value) => {
if (typeof value === 'string') {
return value.replace(/\bbase\b/g, userGroupname);
} else if (Array.isArray(value)) {
return value.map(replaceInValue);
} else if (value && typeof value === 'object') {
return replaceBaseWithUserGroup(value, userGroupname);
}
return value;
};

const result = {};
for (const [key, value] of Object.entries(obj)) {
result[key] = replaceInValue(value);
}
return result;
};

const performBatchUpdate = async (termsToUpdate = null) => {
setIsUpdating(true);

Expand Down Expand Up @@ -107,19 +139,25 @@ const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) =
termId = termId.split('/').pop();
}

const group = 'base'; // Default group, can be made configurable
// Use user's groupname instead of 'base'
const group = user?.groupname || 'base';

// Create the JSON-LD payload with the current term data
const jsonLdPayload = {
// Create the JSON-LD payload with the current term data, replacing base with user groupname
let jsonLdPayload = {
"@context": term["@context"] || {
"@vocab": "http://uri.interlex.org/base/",
"@vocab": `http://uri.interlex.org/${group}/`,
"owl": "http://www.w3.org/2002/07/owl#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#"
},
"@id": term['@id'] || termId,
...term
};

// Replace any "base" references with user's groupname in the payload
if (user?.groupname && user.groupname !== 'base') {
jsonLdPayload = replaceBaseWithUserGroup(jsonLdPayload, user.groupname);
}

// Send PATCH request
const response = await patchTerm(group, termId, jsonLdPayload);

Expand Down
1 change: 1 addition & 0 deletions src/components/Dashboard/EditBulkTerms/SearchTerms.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ const SearchTerms = ({ searchConditions, setSearchConditions, initialSearchCondi
extra={ontologySearchExtraStyles}
userGroupname={user?.groupname}
onOntologySelect={handleOntologySelect}
disableGlobalUpdate={true}
/>
{selectedOntology && ontologyTerms.length === 0 && !attributesLoading && (
<Typography variant="body2" sx={{ mt: 2, color: 'warning.main', fontWeight: 500 }}>
Expand Down
21 changes: 21 additions & 0 deletions src/components/Header/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ import { useCookies } from 'react-cookie';
import CustomButtonGroup from '../common/CustomButtonGroup';
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
import AddIcon from '@mui/icons-material/Add';
import AccountTreeIcon from '@mui/icons-material/AccountTree';
import AddNewTermDialog from '../TermEditor/newTerm/AddNewTermDialog';
import AddNewOntologyDialog from '../SingleOrganization/AddNewOntologyDialog';

import { vars } from "../../theme/variables";
const { gray200, white, gray100, gray600 } = vars;
Expand Down Expand Up @@ -151,6 +153,7 @@ const Header = () => {
const [isLoggedIn, setIsLoggedIn] = React.useState(false);
const { user, setUserData } = useContext(GlobalDataContext);
const [openNewTermDialog, setOpenNewTermDialog] = React.useState(false);
const [openNewOntologyDialog, setOpenNewOntologyDialog] = React.useState(false);
// eslint-disable-next-line no-unused-vars
const [existingCookies, setCookie, removeCookie] = useCookies(['session']);

Expand Down Expand Up @@ -179,6 +182,14 @@ const Header = () => {
setOpenNewTermDialog(true);
}

const handleNewOntologyDialogClose = () => {
setOpenNewOntologyDialog(false);
}

const handleNewOntologyDialogOpen = () => {
setOpenNewOntologyDialog(true);
}

// eslint-disable-next-line no-unused-vars
const handleSetUserData = (user, organization) => {
setUserData(user, organization);
Expand Down Expand Up @@ -293,6 +304,11 @@ const Header = () => {
icon: <AddIcon />,
action: handleNewTermDialogOpen
},
{
label: 'Add a new ontology',
icon: <AccountTreeIcon />,
action: handleNewOntologyDialogOpen
},
{
label: 'Bulk add terms',
icon: <PlaylistAddIcon />,
Expand Down Expand Up @@ -471,6 +487,11 @@ const Header = () => {
)}
</Box>
<AddNewTermDialog open={openNewTermDialog} handleClose={handleNewTermDialogClose} />
<AddNewOntologyDialog
open={openNewOntologyDialog}
handleClose={handleNewOntologyDialogClose}
organizationName={user?.groupname}
/>
<EditBulkTermsDialog handleClose={handleCloseEditBulkTerms} open={openEditBulkTerms} activeStep={activeStep} setActiveStep={setActiveStep} />
</>
)
Expand Down
34 changes: 5 additions & 29 deletions src/components/SearchResults/SearchResultsBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,15 @@ import PropTypes from 'prop-types';
import { TableChartIcon, ListIcon } from '../../Icons';
import OntologySearch from '../SingleTermView/OntologySearch';
import CustomSingleSelect from '../common/CustomSingleSelect';
import { Box, Typography, Grid, ButtonGroup, Button, Stack, Divider } from '@mui/material';
import CustomViewButton from '../common/CustomViewButton';
import { Box, Typography, Grid, ButtonGroup, Stack, Divider } from '@mui/material';
import CustomPagination from '../common/CustomPagination';
import { vars } from '../../theme/variables';
import { GlobalDataContext } from '../../contexts/DataContext';

const { gray50, gray200, gray300, gray600 } = vars;

const CustomViewButton = ({ view, listView, onClick, icon }) => (
<Button
sx={{
background: listView === view ? gray50 : 'transparent',
padding: '0.5rem 0.75rem',
border: `1px solid ${gray300}`,
'&.Mui-disabled': {
border: `1px solid ${gray300}`
},
'& svg path': {
fill: listView !== view ? gray300 : 'currentColor'
}
}}
disabled={view === 'table' && true}
onClick={onClick}
>
{icon}
</Button>
);
const { gray200, gray600 } = vars;



const getPaginationSettings = (totalItems) => {
const largeDatasetOptions = [20, 50, 100, 200];
Expand Down Expand Up @@ -170,13 +153,6 @@ const SearchResultsBox = ({
);
};

CustomViewButton.propTypes = {
view: PropTypes.string,
listView: PropTypes.string,
onClick: PropTypes.func,
icon: PropTypes.node
};

SearchResultsBox.propTypes = {
allResults: PropTypes.object,
pageResults: PropTypes.object,
Expand Down
Loading