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
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { patchTerm } from "../../../api/endpoints";

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

const HeaderRightSideContent = ({ handleClose, activeStep, handleNext, handleBack, setActiveStep, isAllFieldsFilled, selectedOntology, isUpdating }) => {
const HeaderRightSideContent = ({ handleClose, activeStep, handleNext, handleBack, setActiveStep, isAllFieldsFilled, selectedOntology, isUpdating, ontologyTerms }) => {
return (
<Box display='flex' alignItems='center' gap='.75rem'>
<MobileStepper
Expand Down Expand Up @@ -47,7 +47,7 @@ const HeaderRightSideContent = ({ handleClose, activeStep, handleNext, handleBac
variant='contained'
color='primary'
onClick={handleNext}
disabled={(activeStep === 0 && !isAllFieldsFilled && !selectedOntology) || isUpdating}
disabled={(activeStep === 0 && (!isAllFieldsFilled && !selectedOntology)) || (activeStep === 0 && selectedOntology && (!ontologyTerms || ontologyTerms.length === 0)) || isUpdating}
>
{isUpdating ? 'Saving Changes...' : 'Continue'}
</Button>
Expand All @@ -65,6 +65,8 @@ HeaderRightSideContent.propTypes = {
setActiveStep: PropTypes.func.isRequired,
isAllFieldsFilled: PropTypes.bool.isRequired,
selectedOntology: PropTypes.object,
isUpdating: PropTypes.bool.isRequired,
ontologyTerms: PropTypes.array,
};

const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) => {
Expand Down Expand Up @@ -218,6 +220,7 @@ const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) =
isAllFieldsFilled={isAllFieldsFilled(searchConditions)}
selectedOntology={selectedOntology}
isUpdating={isUpdating}
ontologyTerms={ontologyTerms}
/>
}
sx={{
Expand Down
23 changes: 15 additions & 8 deletions src/components/Dashboard/EditBulkTerms/SearchTerms.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const styles = {
},
}

const SearchTerms = ({ searchConditions, setSearchConditions, initialSearchConditions, setOntologyTerms, ontologyAttributes, setOntologyAttributes, selectedOntology, setSelectedOntology, setOriginalTerms }) => {
const SearchTerms = ({ searchConditions, setSearchConditions, initialSearchConditions, ontologyTerms, setOntologyTerms, ontologyAttributes, setOntologyAttributes, selectedOntology, setSelectedOntology, setOriginalTerms }) => {
const [ontologyEditOption, setOntologyEditOption] = useState(Confirmation.Yes);
const [attributesLoading, setAttributesLoading] = useState(false);
const { user } = useContext(GlobalDataContext);
Expand Down Expand Up @@ -292,13 +292,20 @@ const SearchTerms = ({ searchConditions, setSearchConditions, initialSearchCondi
</Stack>

{ontologyEditOption === Confirmation.Yes && (
<OntologySearch
placeholder="Enter an Ontology URI"
fullWidth
extra={ontologySearchExtraStyles}
userGroupname={user?.groupname}
onOntologySelect={handleOntologySelect}
/>
<>
<OntologySearch
placeholder="Enter an Ontology URI"
fullWidth
extra={ontologySearchExtraStyles}
userGroupname={user?.groupname}
onOntologySelect={handleOntologySelect}
/>
{selectedOntology && ontologyTerms.length === 0 && !attributesLoading && (
<Typography variant="body2" sx={{ mt: 2, color: 'warning.main', fontWeight: 500 }}>
⚠️ The selected ontology &quot;{selectedOntology.label}&quot; contains no terms to edit.
</Typography>
)}
</>
)}
</Box>

Expand Down
49 changes: 38 additions & 11 deletions src/components/Dashboard/EditBulkTerms/TermsTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ const TermsTable = ({ setOpenEditAttributes, setAttributes, attributes, ontology
};

const sortedRows = React.useMemo(
() => stableSort(terms || [], getComparator(order, orderBy)),
() => {
if (!terms || terms.length === 0) return [];
return stableSort(terms, getComparator(order, orderBy));
},
[order, orderBy, terms]
);

Expand Down Expand Up @@ -156,7 +159,7 @@ const TermsTable = ({ setOpenEditAttributes, setAttributes, attributes, ontology
</Box>
}
return (
terms.length > 0 ? (
terms && terms.length > 0 ? (
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Typography color={gray800} fontSize='1.125rem' fontWeight={600} mb='2.75rem'>
Edit your terms or select an header to bulk edit that property
Expand Down Expand Up @@ -243,11 +246,11 @@ const TermsTable = ({ setOpenEditAttributes, setAttributes, attributes, ontology
attributes={attributes}
/>
<TableBody>
{sortedRows.map((row, index) => (
{sortedRows && sortedRows.length > 0 ? sortedRows.map((row, index) => (
<TableRow key={index}>
{filteredColumns.map((column) => {
const isEditing = editingCell?.rowIndex === index && editingCell?.columnId === column.id;
const cellValue = row[column.id];
const cellValue = row && row[column.id];

return (
<TableCell
Expand All @@ -273,27 +276,51 @@ const TermsTable = ({ setOpenEditAttributes, setAttributes, attributes, ontology
fullWidth
sx={{ minWidth: 0 }}
/>
) : Array.isArray(cellValue) ? (
) : Array.isArray(cellValue) && cellValue.length > 0 ? (
<Stack gap='.25rem' direction="row" alignItems="center" maxWidth='20rem' flexWrap='wrap'>
{cellValue.map((chip, chipIndex) => (
<Chip key={`${chip}-${chipIndex}`} label={chip} className='rounded IDchip-outlined' icon={<OpenInNewOutlinedIcon />} onClick={() => handleChipClick(chip)} />
))}
{cellValue.map((chip, chipIndex) => {
const chipLabel = typeof chip === 'object' && chip !== null
? chip['@value'] || chip.value || chip['@id'] || JSON.stringify(chip)
: chip || '';
const chipValue = typeof chip === 'object' && chip !== null
? chip['@id'] || chip.value || chip['@value']
: chip;
return (
<Chip
key={`${chipLabel}-${chipIndex}`}
label={chipLabel}
className='rounded IDchip-outlined'
icon={<OpenInNewOutlinedIcon />}
onClick={() => handleChipClick(chipValue)}
/>
);
})}
</Stack>
) : column.id === '@id' ? (
// Format the Interlex ID to show just the ID part - read-only display
<span style={{ color: gray700, fontWeight: 500 }}>
{cellValue?.split('/').pop() || cellValue}
{(() => {
if (!cellValue) return '';
if (typeof cellValue === 'object') {
const idValue = cellValue['@id'] || cellValue.id || cellValue['@value'] || JSON.stringify(cellValue);
return typeof idValue === 'string' ? idValue.split('/').pop() || idValue : idValue;
}
return typeof cellValue === 'string' ? cellValue.split('/').pop() || cellValue : cellValue;
})()}
</span>
) : (
<span>
{cellValue}
{typeof cellValue === 'object' && cellValue !== null
? cellValue['@value'] || cellValue.value || JSON.stringify(cellValue)
: cellValue || ''
}
</span>
)}
</TableCell>
);
})}
</TableRow>
))}
)) : null}
</TableBody>
</Table>
</TableContainer>
Expand Down
34 changes: 22 additions & 12 deletions src/components/SingleTermView/OntologySearch.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,17 @@ const OntologySearch = ({ placeholder, fullWidth = false, disabled, extra, userG
setSearchTerm('');
setSelectedValue(value);

// If value is null (cleared), also clear from context
if (!value) {
setOntologyData(null);
console.log('Ontology cleared from context');
}

// Call the callback if provided
if (onOntologySelect) {
onOntologySelect(value);
}
}, [onOntologySelect]);
}, [onOntologySelect, setOntologyData]);

const popperProps = useMemo(() => ({
sx: {
Expand Down Expand Up @@ -315,15 +321,18 @@ const OntologySearch = ({ placeholder, fullWidth = false, disabled, extra, userG
<FolderSharedOutlinedIcon />
</InputAdornment>
),
endAdornment: selectedValue?.selected && (
<InputAdornment position='end'>
<Chip
label={selectedValue.badge}
variant='outlined'
size='small'
sx={{ marginLeft: '0.5rem' }}
/>
</InputAdornment>
endAdornment: (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
{selectedValue?.selected && (
<Chip
label={selectedValue.badge}
variant='outlined'
size='small'
sx={{ marginRight: '0.5rem' }}
/>
)}
{params.InputProps.endAdornment}
</Box>
),
}}
sx={{
Expand All @@ -345,8 +354,9 @@ const OntologySearch = ({ placeholder, fullWidth = false, disabled, extra, userG
<div ref={autocompleteRef} style={{ width: fullWidth ? '100%' : 'fit-content' }}>
<Autocomplete
disableCloseOnSelect
disableClearable
disableClearable={false}
options={ontologies}
value={selectedValue}
open={openList}
disabled={disabled}
onOpen={handleOpenList}
Expand All @@ -360,7 +370,7 @@ const OntologySearch = ({ placeholder, fullWidth = false, disabled, extra, userG
popper: popperProps,
paper: { sx: paperStyles },
}}
inputValue={searchTerm ? searchTerm : selectedValue?.selected ? selectedValue?.label : ''}
inputValue={searchTerm ? searchTerm : selectedValue?.selected ? selectedValue?.label : selectedValue?.label || ''}
renderOption={renderOption}
renderInput={renderInput}
PaperComponent={PaperComponent}
Expand Down
37 changes: 37 additions & 0 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,43 @@ export default defineConfig({
});
},
},
'^/([^/]+)/priv/password_change$': {
target: "https://uri.olympiangods.org",
secure: false,
changeOrigin: true,
rewrite: (path) => path, // keep full path
configure: (proxy) => {
proxy.on('error', (err, req) => {
console.log('Password change proxy error:', err);
console.log('Request URL:', req.url);
});
proxy.on('proxyReq', (proxyReq, req) => {
console.log('Proxying password change request:', req.method, req.url);
// pass through auth/cookies if present
if (req.headers.authorization) proxyReq.setHeader('Authorization', req.headers.authorization);
if (req.headers.cookie) proxyReq.setHeader('Cookie', req.headers.cookie);
// Set appropriate content type for password change (likely form data)
if (req.method === 'POST') {
proxyReq.setHeader('Content-Type', 'application/x-www-form-urlencoded');
}
});
proxy.on('proxyRes', (proxyRes, req, res) => {
console.log('Password change response:', proxyRes.statusCode, req.url);
// handle redirects
const location = proxyRes.headers['location'];
if (proxyRes.statusCode === 303 && location) {
delete proxyRes.headers['location'];
res.setHeader('X-Redirect-Location', location);
}

// CORS headers
const origin = req.headers.origin;
if (origin) res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Expose-Headers', 'X-Redirect-Location');
});
},
},
'^/([^/]+)/priv/entity(.*)': {
target: "https://uri.olympiangods.org",
secure: false,
Expand Down