• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CenterForOpenScience / ember-osf-web / 12241206680

09 Dec 2024 05:42PM UTC coverage: 64.754% (-0.08%) from 64.836%
12241206680

Pull #2421

github

web-flow
Merge 7639ea116 into 951bcb2a8
Pull Request #2421: [ENG-6665] Add user messaging modal to user's tab on institutional dashboard

2736 of 4640 branches covered (58.97%)

Branch coverage included in aggregate %.

2 of 18 new or added lines in 2 files covered. (11.11%)

25 existing lines in 1 file now uncovered.

6935 of 10295 relevant lines covered (67.36%)

201.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

48.15
/app/institutions/dashboard/-components/institutional-users-list/component.ts
1
import { task } from 'ember-concurrency';
2
import Component from '@glimmer/component';
3
import { tracked } from '@glimmer/tracking';
4
import { action } from '@ember/object';
5
import { inject as service } from '@ember/service';
6
import { waitFor } from '@ember/test-waiters';
7
import { restartableTask, timeout } from 'ember-concurrency';
8
import Intl from 'ember-intl/services/intl';
9

10
import InstitutionModel from 'ember-osf-web/models/institution';
11
import InstitutionDepartmentsModel from 'ember-osf-web/models/institution-department';
12
import Analytics from 'ember-osf-web/services/analytics';
13

14
interface Column {
15
    key: string;
16
    selected: boolean;
17
    label: string;
18
    sort_key: string | false;
19
    type: 'string' | 'date_by_month' | 'osf_link' | 'user_name' | 'orcid';
20
}
21

22
interface InstitutionalUsersListArgs {
23
    institution: InstitutionModel;
24
    departmentMetrics: InstitutionDepartmentsModel[];
25
}
26

27
export default class InstitutionalUsersList extends Component<InstitutionalUsersListArgs> {
28
    @service analytics!: Analytics;
29
    @service intl!: Intl;
30
    @service store;
31
    @service currentUser!: CurrentUser;
32

33
    institution?: InstitutionModel;
34

35
    departmentMetrics?: InstitutionDepartmentsModel[];
36

37
    // Properties
38
    @tracked department = this.defaultDepartment;
3✔
39
    @tracked sort = 'user_name';
3✔
UNCOV
40
    @tracked selectedDepartments: string[] = [];
×
41
    @tracked filteredUsers = [];
×
42
    @tracked messageModalShown = false;
3✔
NEW
UNCOV
43
    @tracked messageText = '';
×
NEW
44
    @tracked selectedUserId = null;
×
45
    @service toast!: Toast;
46

47
    @tracked columns: Column[] = [
3✔
48
        {
49
            key: 'user_name',
50
            sort_key: 'user_name',
51
            label: this.intl.t('institutions.dashboard.users_list.name'),
52
            selected: true,
53
            type: 'user_name',
54
        },
55
        {
56
            key: 'department',
57
            sort_key: 'department',
58
            label: this.intl.t('institutions.dashboard.users_list.department'),
59
            selected: true,
60
            type: 'string',
61
        },
62
        {
63
            key: 'osf_link',
64
            sort_key: false,
65
            label: this.intl.t('institutions.dashboard.users_list.osf_link'),
66
            selected: true,
67
            type: 'osf_link',
68
        },
69
        {
70
            key: 'orcid',
71
            sort_key: false,
72
            label: this.intl.t('institutions.dashboard.users_list.orcid'),
73
            selected: true,
74
            type: 'orcid',
75
        },
76
        {
77
            key: 'publicProjects',
78
            sort_key: 'public_projects',
79
            label: this.intl.t('institutions.dashboard.users_list.public_projects'),
80
            selected: true,
81
            type: 'string',
82
        },
83
        {
84
            key: 'privateProjects',
85
            sort_key: 'private_projects',
86
            label: this.intl.t('institutions.dashboard.users_list.private_projects'),
87
            selected: true,
88
            type: 'string',
89
        },
90
        {
91
            key: 'publicRegistrationCount',
92
            sort_key: 'public_registration_count',
93
            label: this.intl.t('institutions.dashboard.users_list.public_registration_count'),
94
            selected: true,
95
            type: 'string',
96
        },
97
        {
98
            key: 'embargoedRegistrationCount',
99
            sort_key: 'embargoed_registration_count',
100
            label: this.intl.t('institutions.dashboard.users_list.private_registration_count'),
101
            selected: true,
102
            type: 'string',
103
        },
104
        {
105
            key: 'publishedPreprintCount',
106
            sort_key: 'published_preprint_count',
107
            label: this.intl.t('institutions.dashboard.users_list.published_preprint_count'),
108
            selected: true,
109
            type: 'string',
110
        },
111
        {
112
            key: 'publicFileCount',
113
            sort_key: 'public_file_count',
114
            label: this.intl.t('institutions.dashboard.users_list.public_file_count'),
115
            selected: false,
116
            type: 'string',
117
        },
118
        {
119
            key: 'userDataUsage',
120
            sort_key: 'storage_byte_count',
121
            label: this.intl.t('institutions.dashboard.users_list.storage_byte_count'),
122
            selected: false,
123
            type: 'string',
124
        },
125
        {
126
            key: 'accountCreationDate',
127
            sort_key: 'account_creation_date',
128
            label: this.intl.t('institutions.dashboard.users_list.account_created'),
129
            selected: false,
130
            type: 'date_by_month',
131
        },
132
        {
133
            key: 'monthLastLogin',
134
            sort_key: 'month_last_login',
135
            label: this.intl.t('institutions.dashboard.users_list.month_last_login'),
136
            selected: false,
137
            type: 'date_by_month',
138
        },
139
        {
140
            key: 'monthLastActive',
141
            sort_key: 'month_last_active',
142
            label: this.intl.t('institutions.dashboard.users_list.month_last_active'),
143
            selected: false,
144
            type: 'date_by_month',
145
        },
146
    ];
147

148
    @tracked selectedColumns: string[] = this.columns.filter(col => col.selected).map(col => col.key);
42✔
149
    // Private properties
150
    @tracked hasOrcid = false;
3✔
151
    @tracked totalUsers = 0;
3✔
152
    orcidUrlPrefix = 'https://orcid.org/';
3✔
153

154
    @action
155
    toggleColumnSelection(columnKey: string) {
UNCOV
156
        const column = this.columns.find(col => col.key === columnKey);
×
UNCOV
157
        if (column) {
×
UNCOV
158
            column.selected = !column.selected;
×
159
        }
160
    }
161

162
    get defaultDepartment() {
163
        return this.intl.t('institutions.dashboard.select_default');
12✔
164
    }
165

166
    get departments() {
167
        let departments = [this.defaultDepartment];
3✔
168

169
        if (this.args.institution && this.args.departmentMetrics) {
3✔
170
            const institutionDepartments = this.args.departmentMetrics.map((x: InstitutionDepartmentsModel) => x.name);
3✔
171
            departments = departments.concat(institutionDepartments);
1✔
172
        }
173
        return departments;
3✔
174
    }
175

176
    get isDefaultDepartment() {
177
        return this.department === this.defaultDepartment;
6✔
178
    }
179

180
    get queryUsers() {
181
        const query = {} as Record<string, string>;
6✔
182
        if (this.department && !this.isDefaultDepartment) {
6!
UNCOV
183
            query['filter[department]'] = this.department;
×
184
        }
185
        if (this.hasOrcid) {
6!
186
            query['filter[orcid_id][ne]'] = '';
×
187
        }
188
        if (this.sort) {
6!
189
            query.sort = this.sort;
6✔
190
        }
191
        return query;
6✔
192
    }
193

194
    @restartableTask
195
    @waitFor
196
    async searchDepartment(name: string) {
UNCOV
197
        await timeout(500);
×
UNCOV
198
        if (this.institution) {
×
UNCOV
199
            const depts: InstitutionDepartmentsModel[] = await this.args.institution.queryHasMany('departmentMetrics', {
×
200
                filter: {
201
                    name,
202
                },
203
            });
UNCOV
204
            return depts.map(dept => dept.name);
×
205
        }
UNCOV
206
        return [];
×
207
    }
208

209
    @action
210
    onSelectChange(department: string) {
UNCOV
211
        this.department = department;
×
212
    }
213

214
    @action
215
    sortInstitutionalUsers(sortBy: string) {
216
        if (this.sort === sortBy) {
3✔
217
            // If the current sort is ascending, toggle to descending
218
            this.sort = `-${sortBy}`;
1✔
219
        } else if (this.sort === `-${sortBy}`) {
2✔
220
            // If the current sort is descending, toggle to ascending
221
            this.sort = sortBy;
1✔
222
        } else {
223
            // Set to descending if it's a new sort field
224
            this.sort = `-${sortBy}`;
1✔
225
        }
226
    }
227

228
    @action
229
    cancelSelection() {
UNCOV
230
        this.selectedDepartments = [];
×
231
    }
232

233
    @action
234
    applyColumnSelection() {
UNCOV
235
        this.selectedColumns = this.columns.filter(col => col.selected).map(col => col.key);
×
236
    }
237

238
    @action
239
    toggleOrcidFilter(hasOrcid: boolean) {
UNCOV
240
        this.hasOrcid = hasOrcid;
×
241
    }
242

243
    @action
244
    clickToggleOrcidFilter(hasOrcid: boolean) {
UNCOV
245
        this.hasOrcid = !hasOrcid;
×
246
    }
247

248
    @action
249
    openMessageModal(userId: string) {
NEW
UNCOV
250
        this.selectedUserId = userId;
×
NEW
UNCOV
251
        this.messageModalShown = true;
×
252
    }
253

254
    @action
255
    updateMessageText(event: Event) {
NEW
UNCOV
256
        this.messageText = (event.target as HTMLTextAreaElement).value;
×
257
    }
258

259
    @task
260
    @waitFor
261
    async sendMessage() {
NEW
UNCOV
262
        if (!this.selectedUserId || !this.messageText.trim()) {
×
NEW
UNCOV
263
            return;
×
264
        }
265

NEW
UNCOV
266
        try {
×
NEW
UNCOV
267
            const userMessage = this.store.createRecord('user-message', {
×
268
                messageText: this.messageText.trim(),
269
                messageType: 'institutional_request',
270
                institution: this.args.institution,
271
                user: this.selectedUserId,
272
            });
NEW
UNCOV
273
            await userMessage.save();
×
NEW
UNCOV
274
            this.toast.success(this.intl.t('institutions.dashboard.send_message_modal.message_sent_success'));
×
275
        } catch (error) {
NEW
276
            this.toast.error(this.intl.t('institutions.dashboard.send_message_modal.message_sent_failed'));
×
277
        } finally {
NEW
278
            this.messageModalShown = false;
×
NEW
279
            this.messageText = '';
×
280
        }
281
    }
282
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc