Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
40 changes: 23 additions & 17 deletions manage-gui/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
import js from "@eslint/js";
import globals from "globals";
import pluginReact from "eslint-plugin-react";
import reactHooks from 'eslint-plugin-react-hooks';
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import { defineConfig } from "eslint/config";

export default defineConfig([
{
ignores: ["dist/**","node/**"],
ignores: ["dist/**", "node_modules/**"],
},

js.configs.recommended,
react.configs.flat.recommended,
reactHooks.configs.flat.recommended,

{
files: ["**/*.{js,mjs,cjs,jsx}"],

plugins: {
js ,
'react-hooks': reactHooks
react,
"react-hooks": reactHooks,
},
extends: ["js/recommended"],
languageOptions: { globals: globals.browser },

languageOptions: {
globals: globals.browser,
},

settings: {
react: {
version: "detect",
},
},
},
pluginReact.configs.flat.recommended, // React config first
reactHooks.configs.flat.recommended,
{

rules: {
"react/prop-types": "off",
"react/no-children-prop": "off",
'react-hooks/exhaustive-deps': 'warn',
"react/react-in-jsx-scope" : "off",
"react/react-in-jsx-scope": "off",
"react/no-deprecated": "off",
"react/no-unsafe": "off",

"react-hooks/exhaustive-deps": "warn",
"react-hooks/set-state-in-effect": "warn",
"react-hooks/immutability":"off",
"react/no-deprecated": "off", // ReactDOM.unmountComponentAtNode / andere deprecated APIs
"react/no-unsafe": "off", // componentWillReceiveProps, componentWillMount, etc
"react-hooks/immutability": "off",
},
},

]);
19 changes: 11 additions & 8 deletions manage-gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
"js-cookie": "^3.0.5",
"lodash.merge": "^4.6.2",
"papaparse": "^5.5.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-json-view": "^1.21.3",
"semver": "^7.5.4",
"source-map-explorer": "^2.5.3",
"typeface-roboto": "^1.1.13",
"xml-formatter": "^3.6.7"
"xml-formatter": "^3.6.7",
"yaml": "^2.3.1"
},
"devDependencies": {
"@vitejs/plugin-react": "^5.1.2",
Expand All @@ -28,18 +30,18 @@
"eslint-plugin-react-hooks": "^7.0.1",
"events": "^3.3.0",
"globals": "^17.2.0",
"i18n-js": "^3.8.0",
"jsondiffpatch": "^0.4.1",
"i18n-js": "^4.5.3",
"jsondiffpatch": "^0.7.3",
"lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"lodash.escape": "^4.0.1",
"prop-types": "^15.8.1",
"react-codemirror": "^1.0.0",
"react-copy-to-clipboard": "^5.0.4",
"react-highlight": "^0.14.0",
"react-highlight": "^0.15.0",
"react-json-pretty": "^2.2.0",
"react-modal": "^3.14.4",
"react-router-dom": "^6.30.3",
"react-router-dom": "^7.13.1",
"react-select": "^5.2.2",
"react-tooltip": "^4.2.21",
"sass": "^1.97.3",
Expand All @@ -52,7 +54,8 @@
"resolutions": {
"semver": "^7.5.4",
"yaml": "^2.3.1",
"dom-serializer": "^2.0.0"
"dom-serializer": "^2.0.0",
"minimatch": "10.2.4"
},
"scripts": {
"dev": "vite",
Expand Down
17 changes: 17 additions & 0 deletions manage-gui/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ export function uniqueEntityId(entityid, type) {
return postPutJson(`uniqueEntityId/${type}`, {entityid}, "post");
}

export function uniqueInstitutionIdentifier(identifier) {
return postPutJson("uniqueInstitutionIdentifier", {identifier}, "post");
}


export function uniquePolicyName(name) {
return postPutJson(`uniquePolicyName/policy`, {name}, "post");
}
Expand Down Expand Up @@ -277,6 +282,18 @@ export function pushPreviewPdP() {
return fetchJson("playground/pushPreviewPdP");
}

export function pushPreviewSFO() {
return fetchJson("playground/pushPreviewSFO");
}

export function pushPreviewInstitution() {
return fetchJson("playground/pushPreviewInstitution");
}

export function pushPreviewStepup() {
return fetchJson("playground/pushPreviewStepup");
}

export function validate() {
return fetchJson("playground/validate");
}
Expand Down
29 changes: 17 additions & 12 deletions manage-gui/src/components/Autocomplete.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../locale/I18n";
import PropTypes from "prop-types";
import scrollIntoView from "scroll-into-view";
import {isEmpty} from "../utils/Utils";
Expand Down Expand Up @@ -59,26 +59,30 @@ export default class Autocomplete extends React.PureComponent {
<section className="metadata-autocomplete">
{!showSuggestions && this.renderAlternatives(alternatives, selected, itemSelected, query, moreAlternativesToShow)}
{showSuggestions &&
<div>
{moreToShow && <div className="results-info">
<p>{I18n.t("metadata_autocomplete.results_limited")}</p>
<div>
{moreToShow && <div className="results-info">
<p>{I18n.t("metadata_autocomplete.results_limited")}</p>
</div>}
{this.getTable(suggestions, selected, itemSelected, query, type)}
</div>}
{this.getTable(suggestions, selected, itemSelected, query, type)}
</div>}
</section>
);
}

getTable(suggestions, selected, itemSelected, query, type) {
const isOrganisation = type === "organisation";
const isPolicy = type === "policy";
const isInstitution = type === "institution";
const isSfo = type === "sfo";
return <table className="result">
<thead>
<tr>
{!isOrganisation && <th className="count"></th>}
<th className="name">{I18n.t("metadata_autocomplete.name")}</th>
{!isPolicy && !isOrganisation && <th className="organization">{I18n.t("metadata_autocomplete.organization")}</th>}
{!isPolicy && !isOrganisation && !isInstitution &&
<th className="organization">{I18n.t("metadata_autocomplete.organization")}</th>}
{isPolicy && <th className="organization">{I18n.t("metadata_autocomplete.policy")}</th>}
{isInstitution && <th className="institution">{I18n.t("metadata_autocomplete.institution")}</th>}
{!isOrganisation && <th className="type">{I18n.t("metadata_autocomplete.type")}</th>}
{!isOrganisation && <th className="state">{I18n.t("metadata_autocomplete.state")}</th>}
{!isOrganisation && <th className="entity_id">{I18n.t("metadata_autocomplete.entity_id")}</th>}
Expand All @@ -93,7 +97,7 @@ export default class Autocomplete extends React.PureComponent {
case "organisation":
return this.renderOrganisation(item, index, selected, itemSelected);
default:
return this.renderMetadata(item, index, selected, itemSelected, isPolicy, query);
return this.renderMetadata(item, index, selected, itemSelected, isPolicy, isInstitution, isSfo, query);
}
}
)}
Expand Down Expand Up @@ -124,7 +128,7 @@ export default class Autocomplete extends React.PureComponent {
);
};

renderMetadata(item, index, selected, itemSelected, isPolicy, query) {
renderMetadata(item, index, selected, itemSelected, isPolicy, isInstitution,isSfo, query) {
return (
<tr key={index}
className={selected === index ? "active" : ""}
Expand All @@ -136,11 +140,12 @@ export default class Autocomplete extends React.PureComponent {
}}>
<td className="count">{index + 1}</td>
<td>
{!isPolicy && this.item(getNameForLanguage(item.data.metaDataFields), query)}
{isPolicy && this.item(item.data.name, query)}
{!isPolicy && !isInstitution && !isSfo && this.item(getNameForLanguage(item.data.metaDataFields), query)}
{(isPolicy || isInstitution || isSfo) && this.item(item.data.name, query)}
</td>
<td>
{!isPolicy && this.item(getOrganisationForLanguage(item.data.metaDataFields), query)}
{!isPolicy && isInstitution && this.item(getOrganisationForLanguage(item.data.metaDataFields), query)}
{isInstitution && this.item(item.data.identifier, query)}
{isPolicy && I18n.t(`topBannerDetails.${item.data.type}`)}
</td>
Comment on lines 142 to 150
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The organization/institution column rendering logic is inconsistent with the table headers: for non-policy/non-organisation types the organization cell is now always empty (because it only renders when isInstitution is true), and for institutions the same cell tries to render both organization and identifier. Adjust the conditions so regular metadata types render the organisation name, institutions render the identifier in their dedicated column, and SFOs don’t render an empty organization column.

Copilot uses AI. Check for mistakes.
<td>{item.type}</td>
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/ClipBoardCopy.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../locale/I18n";
import PropTypes from "prop-types";
import ReactTooltip from "react-tooltip";
import CopyToClipboard from "react-copy-to-clipboard";
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/ConfirmationDialog.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 Modal from "react-modal";
import I18n from "i18n-js";
import I18n from "../locale/I18n";
import {stop} from "../utils/Utils";

import "./ConfirmationDialog.scss";
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/ErrorDialog.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 Modal from "react-modal";
import I18n from "i18n-js";
import I18n from "../locale/I18n";
import {stop} from "../utils/Utils";

import "./ConfirmationDialog.scss";
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/FormatInput.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import I18n from "i18n-js";
import I18n from "../locale/I18n";

import {validation} from "./../api";
import {isEmpty} from "../utils/Utils";
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../locale/I18n";
import PropTypes from "prop-types";
import {unmountComponentAtNode} from "react-dom";
import {Link} from "react-router-dom";
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/Navigation.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../locale/I18n";
import PropTypes from "prop-types";

import Spinner from "spin.js";
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/PolicyPlaygound.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {useEffect, useState} from "react";

import "./PolicyPlayground.scss";
import I18n from "i18n-js";
import I18n from "../locale/I18n";
import CodeMirror from '@uiw/react-codemirror';
import {json} from '@codemirror/lang-json';
import {
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/form/Password.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";
import "./Password.scss";
import {secret} from "../../api";
import CopyToClipboard from "react-copy-to-clipboard";
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/form/ScopeSelection.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import {Select} from "./../../components";
import {fetchEnumValues, scopeInUse} from "../../api";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";
import "./ScopeSelection.scss";
import {getNameForLanguage} from "../../utils/Language";

Expand Down
1 change: 0 additions & 1 deletion manage-gui/src/components/form/SelectMulti.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export default class SelectMulti extends React.PureComponent {
if (fetchValue) {
fetchEnumValues(fetchValue).then(res => this.setState({fetchValues: res}));
}

}

valuesToOptions(values) {
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/form/StringWithFormat.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";
import {Password} from "../form";

export default class StringWithFormat extends React.PureComponent {
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/form/Strings.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 Creatable from 'react-select/creatable';
import I18n from "i18n-js";
import I18n from "../../locale/I18n";

import reactSelectStyles from "./../reactSelectStyles.js";

Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/metadata/ARP.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";
import PropTypes from "prop-types";
import ReactTooltip from "react-tooltip";

Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/metadata/AutoRefresh.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from "react";
import "./AutoRefresh.scss";
import PropTypes from "prop-types";
import CheckBox from "../CheckBox";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";
import {copyToClip, isEmpty} from "../../utils/Utils";

export default class AutoRefresh extends React.PureComponent {
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/metadata/ConnectedIdps.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";
import PropTypes from "prop-types";
import {Link} from "react-router-dom";

Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/metadata/Connection.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";

import EntityId from "./EntityId";
import SelectState from "./SelectState";
Expand Down
2 changes: 1 addition & 1 deletion manage-gui/src/components/metadata/ConsentDisabling.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";
import PropTypes from "prop-types";
import {Link} from "react-router-dom";
import SelectEntities from "./../SelectEntities";
Expand Down
13 changes: 8 additions & 5 deletions manage-gui/src/components/metadata/Diff.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";
import DOMPurify from "dompurify";
import {DiffPatcher, formatters} from "jsondiffpatch";
import {create} from "jsondiffpatch";

import * as htmlFormatter from "jsondiffpatch/formatters/html";

import cloneDeep from "lodash.clonedeep";
import {sortDict} from "../../utils/Utils";
import "./Diff.scss";

const ignoreInDiff = ["id", "eid", "revisionid", "user", "created", "ip", "revisionnote"];

const differ = new DiffPatcher({
const differ = create({
// https://github.com/benjamine/jsondiffpatch/blob/HEAD/docs/arrays.md
objectHash: (obj, index) => obj.name || obj.level || obj.type || obj.source || obj.value || "$$index:" + index
});
Expand All @@ -23,9 +26,9 @@ export const Diff = ({revision, previousRevision}) => {
sortDict(prev);

const diffs = differ.diff(prev, rev);
const html = DOMPurify.sanitize(formatters.html.format(diffs));
const html = DOMPurify.sanitize(htmlFormatter.format(diffs));

return diffs
? <div dangerouslySetInnerHTML={{__html: html}} />
? <div dangerouslySetInnerHTML={{__html: html}}/>
: <p className="diff-identical">{I18n.t("revisions.identical")}</p>;
};
2 changes: 1 addition & 1 deletion manage-gui/src/components/metadata/EntityId.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import I18n from "i18n-js";
import I18n from "../../locale/I18n";

import {uniqueEntityId, validation} from "../../api";
import InlineEditable from "./InlineEditable";
Expand Down
Loading
Loading