Skip to content

[Reference PR] VRL - Ember FE Part #2553

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
017148d
[ENG-7739] Add query params for routing (#2539)
adlius Apr 17, 2025
a684240
add VRL models (#2549)
adlius Apr 24, 2025
2a1e19a
Merge remote-tracking branch 'upstream/develop' into feature/verified…
cslzchen Apr 29, 2025
a28878b
fixed supportedResourceType enum to be consistent with Datacite's res…
opaduchak May 5, 2025
99a6b14
[ENG-7576] Linked service settings page (#2560)
adlius May 13, 2025
aa75b42
add leftnav link for linked services (#2561)
adlius May 16, 2025
2b183bc
Merge remote-tracking branch 'upstream/develop' into feature/verified…
cslzchen May 20, 2025
5b2bbcd
[ENG-7784][ENG-8043] Linked services project page + bug fixes (#2564)
adlius May 28, 2025
e050955
add tabIndex queryparam (#2565)
adlius May 28, 2025
7a499e8
[ENG-8078] Show a dedicated empty linked services page when no LINK a…
opaduchak Jun 13, 2025
4284f8b
[ENG-8080] More FE update due to recent mock-up changes from PO (#2575)
opaduchak Jun 13, 2025
6d48aac
[ENG-8042] Update TOS page for links (#2579)
adlius Jun 16, 2025
e37a224
small fix for language (#2581)
adlius Jun 17, 2025
2728c66
[ENG-8234] FE: Incorrect error message shown on Linked Services page …
opaduchak Jun 18, 2025
cae502b
[ENG-8245] Open verified links in new tab
cslzchen Jun 18, 2025
6975e95
[ENG-8243] Update language for add-ons on the user settings page - Em…
cslzchen Jun 18, 2025
b1d359d
[ENG-8242] FE: Fix the issue that display name cannot be changed for …
opaduchak Jun 19, 2025
87bfb44
[ENG-8254] Fix reconnect for linked services in user settings (#2590)
cslzchen Jun 19, 2025
f617801
[ENG-8236] Fix missing resource-type dropdown during linked service c…
cslzchen Jun 19, 2025
aea44b7
[ENG-8235] Fix "Linked Item" during addon reconfiguration (#2586)
adlius Jun 20, 2025
a6a0902
[ENG-8266] Fix "linked item" for linked service configuration (#2591)
cslzchen Jun 24, 2025
7d1c063
[ENG-8255][ENG-8244] Show item name when deleting linked service; fix…
adlius Jun 24, 2025
eefee2b
[ENG-8233] Fix "unresponsive" "Link" button when the connected Datave…
opaduchak Jun 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/adapters/authorized-link-account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import AddonServiceAdapter from './addon-service';

export default class AuthorizedLinkAccountAdapter extends AddonServiceAdapter {
}

declare module 'ember-data/types/registries/adapter' {
export default interface AdapterRegistry {
'authorized-link-account': AuthorizedLinkAccountAdapter;
} // eslint-disable-line semi
}
10 changes: 10 additions & 0 deletions app/adapters/configured-link-addon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import AddonServiceAdapter from './addon-service';

export default class ConfiguredLinkAddonAdapter extends AddonServiceAdapter {
}

declare module 'ember-data/types/registries/adapter' {
export default interface AdapterRegistry {
'configured-link-addon': ConfiguredLinkAddonAdapter;
} // eslint-disable-line semi
}
10 changes: 10 additions & 0 deletions app/adapters/external-link-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import AddonServiceAdapter from './addon-service';

export default class ExternalLinkServiceAdapter extends AddonServiceAdapter {
}

declare module 'ember-data/types/registries/adapter' {
export default interface AdapterRegistry {
'external-link-service': ExternalLinkServiceAdapter;
} // eslint-disable-line semi
}
11 changes: 11 additions & 0 deletions app/guid-node/addons/index/controller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import Media from 'ember-responsive';
import { tracked } from 'tracked-built-ins';

enum FilterTypes {
STORAGE = 'additional-storage',
CITATION_MANAGER = 'citation-manager',
VERIFIED_LINK = 'verified-link',
// CLOUD_COMPUTING = 'cloud-computing', // disabled because BOA is down
}
export default class GuidNodeAddonsController extends Controller {
@service media!: Media;
@tracked tabIndex = 0;
@tracked activeFilterType = FilterTypes.STORAGE;

queryParams = ['tabIndex', 'activeFilterType'];

get isMobile() {
return this.media.isMobile;
Expand Down
27 changes: 20 additions & 7 deletions app/guid-node/addons/index/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<div local-class='addon-page-wrapper'>
<AddonsService::Manager
@node={{this.model}}
@activeFilterType={{this.activeFilterType}}
@updateActiveFilterType={{fn (mut this.activeFilterType)}}
@updateTabIndex={{fn (mut this.tabIndex)}}
as |manager|
>
{{#if manager.selectedProvider}}
Expand Down Expand Up @@ -166,7 +169,7 @@
<div
data-test-configured-addon-name
local-class='configured-addon-display-name'
{{did-insert (perform configuredAddon.getRootFolderName)}}
{{did-insert (perform configuredAddon.getSelectedItemName)}}
>
<span>{{configuredAddon.displayName}}</span>
<span local-class='float-right'>
Expand Down Expand Up @@ -240,10 +243,18 @@
{{manager.selectedConfiguration.displayName}}
</div>
<div>
{{#if manager.selectedConfiguration.rootFolder}}
{{manager.selectedConfiguration.rootFolderName}}
{{#if (instance-of manager.selectedConfiguration 'ConfiguredLinkAddon')}}
{{#if manager.selectedConfiguration.targetUrl}}
{{manager.selectedConfiguration.targetItemName}}
{{else}}
{{t 'addons.list.target-not-set'}}
{{/if}}
{{else}}
{{t 'addons.list.root-folder-not-set'}}
{{#if manager.selectedConfiguration.rootFolder}}
{{manager.selectedConfiguration.rootFolderName}}
{{else}}
{{t 'addons.list.root-folder-not-set'}}
{{/if}}
{{/if}}
</div>
<div>
Expand Down Expand Up @@ -274,6 +285,7 @@
<AddonsService::ConfiguredAddonEdit
@configuredAddon={{manager.selectedConfiguration}}
@authorizedAccount={{manager.selectedAccount}}
@supportedResourceTypes={{manager.selectedProvider.provider.supportedResourceTypes}}
@onSave={{perform manager.saveOrCreateConfiguration}}
@onCancel={{manager.cancelSetup}}
/>
Expand All @@ -282,7 +294,8 @@
{{/let}}
{{else}}
<AriaTabs
@defaultIndex={{0}}
@selectedIndex={{this.tabIndex}}
@onSelect={{action (mut this.tabIndex)}}
local-class='tabs'
as |tab|
>
Expand Down Expand Up @@ -324,7 +337,7 @@
<Button
data-test-addon-list-filter={{type}}
data-analytics-name={{t (concat 'addons.list.filter.' type)}}
local-class='filter-button {{if (eq manager.activeFilterType type) 'active'}}'
local-class='filter-button {{if (eq this.activeFilterType type) 'active'}}'
@layout='fake-link'
{{on 'click' (fn manager.filterByAddonType type)}}
>
Expand Down Expand Up @@ -364,7 +377,7 @@
<Button
data-test-addon-list-filter={{type}}
data-analytics-name={{t (concat 'addons.list.filter.' type)}}
local-class='filter-button {{if (eq manager.activeFilterType type) 'active'}}'
local-class='filter-button {{if (eq this.activeFilterType type) 'active'}}'
@layout='fake-link'
{{on 'click' (fn manager.filterByAddonType type)}}
>
Expand Down
8 changes: 8 additions & 0 deletions app/guid-node/links/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Controller from '@ember/controller';
import { Permission } from 'ember-osf-web/models/osf-model';

export default class GuidNodeLinksController extends Controller {
get currentUserCanEdit() {
return this.model.node.currentUserPermissions.includes(Permission.Write);
}
}
17 changes: 17 additions & 0 deletions app/guid-node/links/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Route from '@ember/routing/route';
import Store from '@ember-data/store';
import { inject as service } from '@ember/service';

export default class GuidNodeLinks extends Route {
@service store!: Store;

async model() {
const node = await this.modelFor('guid-node').taskInstance;
const resourceReferences = await this.store.query('resource-reference', {
filter: {resource_uri: node.links.iri?.toString()},
});
const resourceReference = resourceReferences?.firstObject;
const configuredLinkAddons = await resourceReference?.configuredLinkAddons;
return await {node, configuredLinkAddons};
}
}
33 changes: 33 additions & 0 deletions app/guid-node/links/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.table {
width: 100%;
text-align: left;
border-collapse: collapse;
}

.table-header {
padding-bottom: 10px;
padding-top: 10px;
}

.edit-header {
min-width: 30px;
}

.table-row {
border-bottom: 1px solid $color-border-gray;
}

.table-cell {
padding-bottom: 10px;
padding-top: 10px;
}

.logo {
max-width: 50px;
max-height: 50px;
}

.links-page-wrapper {
margin: 20px;
}

47 changes: 47 additions & 0 deletions app/guid-node/links/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<div local-class='links-page-wrapper'>
<h2>{{t 'links.linked-services'}}</h2>
{{#if this.model.configuredLinkAddons}}
<table local-class='table'>
<thead>
<tr local-class='table-row'>
<th local-class='table-header'>{{t 'links.linked-service'}}</th>
<th local-class='table-header'>{{t 'links.display-name'}}</th>
<th local-class='table-header'>{{t 'links.resource-type'}}</th>
<th local-class='table-header edit-header'>
{{#if this.currentUserCanEdit}}
<OsfLink
@route='guid-node.addons'
@queryParams={{hash
activeFilterType='verified-link'
tabIndex='1'
}}
@models={{array this.model.node.id}}
>
{{t 'links.edit'}}
</OsfLink>
{{/if}}
</th>
</tr>
</thead>
<tbody>
{{#each this.model.configuredLinkAddons as |configuredLinkAddon|}}
<tr local-class='table-row'>
<td local-class='table-cell'>
<img alt={{t 'links.logo'}} local-class='logo'
src={{configuredLinkAddon.externalLinkService.iconUrl}}>
<span>{{configuredLinkAddon.externalLinkService.displayName}}</span>
</td>
<td local-class='table-cell'>{{configuredLinkAddon.displayName}}</td>
<td local-class='table-cell'>{{configuredLinkAddon.resourceType}}</td>
<td local-class='table-cell'><a target='_blank' rel='noopener noreferrer' href={{configuredLinkAddon.targetUrl}}>{{t 'links.link'}}</a></td>
</tr>
{{/each}}
</tbody>
</table>
{{else}}
<p>{{t 'links.empty-screen-message'}}</p>
{{#if this.currentUserCanEdit}}
<p> {{t 'links.point-to-addons-message'}} <a href='/{{this.model.node.id}}/addons?activeFilterType=verified-link'>{{t 'addons.heading'}}</a></p>
{{/if}}
{{/if}}
</div>
9 changes: 9 additions & 0 deletions app/guid-node/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@
/>
{{/if}}
{{/if}}
{{/unless}}
<leftNav.link
data-test-verified-links-link
data-analytics-name='Linked services'
@route='guid-node.links'
@icon='link'
@label={{t 'node.left_nav.links'}}
/>
{{#unless this.currentUser.viewOnlyToken}}
{{#if (and (not this.viewOnly) this.model.taskInstance.value.userHasReadPermission)}}
<leftNav.link
data-test-settings-link
Expand Down
20 changes: 20 additions & 0 deletions app/helpers/instance-of.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getOwner } from '@ember/application';
import { assert } from '@ember/debug';

import Helper from '@ember/component/helper';

export default class InstanceOf extends Helper {
compute([object, className]: [any, string]) {
if (!object || typeof className !== 'string') {
return false;
}
// Look up the class from the container
const owner = getOwner(this);
const klass = owner.factoryFor(`model:${className}`)?.class;
if (!klass) {
assert(`Class "${className}" not found`);
return false;
}
return object instanceof klass;
}
}
6 changes: 6 additions & 0 deletions app/models/addon-operation-invocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export enum ConnectedCitationOperationNames {
GetItemInfo = 'get_item_info',
}

export enum ConnectedLinkOperationNames {
ListRootItems = 'list_root_items',
ListChildItems = 'list_child_items',
GetItemInfo = 'get_item_info',
}

export interface OperationKwargs {
itemId?: string;
itemType?: ItemType;
Expand Down
47 changes: 47 additions & 0 deletions app/models/authorized-link-account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { AsyncBelongsTo, belongsTo } from '@ember-data/model';
import { waitFor } from '@ember/test-waiters';
import { task } from 'ember-concurrency';
import { ConnectedLinkOperationNames, OperationKwargs } from 'ember-osf-web/models/addon-operation-invocation';

import ExternalLinkServiceModel from 'ember-osf-web/models/external-link-service';
import AuthorizedAccountModel from './authorized-account';
import UserReferenceModel from './user-reference';

export default class AuthorizedLinkAccountModel extends AuthorizedAccountModel {
@belongsTo('user-reference', { inverse: 'authorizedLinkAccounts' })
readonly accountOwner!: AsyncBelongsTo<UserReferenceModel> & UserReferenceModel;

@belongsTo('external-link-service')
externalLinkService!: AsyncBelongsTo<ExternalLinkServiceModel> & ExternalLinkServiceModel;

@task
@waitFor
async getFolderItems(this: AuthorizedAccountModel, kwargs?: OperationKwargs) {
const operationKwargs = kwargs || {};
const operationName = operationKwargs.itemId ? ConnectedLinkOperationNames.ListChildItems :
ConnectedLinkOperationNames.ListRootItems;
const newInvocation = this.store.createRecord('addon-operation-invocation', {
operationName,
operationKwargs,
thruAccount: this,
});
return await newInvocation.save();
}

@task
@waitFor
async getItemInfo(this: AuthorizedAccountModel, itemId: string) {
const newInvocation = this.store.createRecord('addon-operation-invocation', {
operationName: ConnectedLinkOperationNames.GetItemInfo,
operationKwargs: { itemId },
thruAccount: this,
});
return await newInvocation.save();
}
}

declare module 'ember-data/types/registries/model' {
export default interface ModelRegistry {
'authorized-link-account': AuthorizedLinkAccountModel;
} // eslint-disable-line semi
}
15 changes: 5 additions & 10 deletions app/models/configured-addon.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import Model, { AsyncBelongsTo, attr, belongsTo } from '@ember-data/model';
import { waitFor } from '@ember/test-waiters';
import { task } from 'ember-concurrency';

import UserReferenceModel from 'ember-osf-web/models/user-reference';
import { tracked } from 'tracked-built-ins';
import { taskFor } from 'ember-concurrency-ts';
import { SupportedResourceTypes } from 'ember-osf-web/models/external-link-service';
// import ConfiguredLinkAddonModel from 'ember-osf-web/models/configured-link-addon';
import { ConnectedStorageOperationNames, OperationKwargs } from './addon-operation-invocation';
import { ConnectedCapabilities } from './authorized-account';


export interface ConfiguredAddonEditableAttrs {
displayName: string;
rootFolder: string;
targetId: string;
resourceType: SupportedResourceTypes;
}

export default class ConfiguredAddonModel extends Model {
Expand Down Expand Up @@ -50,11 +51,5 @@ export default class ConfiguredAddonModel extends Model {
}

@tracked rootFolderName = '';

@task
@waitFor
async getRootFolderName(this: ConfiguredAddonModel) {
const response = await taskFor(this.getItemInfo).perform(this.rootFolder);
this.rootFolderName = response.operationResult.itemName;
}
@tracked targetItemName = '';
}
12 changes: 11 additions & 1 deletion app/models/configured-citation-addon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { AsyncBelongsTo, belongsTo } from '@ember-data/model';
import ResourceReferenceModel from 'ember-osf-web/models/resource-reference';
import { task } from 'ember-concurrency';
import { waitFor } from '@ember/test-waiters';
import { ConnectedCitationOperationNames, OperationKwargs } from 'ember-osf-web/models/addon-operation-invocation';
import {
ConnectedCitationOperationNames, Item, OperationKwargs,
} from 'ember-osf-web/models/addon-operation-invocation';
import { taskFor } from 'ember-concurrency-ts';
import AuthorizedCitationAccountModel from './authorized-citation-account';
import ExternalCitationServiceModel from './external-citation-service';
import ConfiguredAddonModel from './configured-addon';
Expand Down Expand Up @@ -48,6 +51,13 @@ export default class ConfiguredCitationAddonModel extends ConfiguredAddonModel {
});
return await newInvocation.save();
}

@task
@waitFor
async getSelectedItemName(this: ConfiguredCitationAddonModel) {
const response = await taskFor(this.getItemInfo).perform(this.rootFolder);
this.rootFolderName = (response.operationResult as Item).itemName;
}
}

declare module 'ember-data/types/registries/model' {
Expand Down
Loading