Skip to content

Commit 481f068

Browse files
feat(institution-users): added improvements by suggestions
1 parent a260a40 commit 481f068

20 files changed

+231
-185
lines changed

src/app/features/admin-institutions/components/admin-table/admin-table.component.html

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
<div class="flex justify-content-between align-items-center p-3 border-bottom-1 surface-border">
2-
<div class="amount-section">
1+
<div class="flex justify-content-between align-items-center p-3">
2+
<div>
33
<ng-content select="[slot=amount]"></ng-content>
44
</div>
55

6+
<p-menu #downloadMenu [model]="downloadMenuItems()" appendTo="body" popup>
7+
<ng-template #item let-item>
8+
<a class="p-menu-item-link ml-1" target="_blank" [href]="item.link">
9+
<i [class]="item.icon"></i>
10+
11+
{{ item.label }}
12+
</a>
13+
</ng-template>
14+
</p-menu>
15+
616
<div class="filters-section flex align-items-center gap-2">
717
<ng-content select="[slot=otherFilters]"></ng-content>
818

@@ -11,38 +21,39 @@
1121
[(ngModel)]="selectedColumns"
1222
(onChange)="onColumnSelectionChange($event.value)"
1323
optionLabel="header"
14-
[placeholder]="customizeLabel()"
1524
[showToggleAll]="false"
1625
[showClear]="false"
1726
[dropdownIcon]="'hidden'"
1827
>
1928
<ng-template let-values pTemplate="selectedItems">
2029
<div class="flex align-items-center gap-2">
21-
<i class="pi pi-table text-primary font-bold text-xl"></i>
22-
<span class="text-lg">{{ 'adminInstitutions.institutionUsers.customize' | translate }}</span>
30+
<i class="fa fa-table-columns text-primary font-bold"></i>
31+
<span>{{ 'adminInstitutions.institutionUsers.customize' | translate }}</span>
2332
</div>
2433
</ng-template>
34+
35+
<ng-template #item let-item>
36+
{{ item.header | translate }}
37+
</ng-template>
2538
</p-multiselect>
2639

2740
@if (downloadLink()) {
28-
<a
29-
pButton
30-
[href]="downloadLink()"
31-
class="p-button p-button-outlined p-button-sm p-08 font-bold no-underline grey-border-color"
32-
target="_blank"
33-
>
34-
<i class="pi pi-download text-primary text-xl"></i>
35-
</a>
41+
<p-button
42+
icon="fa fa-download text-primary text-xl"
43+
class="p-button p-button-outlined p-button-sm p-06 font-bold grey-border-color child-button-0-padding"
44+
severity="info"
45+
(click)="downloadMenu.toggle($event)"
46+
/>
3647
}
3748

3849
@if (reportsLink()) {
3950
<a
4051
pButton
4152
[href]="reportsLink()"
42-
class="p-button p-button-outlined p-button-sm p-08 font-bold no-underline grey-border-color"
53+
class="p-button p-button-outlined p-button-sm p-06 font-bold grey-border-color child-button-0-padding"
4354
target="_blank"
4455
>
45-
<i class="pi pi-chart-pie text-primary text-xl"></i>
56+
<i class="fa fa-chart-pie text-primary border-1 border-transparent text-xl"></i>
4657
</a>
4758
}
4859
</div>
@@ -87,17 +98,17 @@
8798
[target]="getLinkTarget(rowData[col.field], col)"
8899
class="text-primary no-underline hover:underline"
89100
>
90-
{{ getCellValue(rowData[col.field]) }}
101+
{{ getCellValueWithFormatting(rowData[col.field], col) }}
91102
</a>
92103
} @else {
93-
{{ getCellValue(rowData[col.field]) }}
104+
{{ getCellValueWithFormatting(rowData[col.field], col) }}
94105
}
95106

96107
@if (col.showIcon) {
97108
<p-button
98-
class="cursor-pointer icon-button pl-3"
109+
[pTooltip]="col.iconTooltip | translate"
110+
class="icon-button pl-3"
99111
[icon]="col.iconClass"
100-
iconPos="right"
101112
variant="text"
102113
severity="info"
103114
(click)="onIconClick(rowData, col)"

src/app/features/admin-institutions/components/admin-table/admin-table.component.scss

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
.p-08 {
2-
padding: 0.7rem;
1+
.p-06 {
2+
padding: 0.6rem;
33
}
44

55
.hover-group {
@@ -14,3 +14,8 @@
1414
}
1515
}
1616
}
17+
18+
.child-button-0-padding {
19+
--p-button-padding-y: 0;
20+
--p-button-icon-only-width: max-content;
21+
}

src/app/features/admin-institutions/components/admin-table/admin-table.component.ts

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
22

33
import { SortEvent } from 'primeng/api';
44
import { Button, ButtonDirective } from 'primeng/button';
5+
import { Menu } from 'primeng/menu';
56
import { MultiSelect } from 'primeng/multiselect';
67
import { PaginatorState } from 'primeng/paginator';
78
import { TableModule } from 'primeng/table';
9+
import { Tooltip } from 'primeng/tooltip';
810

911
import { ChangeDetectionStrategy, Component, computed, effect, inject, input, output, signal } from '@angular/core';
1012
import { FormsModule } from '@angular/forms';
@@ -21,7 +23,17 @@ import { QueryParams } from '@shared/models';
2123

2224
@Component({
2325
selector: 'osf-admin-table',
24-
imports: [MultiSelect, TableModule, FormsModule, ButtonDirective, CustomPaginatorComponent, TranslatePipe, Button],
26+
imports: [
27+
MultiSelect,
28+
TableModule,
29+
FormsModule,
30+
ButtonDirective,
31+
CustomPaginatorComponent,
32+
Tooltip,
33+
TranslatePipe,
34+
Button,
35+
Menu,
36+
],
2537
templateUrl: './admin-table.component.html',
2638
styleUrl: './admin-table.component.scss',
2739
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -39,7 +51,7 @@ export class AdminTableComponent {
3951
first = input<number>(0);
4052

4153
sortField = input<string>('');
42-
sortOrder = input<number>(1); // 1 for asc, -1 for desc
54+
sortOrder = input<number>(1);
4355

4456
pageChanged = output<PaginatorState>();
4557
sortChanged = output<QueryParams>();
@@ -48,12 +60,31 @@ export class AdminTableComponent {
4860
downloadLink = input<string>('');
4961
reportsLink = input<string>('');
5062

51-
downloadLabel = input<string>('Download');
52-
reportsLabel = input<string>('Previous Reports');
53-
customizeLabel = input<string>('Customize');
54-
5563
selectedColumns = signal<TableColumn[]>([]);
5664

65+
downloadMenuItems = computed(() => {
66+
const baseUrl = this.downloadLink();
67+
if (!baseUrl) return [];
68+
69+
return [
70+
{
71+
label: 'CSV',
72+
icon: 'fa fa-file-csv',
73+
link: this.createUrl(baseUrl, 'csv'),
74+
},
75+
{
76+
label: 'TSV',
77+
icon: 'fa fa-file-alt',
78+
link: this.createUrl(baseUrl, 'tsv'),
79+
},
80+
{
81+
label: 'JSON',
82+
icon: 'fa fa-file-code',
83+
link: this.createUrl(baseUrl, 'json'),
84+
},
85+
];
86+
});
87+
5788
selectedColumnsComputed = computed(() => {
5889
const selected = this.selectedColumns();
5990
const allColumns = this.tableColumns();
@@ -115,6 +146,38 @@ export class AdminTableComponent {
115146
return this.translateService.instant(String(value)) || '';
116147
}
117148

149+
getCellValueWithFormatting(value: string | number | TableCellLink | undefined, column: TableColumn): string {
150+
if (this.isLink(value)) {
151+
return this.translateService.instant(value.text);
152+
}
153+
154+
const stringValue = String(value);
155+
156+
if (column.dateFormat && stringValue) {
157+
return this.formatDate(stringValue, column.dateFormat);
158+
}
159+
160+
return this.translateService.instant(stringValue) || '';
161+
}
162+
163+
private formatDate(value: string, format: string): string {
164+
if (format === 'yyyy-mm-to-mm/yyyy') {
165+
const yearMonthRegex = /^(\d{4})-(\d{2})$/;
166+
const match = value.match(yearMonthRegex);
167+
168+
if (match) {
169+
const [, year, month] = match;
170+
return `${month}/${year}`;
171+
}
172+
}
173+
174+
return value;
175+
}
176+
177+
private createUrl(baseUrl: string, format: string): string {
178+
return `${baseUrl}?format=${format}`;
179+
}
180+
118181
getLinkUrl(value: string | number | TableCellLink | undefined): string {
119182
if (this.isLink(value)) {
120183
return value.url;

src/app/features/admin-institutions/constants/admin-table-columns.constant.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,36 @@ export const userTableColumns: TableColumn[] = [
55
field: 'userName',
66
header: 'settings.profileSettings.tabs.name',
77
sortable: true,
8-
isLink: true,
8+
isLink: false,
99
linkTarget: '_blank',
1010
showIcon: true,
11-
iconClass: 'pi pi-comment text-primary',
12-
iconTooltip: 'Send message',
11+
iconClass: 'fa-solid fa-comment text-primary',
12+
iconTooltip: 'adminInstitutions.institutionUsers.sendMessage',
1313
iconAction: 'sendMessage',
1414
},
1515
{ field: 'department', header: 'settings.profileSettings.education.department', sortable: true },
16-
{ field: 'userLink', header: 'OSF Link', isLink: true, linkTarget: '_blank' },
17-
{ field: 'orcidId', header: 'ORCID', isLink: true, linkTarget: '_blank' },
16+
{ field: 'userLink', header: 'adminInstitutions.institutionUsers.osfLink', isLink: false, linkTarget: '_blank' },
17+
{ field: 'orcidId', header: 'adminInstitutions.institutionUsers.orcid', isLink: true, linkTarget: '_blank' },
1818
{ field: 'publicProjects', header: 'adminInstitutions.summary.publicProjects', sortable: true },
1919
{ field: 'privateProjects', header: 'adminInstitutions.summary.privateProjects', sortable: true },
20-
{ field: 'monthLastLogin', header: 'adminInstitutions.institutionUsers.lastLogin', sortable: true },
21-
{ field: 'monthLastActive', header: 'adminInstitutions.institutionUsers.lastActive', sortable: true },
22-
{ field: 'accountCreationDate', header: 'adminInstitutions.institutionUsers.accountCreated', sortable: true },
20+
{
21+
field: 'monthLastLogin',
22+
header: 'adminInstitutions.institutionUsers.lastLogin',
23+
sortable: true,
24+
dateFormat: 'yyyy-mm-to-mm/yyyy',
25+
},
26+
{
27+
field: 'monthLastActive',
28+
header: 'adminInstitutions.institutionUsers.lastActive',
29+
sortable: true,
30+
dateFormat: 'yyyy-mm-to-mm/yyyy',
31+
},
32+
{
33+
field: 'accountCreationDate',
34+
header: 'adminInstitutions.institutionUsers.accountCreated',
35+
sortable: true,
36+
dateFormat: 'yyyy-mm-to-mm/yyyy',
37+
},
2338
{ field: 'publicRegistrationCount', header: 'adminInstitutions.summary.publicRegistrations', sortable: true },
2439
{ field: 'embargoedRegistrationCount', header: 'adminInstitutions.summary.embargoedRegistrations', sortable: true },
2540
{ field: 'publishedPreprintCount', header: 'adminInstitutions.institutionUsers.publishedPreprints', sortable: true },

src/app/features/admin-institutions/dialogs/send-email-dialog/send-email-dialog.component.html

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,59 @@
22
<div class="flex flex-column gap-2">
33
<textarea
44
pTextarea
5-
[ngModel]="emailContent()"
5+
[(ngModel)]="emailContent"
66
(ngModelChange)="emailContent.set($event)"
77
[placeholder]="'adminInstitutions.institutionUsers.writeEmailText' | translate"
88
[rows]="6"
9-
class="w-full border-2 border-round-xl grey-border-color p-3"
109
[style]="{ 'min-height': '200px' }"
1110
></textarea>
1211
</div>
1312

14-
<div class="flex flex-wrap gap-1 pl-3">
15-
<span class="text-sm">{{ 'adminInstitutions.institutionUsers.sincerelyYours' | translate }}, </span>
16-
<span class="text-sm">{{ config.data }}</span>
13+
<div class="flex flex-column gap-1 pl-3">
14+
<span>{{ 'adminInstitutions.institutionUsers.sincerelyYours' | translate }}, </span>
15+
<span>{{ config.data }}</span>
1716
</div>
1817

1918
<div class="flex flex-column gap-3">
2019
<div class="flex align-items-center gap-2">
2120
<p-checkbox
22-
[ngModel]="ccSender()"
21+
[(ngModel)]="ccSender"
2322
(ngModelChange)="ccSender.set($event)"
2423
binary="true"
2524
inputId="cc-sender"
2625
></p-checkbox>
27-
<label for="cc-sender" class="text-sm font-medium cursor-pointer">{{
26+
<label for="cc-sender" class="m-0 font-bold font-medium cursor-pointer">{{
2827
'adminInstitutions.institutionUsers.ccSender' | translate
2928
}}</label>
3029
</div>
3130

3231
<div class="flex align-items-center gap-2">
3332
<p-checkbox
34-
[ngModel]="allowReplyToSender()"
33+
[(ngModel)]="allowReplyToSender"
3534
(ngModelChange)="allowReplyToSender.set($event)"
3635
binary="true"
3736
inputId="allow-reply"
3837
></p-checkbox>
39-
<label for="allow-reply" class="text-sm font-medium cursor-pointer">{{
38+
<label for="allow-reply" class="m-0 font-bold font-medium cursor-pointer">{{
4039
'adminInstitutions.institutionUsers.allowReplyToSenderAddress' | translate
4140
}}</label>
4241
</div>
4342
</div>
4443

45-
<div class="flex justify-content-end gap-2 mt-4">
46-
<p-button label="Cancel" severity="secondary" variant="outlined" (onClick)="onCancel()"></p-button>
47-
<p-button label="Send" severity="primary" (onClick)="onSend()" [disabled]="!emailContent().trim()"></p-button>
44+
<div class="flex justify-content-between gap-2 mt-4">
45+
<p-button
46+
class="w-full"
47+
styleClass="w-full"
48+
severity="info"
49+
[label]="'common.buttons.cancel' | translate"
50+
(click)="onCancel()"
51+
/>
52+
<p-button
53+
class="w-full"
54+
styleClass="w-full"
55+
[label]="'common.buttons.send' | translate"
56+
(click)="onSend()"
57+
[disabled]="!emailContent().trim()"
58+
/>
4859
</div>
4960
</div>

src/app/features/admin-institutions/dialogs/send-email-dialog/send-email-dialog.component.scss

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,3 @@
22
display: block;
33
padding: 0;
44
}
5-
6-
textarea {
7-
font-family: inherit;
8-
font-size: 0.875rem;
9-
resize: none;
10-
11-
&:focus {
12-
outline: none;
13-
border-color: var(--primary-color);
14-
}
15-
}

src/app/features/admin-institutions/dialogs/send-email-dialog/send-email-dialog.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import { TranslatePipe } from '@ngx-translate/core';
33
import { Button } from 'primeng/button';
44
import { Checkbox } from 'primeng/checkbox';
55
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
6+
import { Textarea } from 'primeng/textarea';
67

78
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
89
import { FormsModule } from '@angular/forms';
910

1011
@Component({
1112
selector: 'osf-send-email-dialog',
12-
imports: [FormsModule, Button, Checkbox, TranslatePipe],
13+
imports: [FormsModule, Button, Checkbox, TranslatePipe, Textarea],
1314
templateUrl: './send-email-dialog.component.html',
1415
styleUrl: './send-email-dialog.component.scss',
1516
changeDetection: ChangeDetectionStrategy.OnPush,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { SendMessageRequest } from '@osf/features/admin-institutions/models';
2+
3+
export function sendMessageRequestMapper(request: SendMessageRequest) {
4+
return {
5+
data: {
6+
attributes: {
7+
message_text: request.messageText,
8+
message_type: 'institutional_request',
9+
bcc_sender: request.bccSender,
10+
reply_to: request.replyTo,
11+
},
12+
relationships: {
13+
institution: {
14+
data: {
15+
type: 'institutions',
16+
id: request.institutionId,
17+
},
18+
},
19+
},
20+
type: 'user_messages',
21+
},
22+
};
23+
}

0 commit comments

Comments
 (0)