Skip to content
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

[ENG-4964] Addon cards #2092

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
2 changes: 1 addition & 1 deletion app/adapters/addon-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import JSONAPIAdapter from '@ember-data/adapter/json-api';
import config from 'ember-osf-web/config/environment';

const addonServiceUrl = config.OSF.url;
const { addonServiceUrl } = config.OSF;

export const addonServiceNamespace = 'v1';
export const addonServiceAPIUrl = `${addonServiceUrl}${addonServiceNamespace}/`;
Expand Down
3 changes: 0 additions & 3 deletions app/adapters/external-storage-service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import AddonServiceAdapter from './addon-service';

export default class ExternalStorageServiceAdapter extends AddonServiceAdapter {
pathForType() {
return 'storage_providers';
}
}

declare module 'ember-data/types/registries/adapter' {
Expand Down
7 changes: 7 additions & 0 deletions app/guid-node/addons/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import Store from '@ember-data/store';

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

async model() {
return await this.modelFor('guid-node').taskInstance;
}
}
4 changes: 4 additions & 0 deletions app/guid-node/addons/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.addon-cards-wrapper {
display: flex;
flex-wrap: wrap;
}
11 changes: 10 additions & 1 deletion app/guid-node/addons/template.hbs
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
{{!-- List all providers to choose from here --}}
<AddonsService::Manager
@node={{this.model}}
as |manager|
>
<div local-class='addon-cards-wrapper'>
{{#each manager.addonProviders as |addon|}}
<AddonCard @addon={{addon}} @manager={{manager}} />
{{/each}}
</div>
</AddonsService::Manager>
58 changes: 58 additions & 0 deletions lib/osf-components/addon/components/addon-card/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import LegacyProvider from 'ember-osf-web/packages/addons-service/legacy-provider';
import Provider from 'ember-osf-web/packages/addons-service/provider';
import AddonsServiceManagerComponent from 'osf-components/components/addons-service/manager/component';

interface Args {
addon: LegacyProvider | Provider;
manager: AddonsServiceManagerComponent;
}

const addonLogoMap: Record<string, string> = {
aws: '/assets/images/addons/logos/aws.png',
bitbucket: '/assets/images/addons/logos/bitbucket.png',
boa: '/assets/images/addons/logos/boa_color.png',
box: '/assets/images/addons/logos/box.png',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this one will be coming from the services API, we shouldn't need to store this one. That being said, I have no idea how we'd mirage that, so it may just not show properly when we're running locally though mirage.

dataverse: '/assets/images/addons/logos/dataverse.png',
dropbox: '/assets/images/addons/logos/dropbox.png',
figshare: '/assets/images/addons/logos/figshare.png',
github: '/assets/images/addons/logos/github.png',
gitlab: '/assets/images/addons/logos/gitlab.png',
googledrive: '/assets/images/addons/logos/google.png',
mendeley: '/assets/images/addons/logos/mendeley.png',
onedrive: '/assets/images/addons/logos/onedrive.png',
owncloud: '/assets/images/addons/logos/owncloud.png',
zotero: '/assets/images/addons/logos/zotero.png',
};

export default class AddonsCardComponent extends Component<Args> {
@tracked deleteModalOpen = false;

@action
toggleDeleteModal() {
this.deleteModalOpen = !this.deleteModalOpen;
}

@action
closeDeleteModal() {
this.deleteModalOpen = false;
}

@action
disableAddon() {
const { addon } = this.args;
addon.disableProjectAddon();
}

get assetLogo() {
return addonLogoMap[this.args.addon.provider.id];
}

get addonIsConfigured() {
const { addon, manager } = this.args;
return manager.projectEnabledAddons.includes(addon);
}
}
47 changes: 47 additions & 0 deletions lib/osf-components/addon/components/addon-card/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.card-wrapper {
min-width: 208px;
border: solid 1px $color-border-gray;
margin: 5px 3px;
padding: 20px;
text-align: center;
}

.addon-card-logo {
height: 36px;
}

.provider-name {
font-weight: bold;
margin: 15px 0;
}

.button-looking-links {
composes: Button from '../button/styles.scss';
composes: MediumButton from '../button/styles.scss';
composes: SecondaryButton from '../button/styles.scss';
}

.buttons-wrapper {
display: flex;
justify-content: center;

.enable {
color: darken($brand-success, 10%);
}

.configure {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}

.disable {
color: darken($brand-danger, 10%);
border-left: 0;
border-bottom-left-radius: 0;
border-top-left-radius: 0;

&:hover {
text-decoration: underline;
}
}
}
85 changes: 85 additions & 0 deletions lib/osf-components/addon/components/addon-card/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<div
local-class='card-wrapper'
data-test-addon-card={{@addon.provider.name}}
data-analytics-scope={{concat 'Addon card' @addon.provider.name}}
>
<img
data-test-addon-card-logo
src={{this.assetLogo}}
alt={{t 'osf-components.addon-card.provider-logo-alt' [email protected]}}
local-class='addon-card-logo'
/>
<div
data-test-addon-card-title
local-class='provider-name'
>
{{@addon.provider.name}}
</div>

<div local-class='buttons-wrapper'>
{{#if this.addonIsConfigured}}
<OsfLink
data-test-addon-card-configure
data-analytics-name='Configure'
local-class='button-looking-links configure'
@route='guid-node.addons.addon.configure'
@models={{array @manager.node.id @addon.provider.id}}
>
{{t 'osf-components.addon-card.configure'}}
</OsfLink>
<Button
data-test-addon-card-disable
data-analytics-name='Disable'
local-class='disable'
{{on 'click' this.toggleDeleteModal}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing another use of toggle, and I don't think this is clickable while the dialog is open. Would it make more sense to just mut the property to true here rather than having the function? This is super-minor.

>
{{t 'osf-components.addon-card.disable'}}
</Button>
{{else}}
<OsfLink
data-test-addon-card-enable
data-analytics-name='Enable'
local-class='button-looking-links enable'
@route='guid-node.addons.addon.terms'
@models={{array @manager.node.id @addon.provider.id}}
>
{{t 'osf-components.addon-card.enable'}}
</OsfLink>
{{/if}}
</div>
</div>
{{#if this.addonIsConfigured}}
<OsfDialog
@isOpen={{this.deleteModalOpen}}
@onClose={{action this.closeDeleteModal}}
as |dialog|
>
<dialog.heading>
{{t 'osf-components.addon-card.disable-modal-header'}}
</dialog.heading>
<dialog.main>
<p>
{{t 'osf-components.addon-card.disable-modal-body'}}
</p>
</dialog.main>
<dialog.footer data-analytics-scope='Verify email modal'>
<Button
data-test-addon-disable-cancel
data-analytics-name='Cancel'
@type='secondary'
{{on 'click' dialog.close}}
>
{{t 'general.cancel'}}
</Button>
<Button
data-test-addon-disable-modal-disable
data-analytics-name='Disable'
@type='destroy'
{{on 'click' (queue this.disableAddon dialog.close)}}
>
{{t 'osf-components.addon-card.disable'}}
</Button>
</dialog.footer>
</OsfDialog>
{{/if}}

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { inject as service } from '@ember/service';
import { waitFor } from '@ember/test-waiters';
import Store from '@ember-data/store';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { task } from 'ember-concurrency';
import { taskFor } from 'ember-concurrency-ts';

import NodeModel from 'ember-osf-web/models/node';
import LegacyProvider from 'ember-osf-web/packages/addons-service/legacy-provider';
Expand All @@ -19,31 +21,49 @@ export default class AddonsServiceManagerComponent extends Component<Args> {

node = this.args.node;

@tracked addonProviders: Array<LegacyProvider | Provider> = [];

constructor(owner: unknown, args: Args) {
super(owner, args);
taskFor(this.getAddonProviders).perform();
}

@task
@waitFor
async addonProviders(): Promise<(Array<LegacyProvider | Provider>)> {
const legacyProviders: LegacyProvider[] = await this.legacyProviders();
async getAddonProviders() {
const legacyProviders: LegacyProvider[] = await taskFor(this.legacyProviders).perform();
const serviceStorageProviders: Provider[] = this.serviceStorageProviders();

return [...legacyProviders, ...serviceStorageProviders]
const providers = [...legacyProviders, ...serviceStorageProviders]
.sort(this.providerSorter);
this.addonProviders = providers;
}

providerSorter(a: Provider, b: Provider) {
return a.provider.name.localeCompare(b.provider.name);
}

get projectEnabledAddons(): Array<LegacyProvider | Provider> {
const legacyAddons = this.legacyProjectEnabledAddons();
const serviceAddons = this.serviceProjectEnabledAddons();
return [...legacyAddons, ...serviceAddons];
}

// V2 API Methods

@task
@waitFor
async legacyProviders() {
const addons = await this.store.findAll('addon');
const addons = (await this.store.findAll('addon')).toArray();
const legacyAddons = [] as LegacyProvider[];
for (const addon of addons) {
legacyAddons.addObject(new LegacyProvider(addon, this.currentUser, this.node));
}
return addons;
return legacyAddons;
}

get isLoading() {
return taskFor(this.getAddonProviders).isRunning;
}

legacyProjectEnabledAddons() {
Expand All @@ -58,6 +78,6 @@ export default class AddonsServiceManagerComponent extends Component<Args> {
}

serviceProjectEnabledAddons() {
return;
return [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{yield (hash
node=this.node
addonProviders=this.addonProviders
loading=this.loading
projectEnabledAddons=this.projectEnabledAddons
)}}
1 change: 1 addition & 0 deletions lib/osf-components/app/components/addon-card/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'osf-components/components/addon-card/component';
1 change: 1 addition & 0 deletions lib/osf-components/app/components/addon-card/template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'osf-components/components/addon-card/template';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'osf-components/components/addons-service/manager/template';
12 changes: 6 additions & 6 deletions mirage/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,12 +427,12 @@ export default function(this: Server) {
// Addon service
this.urlPrefix = addonServiceUrl;
this.namespace = addonServiceNamespace;
this.resource('external_storage_services', { only: ['index', 'show'] });
this.resource('internal_users', { only: ['show'] });
this.get('/internal_users/:userGuid/authorized_storage_accounts/', addons.internalUserAuthorizedStorageAccountList);
this.resource('internal_resources', { only: ['show'] });
this.resource('authorized_storage_accounts', { only: ['show', 'update'] });
this.resource('configured_storage_addons', { only: ['show', 'update'] });
this.resource('external-storage-services', { only: ['index', 'show'] });
this.resource('internal-users', { only: ['show'] });
this.get('/internal-users/:userGuid/authorized-storage-accounts/', addons.internalUserAuthorizedStorageAccountList);
this.resource('internal-resources', { only: ['show'] });
this.resource('authorized-storage-accounts', { only: ['show', 'update'] });
this.resource('configured-storage-addons', { only: ['show', 'update'] });

// Reset API url and namespace to use v2 endpoints for tests
this.urlPrefix = apiUrl;
Expand Down
Binary file added public/assets/images/addons/logos/aws.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/bitbucket.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/boa_color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/dataverse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/dropbox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/figshare.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/github.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/gitlab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/google.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/mendeley.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/onedrive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/owncloud.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/images/addons/logos/zotero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading