diff --git a/app/adapters/node-request.ts b/app/adapters/node-request.ts new file mode 100644 index 00000000000..32f1d140bc2 --- /dev/null +++ b/app/adapters/node-request.ts @@ -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/`; + } +} diff --git a/app/adapters/user-message.ts b/app/adapters/user-message.ts new file mode 100644 index 00000000000..a92b865c714 --- /dev/null +++ b/app/adapters/user-message.ts @@ -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/`; + } +} diff --git a/app/institutions/dashboard/-components/object-list/component.ts b/app/institutions/dashboard/-components/object-list/component.ts index 629c23e4dbd..db50069d454 100644 --- a/app/institutions/dashboard/-components/object-list/component.ts +++ b/app/institutions/dashboard/-components/object-list/component.ts @@ -6,6 +6,14 @@ 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 { inject as service } from '@ember/service'; +import Intl from 'ember-intl/services/intl'; +import Store from '@ember-data/store'; +import CurrentUser from 'ember-osf-web/services/current-user'; + interface Column { name: string; @@ -46,8 +54,16 @@ export default class InstitutionalObjectList extends Component { + this.projectRequestModalShown = true; // Reopen the main modal + }, 200); - @action - updateMessageText(event: Event) { - this.messageText = (event.target as HTMLTextAreaElement).value; } @action - toggleMessageModal() { - this.messageModalShown = !this.messageModalShown; + closeSendMessagePrompt() { + this.showSendMessagePrompt = false; // Hide confirmation modal without reopening } @action - sendMessage() { - console.log('Message sent:', this.messageText); - this.toggleMessageModal(); // Close modal after sending + toggleProjectRequestModal() { + this.projectRequestModalShown = !this.projectRequestModalShown; } @@ -176,17 +174,73 @@ export default class InstitutionalObjectList extends Component { + 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(); + } } diff --git a/app/institutions/dashboard/-components/object-list/contributors-field/component.ts b/app/institutions/dashboard/-components/object-list/contributors-field/component.ts index 1c0a69f484b..3e66109559e 100644 --- a/app/institutions/dashboard/-components/object-list/contributors-field/component.ts +++ b/app/institutions/dashboard/-components/object-list/contributors-field/component.ts @@ -1,15 +1,17 @@ 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 { getOsfmapObjects, getSingleOsfmapValue, hasOsfmapValue } from 'ember-osf-web/packages/osfmap/jsonld'; import InstitutionModel from 'ember-osf-web/models/institution'; import SearchResultModel from 'ember-osf-web/models/search-result'; import { AttributionRoleIris } from 'ember-osf-web/models/index-card'; -import { getOsfmapObjects, getSingleOsfmapValue, hasOsfmapValue } from 'ember-osf-web/packages/osfmap/jsonld'; interface ContributorsFieldArgs { searchResult: SearchResultModel; institution: InstitutionModel; + projectRequestModal: (contributor: any) => void; } const roleIriToTranslationKey: Record = { @@ -18,20 +20,20 @@ const roleIriToTranslationKey: Record = { [AttributionRoleIris.Read]: 'general.permissions.read', }; - export default class InstitutionalObjectListContributorsField extends Component { @service intl!: Intl; - // Return two contributors affiliated with the institution given with highest permission levels get topInstitutionAffiliatedContributors() { const { searchResult, institution } = this.args; - const {resourceMetadata} = searchResult; + const { resourceMetadata } = searchResult; + const attributions: any[] = getOsfmapObjects(resourceMetadata, ['qualifiedAttribution']); const contributors = getOsfmapObjects(resourceMetadata, ['creator']); const institutionIris = institution.iris; - const affiliatedAttributions = attributions - .filter((attribution: any) => hasInstitutionAffiliation(contributors, attribution, institutionIris)); + const affiliatedAttributions = attributions.filter((attribution: any) => + hasInstitutionAffiliation(contributors, attribution, institutionIris)); + const adminAttributions = affiliatedAttributions.filter( attribution => hasOsfmapValue(attribution, ['hadRole'], AttributionRoleIris.Admin), ); @@ -47,32 +49,40 @@ 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[]) { const attributedContributor = getContributorById(contributors, getSingleOsfmapValue(attribution, ['agent'])); - if (!attributedContributor.affiliation) { + if (!attributedContributor?.affiliation) { return false; } - return attributedContributor.affiliation.some( - (affiliation: any) => { - if (affiliation.identifier) { - return affiliation.identifier.some( - (affiliationIdentifier: any) => institutionIris.includes(affiliationIdentifier['@value']), - ); - } - return institutionIris.includes(affiliation['@id']); - }, - ); + return attributedContributor.affiliation.some((affiliation: any) => { + if (affiliation.identifier) { + return affiliation.identifier.some((affiliationIdentifier: any) => + institutionIris.includes(affiliationIdentifier['@value'])); + } + return institutionIris.includes(affiliation['@id']); + }); } function getContributorById(contributors: any[], contributorId: string) { diff --git a/app/institutions/dashboard/-components/object-list/contributors-field/styles.scss b/app/institutions/dashboard/-components/object-list/contributors-field/styles.scss new file mode 100644 index 00000000000..da8d8a88a6a --- /dev/null +++ b/app/institutions/dashboard/-components/object-list/contributors-field/styles.scss @@ -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; +} + diff --git a/app/institutions/dashboard/-components/object-list/contributors-field/template.hbs b/app/institutions/dashboard/-components/object-list/contributors-field/template.hbs index 992e6be26a3..e2968eed90a 100644 --- a/app/institutions/dashboard/-components/object-list/contributors-field/template.hbs +++ b/app/institutions/dashboard/-components/object-list/contributors-field/template.hbs @@ -7,6 +7,13 @@ {{t 'institutions.dashboard.object-list.table-items.permission-level' permissionLevel=contributor.permissionLevel}} + {{else}}
{{t 'institutions.dashboard.object-list.table-items.no-contributors'}} diff --git a/app/institutions/dashboard/-components/object-list/styles.scss b/app/institutions/dashboard/-components/object-list/styles.scss index 395963da65c..3a3ebc194c7 100644 --- a/app/institutions/dashboard/-components/object-list/styles.scss +++ b/app/institutions/dashboard/-components/object-list/styles.scss @@ -129,19 +129,6 @@ padding-left: 10px; } -.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; -} - .message-textarea { min-width: 450px; min-height: 280px; @@ -188,7 +175,7 @@ text-align: center; cursor: pointer; background: #f9f9f9; - border: none; + border: 0; border-bottom: 2px solid transparent; &.active { diff --git a/app/institutions/dashboard/-components/object-list/template.hbs b/app/institutions/dashboard/-components/object-list/template.hbs index 60cc9fd994e..e000211e640 100644 --- a/app/institutions/dashboard/-components/object-list/template.hbs +++ b/app/institutions/dashboard/-components/object-list/template.hbs @@ -164,14 +164,8 @@ as |list|> - {{else}} {{call (fn column.getValue result)}} {{/if}} @@ -269,62 +263,59 @@ as |list|> {{t 'institutions.dashboard.object-list.request-project-message-modal.title'}} -
- - +
- {{#if (eq this.activeTab "request-access")}} -
+ {{#if (eq this.activeTab 'request-access')}} +

{{t 'institutions.dashboard.object-list.request-project-message-modal.request_label'}}