Skip to content

Commit afcfc54

Browse files
committed
Fixes #649
1 parent 11a5f34 commit afcfc54

19 files changed

Lines changed: 262 additions & 30 deletions

File tree

client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<groupId>org.openconext</groupId>
66
<artifactId>invite</artifactId>
7-
<version>1.1.9</version>
7+
<version>1.1.10-SNAPSHOT</version>
88
<relativePath>../pom.xml</relativePath>
99
</parent>
1010
<artifactId>invite-client</artifactId>

client/src/api/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ export function invitationsByRoleId(roleId) {
142142
return fetchJson(`/api/v1/invitations/roles/${roleId}`, {}, {}, false);
143143
}
144144

145+
export function invitationsMine() {
146+
return fetchJson("/api/v1/invitations/mine", {}, {}, false);
147+
}
148+
145149
export function resendInvitation(invitationId) {
146150
return postPutJson(`/api/v1/invitations/${invitationId}`, {}, "PUT");
147151
}

client/src/locale/en.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,14 @@ const en = {
242242
invitations: {
243243
title: "Invitations",
244244
searchPlaceHolder: "Search for invitation...",
245-
noResults: "No invitation where found",
245+
noResults: "No invitations where found",
246246
inviter: "Invited by",
247247
status: "Status",
248248
pending: "pending",
249249
open: "Open",
250250
accepted: "Accepted",
251251
expired: "Expired",
252+
mine: "Mine open invitations",
252253
enforceEmailEquality: "Email equality",
253254
eduIDOnly: "eduID only",
254255
requestedAuthnContext: "ACR value",

client/src/locale/nl.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ const nl = {
250250
open: "Open",
251251
accepted: "Geaccepteerd",
252252
expired: "Verlopen",
253+
mine: "Mijn open uitnodigingen",
253254
enforceEmailEquality: "E-mailadres moet overeenkomen",
254255
eduIDOnly: "Uitsluitend eduID",
255256
requestedAuthnContext: "ACR waarde",

client/src/locale/pt.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ const pt = {
246246
open: "Open",
247247
accepted: "Accepted",
248248
expired: "Expired",
249+
mine: "Mine open invitations",
249250
enforceEmailEquality: "Email equality",
250251
eduIDOnly: "eduID only",
251252
requestedAuthnContext: "ACR value",

client/src/pages/Home.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {Tokens} from "../tabs/Tokens";
1414
import {ApplicationUsers} from "../tabs/ApplicationUsers";
1515
import Applications from "../tabs/Applications";
1616
import {isEmpty} from "../utils/Utils";
17+
import {Invitations} from "../tabs/Invitations";
18+
import {MineInvitations} from "../tabs/MineInvitations";
1719

1820
export const Home = () => {
1921
const {tab = "roles"} = useParams();
@@ -53,6 +55,12 @@ export const Home = () => {
5355
label={I18n.t("tabs.applications")}>
5456
<Applications/>
5557
</Page> : null,
58+
(user && !user.superUser && user.institutionAdmin && user.organizationGUID) ?
59+
<Page key="invitations"
60+
name="invitations"
61+
label={I18n.t("tabs.invitations")}>
62+
<MineInvitations/>
63+
</Page> : null,
5664
(user && (user.superUser || (user.institutionAdmin && user.organizationGUID))) ?
5765
<Page key="tokens"
5866
name="tokens"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React, {useEffect, useState} from "react";
2+
import I18n from "../locale/I18n";
3+
import "./MineInvitations.scss";
4+
import {Chip} from "@surfnet/sds";
5+
import {Entities} from "../components/Entities";
6+
import "./Users.scss";
7+
import {shortDateFromEpoch} from "../utils/Date";
8+
9+
import {chipTypeForUserRole, invitationExpiry} from "../utils/Authority";
10+
import {invitationsMine} from "../api";
11+
import {useAppStore} from "../stores/AppStore";
12+
import {INVITATION_STATUS} from "../utils/UserRole";
13+
14+
15+
export const MineInvitations = () => {
16+
17+
const {user} = useAppStore(state => state);
18+
const [invitations, setInvitations] = useState([]);
19+
20+
useEffect(() => {
21+
invitationsMine()
22+
.then(res => {
23+
res.forEach(invitation => {
24+
invitation.intendedRoles = (invitation.roles || [])
25+
.sort((r1, r2) => r1.role.name.localeCompare(r2.role.name))
26+
.map(role => role.role.name).join(", ");
27+
const now = new Date();
28+
invitation.status = new Date(invitation.expiryDate * 1000) < now ? INVITATION_STATUS.EXPIRED : invitation.status;
29+
});
30+
setInvitations(res);
31+
})
32+
},
33+
[user])
34+
35+
36+
const columns = [
37+
{
38+
key: "email",
39+
header: I18n.t("users.email"),
40+
mapper: invitation => <span>{invitation.email}</span>
41+
},
42+
{
43+
key: "intended_authority",
44+
header: I18n.t("users.authority"),
45+
mapper: invitation => <Chip type={chipTypeForUserRole(invitation.intendedAuthority)}
46+
label={I18n.t(`access.${invitation.intendedAuthority}`)}/>
47+
},
48+
{
49+
key: "intendedRoles",
50+
nonSortable: true,
51+
header: I18n.t("invitations.intendedRoles"),
52+
mapper: invitation => invitation.intendedRoles
53+
},
54+
{
55+
key: "name",
56+
header: I18n.t("invitations.inviter"),
57+
mapper: invitation => <div className="user-name-email">
58+
<span className="name">{invitation.inviter.name}</span>
59+
<span className="email">{invitation.inviter.email}</span>
60+
</div>
61+
},
62+
{
63+
key: "created_at",
64+
header: I18n.t("invitations.createdAt"),
65+
mapper: invitation => shortDateFromEpoch(invitation.createdAt, false)
66+
},
67+
{
68+
key: "expiry_date",
69+
header: I18n.t("invitations.expiryDate"),
70+
mapper: invitation => invitationExpiry(invitation)
71+
}
72+
]
73+
74+
return (<div className="mod-mine-invitations">
75+
<Entities entities={invitations}
76+
modelName="invitations"
77+
defaultSort="email"
78+
title={I18n.t("invitations.mine")}
79+
columns={columns}
80+
customNoEntities={I18n.t(`invitations.noResults`)}
81+
showNew={false}
82+
loading={false}
83+
searchAttributes={["intendedRoles", "email", "intendedAuthority", "inviter__email", "inviter__name"]}
84+
inputFocus={true}
85+
>
86+
</Entities>
87+
</div>)
88+
89+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.mod-mine-invitations {
2+
background-color: white;
3+
4+
table.invitations {
5+
6+
thead {
7+
th {
8+
&.email {
9+
width: 20%;
10+
}
11+
12+
&.intended_authority {
13+
width: 15%;
14+
text-align: center;
15+
}
16+
17+
&.intendedRoles {
18+
width: 15%;
19+
}
20+
21+
&.name {
22+
width: 26%;
23+
}
24+
25+
&.created_at {
26+
width: 12%;
27+
}
28+
29+
&.expiry_date {
30+
width: 12%;
31+
}
32+
33+
}
34+
}
35+
36+
tbody {
37+
td {
38+
&.email {
39+
word-break: keep-all;
40+
}
41+
42+
&.intended_authority {
43+
text-align: center;
44+
}
45+
46+
}
47+
}
48+
}
49+
50+
}

client/src/utils/Authority.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ export const chipTypeForUserRole = authority => {
3636
}
3737

3838
export const invitationExpiry = invitation => {
39-
const expired = new Date(invitation.expiry_date) < new Date();
39+
const expiryDate = isEmpty(invitation.expiry_date) ? new Date(invitation.expiryDate * 1000) :
40+
new Date(invitation.expiry_date);
41+
const expired = expiryDate < new Date();
4042
if (expired) {
4143
return <Chip
4244
type={ChipType.Status_error}
4345
label={I18n.t("invitations.statuses.expired")}/>
4446
}
45-
return shortDateFromEpoch(invitation.expiry_date, false);
47+
return shortDateFromEpoch(invitation.expiry_date || invitation.expiryDate * 1000, false);
4648
}
4749

4850
export const authorityForRole = (user, role) => {

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>org.openconext</groupId>
55
<artifactId>invite</artifactId>
6-
<version>1.1.9</version>
6+
<version>1.1.10-SNAPSHOT</version>
77
<packaging>pom</packaging>
88
<name>invite</name>
99
<description>SURFconext Invite</description>

0 commit comments

Comments
 (0)