Skip to content

Commit

Permalink
add Project request modal with user messaging tab to the institution…
Browse files Browse the repository at this point in the history
…al dashboard
  • Loading branch information
John Tordoff committed Dec 18, 2024
1 parent f41bc9a commit 5a7fe94
Show file tree
Hide file tree
Showing 11 changed files with 436 additions and 1 deletion.
14 changes: 14 additions & 0 deletions app/adapters/node-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// app/adapters/user-message.js
import { inject as service } from '@ember/service';
import config from 'ember-osf-web/config/environment';
const { OSF: { apiUrl } } = config;
import OsfAdapter from './osf-adapter';

export default class NodeRequestAdapter extends OsfAdapter {
@service session;

urlForCreateRecord(modelName, snapshot) {
const nodeId = snapshot.record.target;
return `${apiUrl}/v2/nodes/${nodeId}/request/`;
}
}
13 changes: 13 additions & 0 deletions app/adapters/user-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// app/adapters/user-message.js
import { inject as service } from '@ember/service';
import config from 'ember-osf-web/config/environment';
const { OSF: { apiUrl } } = config;
import OsfAdapter from './osf-adapter';

export default class UserMessageAdapter extends OsfAdapter {
@service session;
urlForCreateRecord(modelName, snapshot) {
const userId = snapshot.record.user;
return `${apiUrl}/v2/users/${userId}/messages/`;
}
}
125 changes: 125 additions & 0 deletions app/institutions/dashboard/-components/object-list/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import InstitutionModel from 'ember-osf-web/models/institution';
import { SuggestedFilterOperators } from 'ember-osf-web/models/related-property-path';
import SearchResultModel from 'ember-osf-web/models/search-result';
import { Filter } from 'osf-components/components/search-page/component';
import { waitFor } from '@ember/test-waiters';
import { task } from 'ember-concurrency';
import Toast from 'ember-toastr/services/toast';
import Intl from 'ember-intl/services/intl';
import Store from '@ember-data/store';
import CurrentUser from 'ember-osf-web/services/current-user';

import config from 'ember-osf-web/config/environment';

const shareDownloadFlag = config.featureFlagNames.shareDownload;
Expand Down Expand Up @@ -49,6 +56,20 @@ export default class InstitutionalObjectList extends Component<InstitutionalObje
@tracked sortParam?: string;
@tracked visibleColumns = this.args.columns.map(column => column.name);
@tracked dirtyVisibleColumns = [...this.visibleColumns]; // track changes to visible columns before they are saved
@tracked selectedPermission = 'write';
@tracked projectRequestModalShown = false;
@tracked activeTab = 'request-access'; // Default tab
@tracked messageText = '';
@tracked bcc_sender = false;
@tracked replyTo = false;
@tracked selectedUserId = '';
@tracked selectedNodeId = '';
@tracked showSendMessagePrompt = false;
@service toast!: Toast;
@service intl!: Intl;
@service store!: Store;
@service currentUser!: CurrentUser;


get queryOptions() {
const options = {
Expand Down Expand Up @@ -126,6 +147,34 @@ export default class InstitutionalObjectList extends Component<InstitutionalObje
}
}

@action
openProjectRequestModal(contributor: any) {
this.selectedUserId = contributor.user_id;
this.selectedNodeId = contributor.node_id;
this.projectRequestModalShown = true;
}

@action
handleBackToSendMessage() {
this.activeTab = 'send-message';
this.showSendMessagePrompt = false;
setTimeout(() => {
this.projectRequestModalShown = true; // Reopen the main modal
}, 200);

}

@action
closeSendMessagePrompt() {
this.showSendMessagePrompt = false; // Hide confirmation modal without reopening
}

@action
toggleProjectRequestModal() {
this.projectRequestModalShown = !this.projectRequestModalShown;
}


@action
toggleFilter(property: Filter) {
this.page = '';
Expand All @@ -151,4 +200,80 @@ export default class InstitutionalObjectList extends Component<InstitutionalObje
updatePage(newPage: string) {
this.page = newPage;
}

@action
updateSelectedPermission(event: Event) {
this.selectedPermission = (event.target as HTMLInputElement).value;
}

@action
setActiveTab(tabName: string) {
this.activeTab = tabName;
}


@action
resetFields() {
this.selectedPermission = 'read';
this.bcc_sender = false;
this.replyTo = false;
}

@task
@waitFor
async handleSend() {
try {
if (this.activeTab === 'send-message') {
await this._sendUserMessage();
} else if (this.activeTab === 'request-access') {
await this._sendNodeRequest();
}

this.toast.success(
this.intl.t('institutions.dashboard.object-list.request-project-message-modal.message_sent_success'),
);
this.resetFields();
} catch (error) {
const errorDetail = error?.errors?.[0]?.detail || '';

// Check for the specific error where access requests are disabled
if (error.status === 400 && errorDetail.includes('does not have Access Requests enabled')) {
setTimeout(() => {
this.showSendMessagePrompt = true; // timeout to allow the other to exit
}, 200);
} else {
this.toast.error(
this.intl.t('institutions.dashboard.object-list.request-project-message-modal.message_sent_failed'),
);
}
} finally {
this.projectRequestModalShown = false; // Close the main modal
}
}

async _sendUserMessage() {
const userMessage = this.store.createRecord('user-message', {
messageText: this.messageText.trim(),
messageType: 'institutional_request',
bcc_sender: this.bcc_sender,
replyTo: this.replyTo,
institution: this.args.institution,
user: this.selectedUserId,
});
await userMessage.save();
}

async _sendNodeRequest() {
const nodeRequest = this.store.createRecord('node-request', {
comment: this.messageText.trim(),
requestType: 'institutional_access',
requestedPermission: this.selectedPermission,
bcc_sender: this.bcc_sender,
replyTo: this.replyTo,
institution: this.args.institution,
message_recipent: this.selectedUserId,
target: this.selectedNodeId,
});
await nodeRequest.save();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import Intl from 'ember-intl/services/intl';
import { action } from '@ember/object';

import InstitutionModel from 'ember-osf-web/models/institution';
import SearchResultModel from 'ember-osf-web/models/search-result';
Expand All @@ -10,6 +11,7 @@ import { getOsfmapObjects, getSingleOsfmapValue, hasOsfmapValue } from 'ember-os
interface ContributorsFieldArgs {
searchResult: SearchResultModel;
institution: InstitutionModel;
projectRequestModal: (contributor: any) => void;
}

const roleIriToTranslationKey: Record<AttributionRoleIris, string> = {
Expand Down Expand Up @@ -47,13 +49,24 @@ export default class InstitutionalObjectListContributorsField extends Component<
return prioritizedAttributions.slice(0, 2).map(attribution => {
const contributor = getContributorById(contributors, getSingleOsfmapValue(attribution, ['agent']));
const roleIri: AttributionRoleIris = getSingleOsfmapValue(attribution, ['hadRole']);

const regex = /([^?#]+)$/; // Regex to extract the final part after the last slash
const contributorId = contributor?.['@id']?.match(regex)?.[1] || '';

return {
name: getSingleOsfmapValue(contributor,['name']),
name: getSingleOsfmapValue(contributor, ['name']) || 'Unknown Contributor',
user_id: contributorId,
node_id: searchResult.indexCard.get('osfGuid'),
url: getSingleOsfmapValue(contributor, ['identifier']),
permissionLevel: this.intl.t(roleIriToTranslationKey[roleIri]),
};
});
}

@action
handleOpenModal(contributor: any) {
this.args.projectRequestModal(contributor);
}
}

function hasInstitutionAffiliation(contributors: any[], attribution: any, institutionIris: string[]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

.icon-message {
opacity: 0;
color: $color-text-blue-dark;
background-color: inherit !important;
border: 0 !important;
box-shadow: 0 !important;
}

.icon-message:hover {
opacity: 1;
background-color: inherit !important;
}

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
</OsfLink>
{{t 'institutions.dashboard.object-list.table-items.permission-level' permissionLevel=contributor.permissionLevel}}
</div>
<Button
local-class='icon-message'
aria-label={{t 'institutions.dashboard.request_project_message_modal.open_aira_label'}}
{{on 'click' (fn this.handleOpenModal contributor)}}
>
<FaIcon @icon='comment' />
</Button>
{{else}}
<div>
{{t 'institutions.dashboard.object-list.table-items.no-contributors'}}
Expand Down
68 changes: 68 additions & 0 deletions app/institutions/dashboard/-components/object-list/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,74 @@
padding-left: 10px;
}

.message-textarea {
min-width: 450px;
min-height: 280px;
}

.message-label {
display: block;
}

.checkbox-container {
display: flex;
flex-direction: column;
gap: 10px;
padding: 10px;
}

.checkbox-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}

.radiobox-container {
gap: 20px;
}

.radiobox-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}

.tab-container {
display: flex;
border-bottom: 1px solid #ddd;
margin-bottom: 1rem;
}

.tab-button {
flex: 1;
padding: 10px;
text-align: center;
cursor: pointer;
background: #f9f9f9;
border: 0;
border-bottom: 2px solid transparent;

&.active {
border-bottom: 2px solid #007bff;
font-weight: bold;
}

&:hover {
background: #f1f1f1;
}
}

.tab-pane {
padding: 1rem;
background: #fff;
}

.request-modal {
max-width: min-content;
}

.download-dropdown-content {
display: flex;
flex-direction: column;
Expand Down
Loading

0 comments on commit 5a7fe94

Please sign in to comment.