Skip to content

Commit

Permalink
Fixes #233
Browse files Browse the repository at this point in the history
  • Loading branch information
oharsta committed Dec 10, 2024
1 parent 1ac81ad commit 4eca731
Show file tree
Hide file tree
Showing 17 changed files with 195 additions and 31 deletions.
11 changes: 8 additions & 3 deletions client/src/components/ConfirmationDialog.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, {useState} from "react";
import {Modal,} from "@surfnet/sds";
import I18n from "../locale/I18n";

Expand All @@ -15,12 +15,17 @@ export default function ConfirmationDialog({
largeWidth = false,
confirmationHeader = I18n.t("confirmationDialog.title")
}) {
const [busy, setBusy] = useState(false);

if (!isOpen) {
return null;
}
return (
<Modal
confirm={confirm}
confirm={() => {
setBusy(true);
confirm();
}}
cancel={cancel}
alertType={null}
question={question}
Expand All @@ -29,7 +34,7 @@ export default function ConfirmationDialog({
title={confirmationHeader}
cancelButtonLabel={I18n.t("confirmationDialog.cancel")}
confirmationButtonLabel={confirmationTxt}
confirmDisabled={disabledConfirm}
confirmDisabled={disabledConfirm || (busy && cancel)}
subTitle={null}
full={largeWidth}/>
);
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Entities.scss
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@
margin: auto 0;

svg {
color: var(--sds--color--gray--200);
color: var(--sds--color--gray--400);
cursor: pointer;
margin: 0 5px;
width: 20px;
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/UnitHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const UnitHeader = ({
}
{showChevronAction &&
<div tabIndex={1}
onBlur={() => setTimeout(() => setShowDropDown(false), 250)}>
onBlur={() => setTimeout(() => setShowDropDown(false), 125)}>
<MenuButton txt={I18n.t("home.otherOptions")}
isOpen={showDropDown}
toggle={() => setShowDropDown(!showDropDown)}
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/UserMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const UserMenu = ({user, config, actions}) => {
return (
<div className="user-menu"
tabIndex={1}
onBlur={() => setTimeout(() => setDropDownActive(false), 250)}>
onBlur={() => setTimeout(() => setDropDownActive(false), 125)}>
<UserInfo isOpen={dropDownActive}
children={renderMenu(adminLinks)}
organisationName={I18n.t(`access.${highestAuthority(user, false)}`)}
Expand Down
8 changes: 7 additions & 1 deletion client/src/locale/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ const en = {
updateConfirmationRemoveEndDate: "Are you sure you want to remove the end date of role {{roleName}} for {{userName}}?",
updateFlash: "The end date for role {{roleName}} has been updated",
deleteConfirmation: "Are you sure you want to remove this role from this user(s)?",
deleteOneConfirmation: "Are you sure you want to remove this role from this user?",
deleteFlash: "User role(s) have been removed",
createdAt: "Date accepted",
delete: "Remove"
Expand Down Expand Up @@ -279,7 +280,9 @@ const en = {
notAllowed: "You're not allowed to delete or resend this invitation because of missing roles",
deleteFlash: "Invitation(s) have been revoke",
deleteConfirmation: "Are you sure you want to revoke these invitations?",
deleteOneConfirmation: "Are you sure you want to revoke this invitation?",
resendConfirmation: "Are you sure you want to resend these invitations?",
resendConfirmationOne: "Are you sure you want to resend this invitation?",
resendFlash: "Invitation(s) have been resent",
inviterRole: {
title: "Send new invite",
Expand Down Expand Up @@ -419,10 +422,13 @@ const en = {
intendedAuthorityTooltip: "The authority determines the rights the invitee will be granted on accepting the invitation",
inviteesTooltip: "Add email addresses separated by comma, space or semi-colon or on seperate lines. You can also paste a csv file with line-separated email addresses.",
removeInvitation: "Delete all selected invitations",
removeOneInvitation: "Delete this invitation",
resendInvitation: "Resend all selected invitations",
resendOneInvitation: "Resend this invitation",
inviter: "Send invitations to persons who will - once accepted - gain user access to the application",
overrideSettingsAllowed: "When checked, invitations for this role can override the advanced settings (e.g. email equality, eduID only and the role expiry end date)",
removeUserRole: "Remove all selected user roles",
removeOneUserRole: "Remove this user role",
guestRoleIncludedTooltip: "Do you also want to grant the invitees the user role when they accept the invitation?",
expiredUserRole: "This role will expire soon",
},
Expand Down Expand Up @@ -506,7 +512,7 @@ const en = {
noMails: "No notification mails for user-role expirations were send",
performanceSeedInfo: "The performance seed is dummy / generated data to test the performance of the system. It is deleted when you run the tests.",
performanceSeed: "Insert performance seed",
performanceMillis: "Inserted following entities in {{millis}} ms"
performanceLoadTime: "Inserted following entities in ~{{minutes}} minutes"
},
unknownRoles: {
title: "Roles linked to applications unknown in Manage",
Expand Down
8 changes: 7 additions & 1 deletion client/src/locale/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ const nl = {
updateConfirmationRemoveEndDate: "Weet je zeker dat je de einddatum wil verwijderen van rol {{roleName}} voor {{userName}}?",
updateFlash: "De einddatum van rol {{roleName}} is bijgewerkt",
deleteConfirmation: "Weet je zeker dat je de rol van deze gebruiker(s) wil verwijderen?",
deleteOneConfirmation: "Weet je zeker dat je de rol van deze gebruiker wil verwijderen?",
deleteFlash: "Gebruikersrol(len) zijn verwijderd",
createdAt: "Datum geaccepteerd",
delete: "Verwijder"
Expand Down Expand Up @@ -279,7 +280,9 @@ const nl = {
notAllowed: "Je kunt deze uitnodiging niet opnieuw versturen of intrekken vanwege ontbrekende rollen",
deleteFlash: "Uitnodiging(en) ingetrokken",
deleteConfirmation: "Weet je zeker dat je deze uitnodiging(en) wil intrekken?",
deleteOneConfirmation: "Weet je zeker dat je deze uitnodiging wil intrekken?",
resendConfirmation: "Weet je zeker dat je deze uitnodiging(en) opnieuw wil versturen?",
resendConfirmationOne: "Weet je zeker dat je deze uitnodiging opnieuw wil versturen?",
resendFlash: "Uitnodiging(en) opnieuw verstuurd.",
inviterRole: {
title: "Verstuur nieuwe uitnodiging",
Expand Down Expand Up @@ -419,10 +422,13 @@ const nl = {
intendedAuthorityTooltip: "De autoriteit geeft de rechten aan die de genodigde verwerft na het accepteren van de uitnodiging",
inviteesTooltip: "Geef e-mailadressen op, gescheiden door komma, spatie of puntkomma, of op een eigen regel. Je kunt ook een csv-bestand plakken met daarin op elke regel een e-mailadres.",
removeInvitation: "Verwijder alle geselecteerde uitnodigingen",
removeOneInvitation: "Verwijder deze uitnodiging",
resendInvitation: "Verstuur al de geselecteerde uitnodigingen opnieuw",
resendOneInvitation: "Verstuur deze uitnodiging opnieuw",
inviter: "Verstuur uitnodigingen naar gebruikers die - na accepteren - en rol krijgen voor de applicatie",
overrideSettingsAllowed: "Indien ingeschakeld kunnen uitnodigingen voor de deze rol de geavanceerde configuratie aanpassen (waaronder of e-mailadres overeen moet komen, alleen accepteren met eduID en de verloop- en einddatum)",
removeUserRole: "Verwijder alle geselecteerde rollen",
removeOneUserRole: "Verwijder deze rol",
guestRoleIncludedTooltip: "Wil je dat de uitgenodigden ook gebruiker worden als ze de uitnodiging accepteren?",
expiredUserRole: "Deze rol gaat verlopen binnen een maand",
},
Expand Down Expand Up @@ -506,7 +512,7 @@ const nl = {
noMails: "Geen notificatie mails zijn vertstuurd voor bijna verlopen gebruikersrollen",
performanceSeedInfo: "The performance seed is dummy / generated data to test the performance of the system. It is deleted when you run the tests.",
performanceSeed: "Insert performance seed",
performanceMillis: "Inserted following entities in {{millis}} ms"
performanceLoadTime: "Inserted following entities in ~{{minutes}} minutes"
},
unknownRoles: {
title: "Rollen gekoppeld aan applicaties die verwijderd zijn in Manage",
Expand Down
2 changes: 0 additions & 2 deletions client/src/pages/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ export const App = () => {
}
<Route path="*" element={<NotFound/>}/>
</Routes>}
{/* <Route path="invitations" element={<Invitation user={user}/>}/>*/}
{/* <Route path="institution/:institutionId" element={<InstitutionForm user={user}/>}/>*/}
{!authenticated &&
<Routes>
<Route path="/" element={<Navigate replace to="login"/>}/>
Expand Down
70 changes: 70 additions & 0 deletions client/src/tabs/Invitations.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {isEmpty, pseudoGuid} from "../utils/Utils";
import {allowedToDeleteInvitation, AUTHORITIES, INVITATION_STATUS, isUserAllowed} from "../utils/UserRole";
import {UnitHeader} from "../components/UnitHeader";
import Select from "react-select";
import {ReactComponent as TrashIcon} from "@surfnet/sds/icons/functional-icons/bin.svg";
import {ReactComponent as ResendIcon} from "@surfnet/sds/icons/functional-icons/go-to-other-website.svg";

const allValue = "all";
const mineValue = "mine";
Expand Down Expand Up @@ -149,6 +151,24 @@ export const Invitations = ({
}
};

const doResendInvitationsFromActionLink = (invitation, showConfirmation) => {
if (showConfirmation) {
setConfirmation({
cancel: () => setConfirmationOpen(false),
action: () => doResendInvitationsFromActionLink(invitation, false),
question: I18n.t("invitations.resendConfirmationOne"),
confirmationTxt: I18n.t("confirmationDialog.confirm")
});
setConfirmationOpen(true);
} else {
resendInvitation(invitation.id)
.then(() => {
setConfirmationOpen(false);
setFlash(I18n.t("invitations.resendFlash"));
})
}
}

const doDeleteInvitations = showConfirmation => {
if (showConfirmation) {
setConfirmation({
Expand All @@ -170,6 +190,26 @@ export const Invitations = ({
}
};

const doDeleteInvitationsFromActionLink = (invitation, showConfirmation) => {
if (showConfirmation) {
setConfirmation({
cancel: () => setConfirmationOpen(false),
action: () => doDeleteInvitationsFromActionLink(invitation, false),
question: I18n.t("invitations.deleteOneConfirmation"),
confirmationTxt: I18n.t("confirmationDialog.confirm")
});
setConfirmationOpen(true);
} else {
deleteInvitation(invitation.id)
.then(() => {
setConfirmationOpen(false);
setFlash(I18n.t("invitations.deleteFlash"));
const path = encodeURIComponent(window.location.pathname);
navigate(`/refresh-route/${path}`, {replace: true});
})
}
};

if (loading) {
return <Loader/>
}
Expand Down Expand Up @@ -226,6 +266,29 @@ export const Invitations = ({
);
}

const actionIcons = invitation => {
return (
<div className="admin-icons">
{pending && <div onClick={() => doResendInvitationsFromActionLink(invitation, true)}>
<Tooltip standalone={true}
anchorId={"remove-members"}
tip={I18n.t("tooltips.resendOneInvitation")}
children={
<ResendIcon/>
}/>
</div>}
<div onClick={() => doDeleteInvitationsFromActionLink(invitation, true)}>
<Tooltip standalone={true}
anchorId={"remove-members"}
tip={I18n.t("tooltips.removeOneInvitation")}
children={
<TrashIcon/>
}/>
</div>
</div>);
}


const getActions = () => {
const actions = [];
actions.push({
Expand Down Expand Up @@ -285,6 +348,12 @@ export const Invitations = ({
key: pending ? "expiryDate" : "acceptedAt",
header: I18n.t(pending ? "invitations.expiryDate" : "invitations.acceptedAt"),
mapper: invitation => pending ? invitationExpiry(invitation) : shortDateFromEpoch(invitation.acceptedAt)
},
{
key: "adminIcons",
nonSortable: true,
header: "",
mapper: invitation => actionIcons(invitation)
}];
const filteredInvitations = filterValue.value === allValue ? invitations.current :
invitations.current.filter(invitation => invitation.status === filterValue.value ||
Expand Down Expand Up @@ -319,6 +388,7 @@ export const Invitations = ({
newEntityFunc={role ? () => navigate("/invitation/new", {state: role.id}) : null}
customNoEntities={I18n.t(`invitations.noResults`)}
loading={loading}
onHover={true}
hideTitle={loading}
filters={filter(filterOptions, filterValue)}
actions={actionButtons()}
Expand Down
3 changes: 3 additions & 0 deletions client/src/tabs/Invitations.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
&.expiryDate {
width: 15%;
}
&.adminIcons {
width: 80px;
}

}
}
Expand Down
2 changes: 1 addition & 1 deletion client/src/tabs/PerformanceSeed.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const PerformanceSeed = () => {
</div>
{seed &&
<div className="results">
<p>{I18n.t("system.performanceMillis", {millis: millis})}</p>
<p>{I18n.t("system.performanceLoadTime", {minutes: Math.round(millis / (60 * 60 * 1000)) })}</p>
<code className="results">
{JSON.stringify(seed, null, 4)}
</code>
Expand Down
10 changes: 6 additions & 4 deletions client/src/tabs/Roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {AUTHORITIES, highestAuthority, isUserAllowed, markAndFilterRoles} from "
import {rolesByApplication} from "../api";
import {isEmpty, stopEvent} from "../utils/Utils";
import debounce from "lodash.debounce";
import {chipTypeForUserRole} from "../utils/Authority";
import {authorityForRole, chipTypeForUserRole} from "../utils/Authority";
import {ReactComponent as VoidImage} from "../icons/undraw_void_-3-ggu.svg";
import {ReactComponent as AlertLogo} from "@surfnet/sds/icons/functional-icons/alert-circle.svg";
import DOMPurify from "dompurify";
Expand Down Expand Up @@ -140,9 +140,11 @@ export const Roles = () => {
nonSortable: true,
key: "authority",
header: I18n.t("roles.authority"),
mapper: role => <Chip type={chipTypeForUserRole(role.authority)}
label={role.isUserRole ? I18n.t(`access.${role.authority}`) :
I18n.t("roles.noMember")}/>
mapper: role => {
const authority = authorityForRole(user, role);
const label = authority ? I18n.t(`access.${authority}`) : I18n.t("roles.noMember");
return <Chip type={chipTypeForUserRole(authority)}
label={label}/>}
},
{
key: "userRoleCount",
Expand Down
Loading

0 comments on commit 4eca731

Please sign in to comment.