Skip to content

Commit b66a484

Browse files
committed
SF-2472 Show Paratext users who have not join the project
1 parent 0c0df6f commit b66a484

File tree

13 files changed

+508
-431
lines changed

13 files changed

+508
-431
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/connect-project/connect-project.component.spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,8 @@ class TestEnvironment {
298298
isConnectable: true,
299299
isConnected: false,
300300
hasUserRoleChanged: false,
301-
hasUpdate: false
301+
hasUpdate: false,
302+
members: []
302303
},
303304
{
304305
paratextId: 'pt02',
@@ -309,7 +310,8 @@ class TestEnvironment {
309310
isConnectable: false,
310311
isConnected: true,
311312
hasUserRoleChanged: false,
312-
hasUpdate: false
313+
hasUpdate: false,
314+
members: []
313315
},
314316
{
315317
paratextId: 'pt04',
@@ -319,7 +321,8 @@ class TestEnvironment {
319321
isConnectable: false,
320322
isConnected: false,
321323
hasUserRoleChanged: false,
322-
hasUpdate: false
324+
hasUpdate: false,
325+
members: []
323326
},
324327
{
325328
paratextId: 'pt03',
@@ -330,7 +333,8 @@ class TestEnvironment {
330333
isConnectable: true,
331334
isConnected: true,
332335
hasUserRoleChanged: false,
333-
hasUpdate: false
336+
hasUpdate: false,
337+
members: []
334338
}
335339
];
336340

src/SIL.XForge.Scripture/ClientApp/src/app/core/models/paratext-project.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
import { SFProjectRole } from 'realtime-server/lib/esm/scriptureforge/models/sf-project-role';
2+
3+
export interface ParatextMember {
4+
connectedToProject: boolean;
5+
username: string;
6+
role: SFProjectRole;
7+
}
8+
19
/** See documentation in ParatextProject.cs. */
210
export interface ParatextProject {
311
paratextId: string;
@@ -8,6 +16,7 @@ export interface ParatextProject {
816
projectId?: string | null;
917
isConnectable: boolean;
1018
isConnected: boolean;
19+
members: ParatextMember[];
1120
hasUserRoleChanged: boolean;
1221
hasUpdate: boolean;
1322
}

src/SIL.XForge.Scripture/ClientApp/src/app/settings/settings.component.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,8 @@ describe('SettingsComponent', () => {
299299
isConnectable: true,
300300
isConnected: false,
301301
hasUserRoleChanged: false,
302-
hasUpdate: false
302+
hasUpdate: false,
303+
members: []
303304
}
304305
]);
305306
when(mockedParatextService.getResources()).thenResolve([]);
@@ -792,7 +793,8 @@ class TestEnvironment {
792793
isConnectable: true,
793794
isConnected: false,
794795
hasUserRoleChanged: false,
795-
hasUpdate: false
796+
hasUpdate: false,
797+
members: []
796798
},
797799
{
798800
paratextId: 'paratextId02',
@@ -802,7 +804,8 @@ class TestEnvironment {
802804
isConnectable: true,
803805
isConnected: false,
804806
hasUserRoleChanged: false,
805-
hasUpdate: false
807+
hasUpdate: false,
808+
members: []
806809
}
807810
]);
808811
when(mockedParatextService.getResources()).thenResolve([

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-sources/draft-sources.component.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,8 @@ class TestEnvironment {
761761
isConnectable: false,
762762
isConnected: true,
763763
hasUserRoleChanged: false,
764-
hasUpdate: false
764+
hasUpdate: false,
765+
members: []
765766
},
766767
selectableProjectWithLanguageCode: translateSourceToSelectableProjectWithLanguageTag(o.data),
767768
translateSource: {
@@ -787,7 +788,8 @@ class TestEnvironment {
787788
isConnectable: true,
788789
isConnected: false,
789790
hasUserRoleChanged: false,
790-
hasUpdate: false
791+
hasUpdate: false,
792+
members: []
791793
})).map((o: ParatextProject) => ({
792794
paratextProject: o,
793795
selectableProjectWithLanguageCode: o,
@@ -808,7 +810,8 @@ class TestEnvironment {
808810
isConnectable: true,
809811
isConnected: false,
810812
hasUserRoleChanged: false,
811-
hasUpdate: false
813+
hasUpdate: false,
814+
members: []
812815
})).map((o: ParatextProject) => ({
813816
paratextProject: o,
814817
selectableProjectWithLanguageCode: o,

src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/tabs/editor-tab-add-resource-dialog/editor-tab-add-resource-dialog.component.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ class TestEnvironment {
285285
isConnected: false,
286286
hasUserRoleChanged: false,
287287
hasUpdate: false,
288+
members: [],
288289
...overrides
289290
};
290291
}

src/SIL.XForge.Scripture/ClientApp/src/app/users/collaborators/collaborators.component.html

Lines changed: 112 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -11,137 +11,127 @@
1111
</div>
1212
</app-notice>
1313

14-
<div class="users-controls">
15-
<!-- The tab group component sets the currentTabIndex which filters the list of users.
16-
This is a non-standard way to use the component and causes a slight UI glitch where the
17-
tab text jumps a few pixels when navigating between tabs. -->
18-
<div class="tab-selector">
19-
<mat-tab-group [mat-stretch-tabs]="false" (selectedIndexChange)="currentTabIndex = $event">
20-
<mat-tab [label]="t('all')"></mat-tab>
21-
<mat-tab [label]="t('paratext_members')"></mat-tab>
22-
<mat-tab [label]="t('project_guests')"></mat-tab>
23-
</mat-tab-group>
24-
</div>
25-
<mat-form-field [formGroup]="filterForm" appearance="outline" id="project-user-filter">
26-
<mat-label>{{ t("filter_users") }}</mat-label>
27-
<input matInput formControlName="filter" (keyup)="updateSearchTerm($event.target)" />
28-
</mat-form-field>
29-
</div>
3014
@if (!isLoading) {
3115
<div>
32-
@if (filteredLength > 0) {
33-
<div>
34-
<table mat-table fxFill id="project-users-table" [dataSource]="rowsToDisplay">
35-
<ng-container matColumnDef="avatar">
36-
<td mat-cell *matCellDef="let userRow; let i = index">
37-
@if (!userRow.isInvitee) {
38-
<div>
39-
<app-avatar [user]="userRow.user" [size]="32"></app-avatar>
40-
</div>
41-
}
42-
</td>
43-
</ng-container>
44-
<ng-container matColumnDef="name">
45-
<td mat-cell *matCellDef="let userRow">
46-
@if (!userRow.inviteeStatus) {
47-
<div class="display-name-label">
48-
{{ userRow.user?.displayName }}
49-
@if (isCurrentUser(userRow)) {
50-
<b class="current-user-label">&nbsp;{{ t("me") }}</b>
51-
}
16+
@for (userList of projectUsers; track userList.userType) {
17+
<h2>
18+
{{ userList.userType === "paratext" ? t("paratext_members") : t("project_guests") }}
19+
</h2>
20+
@if (userList.rows.length > 0) {
21+
<div>
22+
<table mat-table fxFill id="{{ userList.userType }}" [dataSource]="userList.rows">
23+
<ng-container matColumnDef="avatar">
24+
<td mat-cell *matCellDef="let userRow; let i = index">
25+
@if (userRow.inviteeStatus == null && !userRow.paratextMemberNotConnected) {
26+
<div>
27+
<app-avatar [user]="userRow.user" [size]="32"></app-avatar>
28+
</div>
29+
}
30+
</td>
31+
</ng-container>
32+
<ng-container matColumnDef="name">
33+
<td mat-cell *matCellDef="let userRow">
34+
@if (!userRow.inviteeStatus) {
35+
<div class="display-name-label">
36+
<div>
37+
{{ userRow.user?.displayName }}
38+
@if (isCurrentUser(userRow)) {
39+
&nbsp;<b class="current-user-label">
40+
{{ t("me") }}
41+
</b>
42+
}
43+
</div>
44+
@if (userRow.paratextMemberNotConnected) {
45+
<i>{{ t("paratext_member_not_connected") }}</i>
46+
}
47+
</div>
48+
} @else {
49+
<div
50+
[innerHtml]="
51+
userRow.inviteeStatus.expired
52+
? i18n.translateAndInsertTags('collaborators.invitation_expired', {
53+
email: userRow.user?.email
54+
})
55+
: i18n.translateAndInsertTags('collaborators.awaiting_response_from', {
56+
email: userRow.user?.email
57+
})
58+
"
59+
></div>
60+
}
61+
<div class="hide-gt-sm">
62+
<em>{{ userRow.role ? i18n.localizeRole(userRow.role) : "" }}</em>
5263
</div>
53-
} @else {
54-
<div
55-
[innerHtml]="
56-
userRow.inviteeStatus.expired
57-
? i18n.translateAndInsertTags('collaborators.invitation_expired', {
58-
email: userRow.user?.email
59-
})
60-
: i18n.translateAndInsertTags('collaborators.awaiting_response_from', {
61-
email: userRow.user?.email
62-
})
63-
"
64-
></div>
65-
}
66-
<div class="hide-gt-sm">
64+
</td>
65+
</ng-container>
66+
<ng-container matColumnDef="questions_permission">
67+
<td mat-cell *matCellDef="let userRow">
68+
@if (userRow.allowCreatingQuestions) {
69+
<div [matTooltip]="t('allow_add_edit_questions')">
70+
<mat-icon>post_add</mat-icon>
71+
</div>
72+
}
73+
</td>
74+
</ng-container>
75+
<ng-container matColumnDef="audio_permission">
76+
<td mat-cell *matCellDef="let userRow">
77+
@if (userRow.canManageAudio) {
78+
<div [matTooltip]="t('allow_manage_audio')">
79+
<mat-icon class="shift-left material-icons-outlined">audio_file</mat-icon>
80+
</div>
81+
}
82+
</td>
83+
</ng-container>
84+
<ng-container matColumnDef="role">
85+
<td class="hide-lt-sm" mat-cell *matCellDef="let userRow">
6786
<em>{{ userRow.role ? i18n.localizeRole(userRow.role) : "" }}</em>
68-
</div>
69-
</td>
70-
</ng-container>
71-
<ng-container matColumnDef="info">
72-
<td mat-cell *matCellDef="let userRow">
73-
@if (hasParatextRole(userRow)) {
74-
<div>
75-
<img src="/assets/images/logo-pt9.png" alt="Paratext Logo" class="paratext-logo" />
76-
</div>
77-
}
78-
</td>
79-
</ng-container>
80-
<ng-container matColumnDef="questions_permission">
81-
<td mat-cell *matCellDef="let userRow">
82-
@if (userRow.allowCreatingQuestions) {
83-
<div [matTooltip]="t('allow_add_edit_questions')">
84-
<mat-icon>post_add</mat-icon>
85-
</div>
86-
}
87-
</td>
88-
</ng-container>
89-
<ng-container matColumnDef="audio_permission">
90-
<td mat-cell *matCellDef="let userRow">
91-
@if (userRow.canManageAudio) {
92-
<div [matTooltip]="t('allow_manage_audio')">
93-
<mat-icon class="shift-left material-icons-outlined">audio_file</mat-icon>
94-
</div>
95-
}
96-
</td>
97-
</ng-container>
98-
<ng-container matColumnDef="role">
99-
<td class="hide-lt-sm" mat-cell *matCellDef="let userRow">
100-
<em>{{ userRow.role ? i18n.localizeRole(userRow.role) : "" }}</em>
101-
</td>
102-
</ng-container>
103-
<ng-container matColumnDef="more">
104-
<td mat-cell *matCellDef="let userRow">
105-
<button mat-icon-button class="user-more-menu" [matMenuTriggerFor]="userOptions">
106-
<mat-icon>more_vert</mat-icon>
107-
</button>
108-
<mat-menu #userOptions="matMenu" class="user-options">
109-
@if (!userRow.inviteeStatus && !isCurrentUser(userRow)) {
110-
<button
111-
mat-menu-item
112-
class="remove-user"
113-
(click)="removeProjectUserClicked(userRow)"
114-
[disabled]="!isAppOnline"
115-
>
116-
{{ t("remove_from_project") }}
87+
</td>
88+
</ng-container>
89+
<ng-container matColumnDef="more">
90+
<td mat-cell *matCellDef="let userRow">
91+
@if (!userRow.paratextMemberNotConnected) {
92+
<button mat-icon-button class="user-more-menu" [matMenuTriggerFor]="userOptions">
93+
<mat-icon>more_vert</mat-icon>
11794
</button>
118-
} @else if (userRow.inviteeStatus) {
95+
}
96+
<mat-menu #userOptions="matMenu" class="user-options">
97+
@if (!userRow.inviteeStatus && !isCurrentUser(userRow)) {
98+
<button
99+
mat-menu-item
100+
class="remove-user"
101+
(click)="removeProjectUserClicked(userRow)"
102+
[disabled]="!isAppOnline"
103+
>
104+
{{ t("remove_from_project") }}
105+
</button>
106+
} @else if (userRow.inviteeStatus) {
107+
<button
108+
mat-menu-item
109+
class="cancel-invite"
110+
(click)="uninviteProjectUser(userRow.user.email)"
111+
[disabled]="!isAppOnline"
112+
>
113+
{{ t("cancel_invite") }}
114+
</button>
115+
}
119116
<button
120117
mat-menu-item
121-
class="cancel-invite"
122-
(click)="uninviteProjectUser(userRow.user.email)"
123-
[disabled]="!isAppOnline"
118+
(click)="openRolesDialog(userRow)"
119+
[disabled]="isAdmin(userRow.role) || userRow.inviteeStatus"
120+
data-test-id="edit-roles-and-permissions"
124121
>
125-
{{ t("cancel_invite") }}
122+
{{ t("edit_roles_and_permissions") }}
126123
</button>
127-
}
128-
<button
129-
mat-menu-item
130-
(click)="openRolesDialog(userRow)"
131-
[disabled]="isAdmin(userRow.role) || userRow.inviteeStatus"
132-
data-test-id="edit-roles-and-permissions"
133-
>
134-
{{ t("edit_roles_and_permissions") }}
135-
</button>
136-
</mat-menu>
137-
</td>
138-
</ng-container>
139-
<tr mat-row *matRowDef="let userRow; columns: tableColumns"></tr>
140-
</table>
141-
</div>
142-
}
143-
@if (filteredLength === 0) {
144-
<mat-hint class="no-users-label">{{ t("no_users_found") }}</mat-hint>
124+
</mat-menu>
125+
</td>
126+
</ng-container>
127+
<tr mat-row *matRowDef="let userRow; columns: tableColumns"></tr>
128+
</table>
129+
</div>
130+
} @else {
131+
<mat-hint class="no-users-label" id="{{ `no-users-${userList.userType}`}}">{{
132+
t("no_users_found")
133+
}}</mat-hint>
134+
}
145135
}
146136
</div>
147137
}

0 commit comments

Comments
 (0)