|
11 | 11 | </div> |
12 | 12 | </app-notice> |
13 | 13 |
|
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> |
30 | 14 | @if (!isLoading) { |
31 | 15 | <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"> {{ t("me") }}</b> |
51 | | - } |
52 | | - </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"> |
67 | | - <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> |
| 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 | + <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) : t("role_unknown") }}</em> |
94 | 63 | </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") }} |
| 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"> |
| 86 | + <em>{{ userRow.role ? i18n.localizeRole(userRow.role) : t("role_unknown") }}</em> |
| 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> |
117 | 94 | </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 | + } |
119 | 116 | <button |
120 | 117 | 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" |
124 | 121 | > |
125 | | - {{ t("cancel_invite") }} |
| 122 | + {{ t("edit_roles_and_permissions") }} |
126 | 123 | </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 | + } |
145 | 135 | } |
146 | 136 | </div> |
147 | 137 | } |
|
0 commit comments