diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b47311619b..0ab29e09fb 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -115,6 +115,7 @@ import { ExporterStepComponent } from './workflow/snapshot-exporter-modal/export import { SnaphotExporterModalComponent } from './workflow/snapshot-exporter-modal/snaphot-exporter-modal.component'; import { ViewService } from './workflow/view/view.service'; import { NgxMatSelectSearchModule } from 'ngx-mat-select-search'; +import { MyOrganizationsDialogComponent } from './home-page/widget/organization-box/my-organizations-dialog.component/my-organizations-dialog.component'; export const myCustomTooltipDefaults: MatTooltipDefaultOptions = { showDelay: 500, @@ -171,6 +172,7 @@ export function initializerFactory( FileTreeComponent, ChangeUsernameBannerComponent, RevokeTokenDialogComponent, + MyOrganizationsDialogComponent, ], imports: [ environment.production ? [] : AkitaNgDevtools.forRoot(), diff --git a/src/app/home-page/home-logged-in/home-logged-in.component.html b/src/app/home-page/home-logged-in/home-logged-in.component.html index 37d146535b..adecc3c985 100644 --- a/src/app/home-page/home-logged-in/home-logged-in.component.html +++ b/src/app/home-page/home-logged-in/home-logged-in.component.html @@ -47,12 +47,6 @@

Explore Featured Content

-
-

Recent Activity

-
- -
-
-
- - info No events found. Star entries or organizations to see events for them. Read - Starring Tools and Workflows - to learn how to star entries. - -
- - -
-
-
-
- {{ event.initiatorUser?.username }} created the {{ event.version?.referenceType | lowercase }} - {{ event.version?.name }} in {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ - (event?.tool | entryToDisplayName) || (event?.workflow | entryToDisplayName) || (event?.apptool | entryToDisplayName) - }} +
+
+ + User avatar + +
+ +
+ {{ + event.initiatorUser?.username + }} + published the {{ event | recentEvents: 'entryType' }} + {{ event | recentEvents: 'displayName' }} +
+
+ + {{ + event.initiatorUser?.username + }} -
{{ 'on ' + (event.dbCreateDate | date: 'medium') }}
- + unpublished the {{ event | recentEvents: 'entryType' }} {{ event | recentEvents: 'displayName' }}
-
- {{ event.initiatorUser?.username }} created the collection - {{ event.collection?.name }} - +
+ {{ + event.initiatorUser?.username + }} + created the {{ event.version?.referenceType | lowercase }} {{ event.version?.name }} in + {{ event | recentEvents: 'entryType' }} + {{ event | recentEvents: 'displayName' }} +
+ + +
+ The organization + {{ event.organization?.displayName }} + was approved. +
+
+ The organization + {{ event.organization?.displayName }} + was rejected. +
+
+ The organization + {{ event.organization?.displayName }} + was re-requested for review. +
+
+ {{ + event.initiatorUser?.username + }} + added {{ event.user.username }} to the organization + {{ event.organization?.displayName }} +
+ + + +
+ {{ + event.initiatorUser?.username + }} + created the collection + {{ event.collection?.name }} in organization - {{ event.organization?.displayName }} -
{{ 'on ' + (event.dbCreateDate | date: 'medium') }}
- + {{ event.organization?.displayName }} +
+
+ + {{ + event.initiatorUser?.username + }} + removed the collection {{ event.collection?.name }} in organization + {{ event.organization?.displayName }}
-
- {{ event.initiatorUser.username }} added the {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ - (event?.tool | entryToDisplayName) || (event?.workflow | entryToDisplayName) || (event?.apptool | entryToDisplayName) - }} + {{ + event.initiatorUser?.username + }} + added the {{ event | recentEvents: 'entryType' }} + {{ event | recentEvents: 'displayName' }} to the collection - {{ event.collection?.name }} - + {{ event.collection?.name }} in organization - {{ event.organization?.displayName }} -
{{ 'on ' + (event.dbCreateDate | date: 'medium') }}
- + {{ event.organization?.displayName }}
-
- {{ event.user.username }} published the {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ - (event?.tool | entryToDisplayName) || (event?.workflow | entryToDisplayName) || (event?.apptool | entryToDisplayName) - }} + {{ + event.initiatorUser?.username + }} -
{{ 'on ' + (event.dbCreateDate | date: 'medium') }}
- + edited the collection + {{ event.collection?.name }}
-
- {{ event.user.username }} unpublished the {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ - (event?.tool | entryToDisplayName) || (event?.workflow | entryToDisplayName) || (event?.apptool | entryToDisplayName) - }} + {{ + event.initiatorUser?.username + }} -
{{ 'on ' + (event.dbCreateDate | date: 'medium') }}
- + removed the {{ event | recentEvents: 'entryType' }} + {{ event | recentEvents: 'displayName' }} + from the collection + {{ event.collection?.name }}
+
{{ event.dbCreateDate | date: 'medium' }}
+
+ + {{ event | recentEvents: 'entryType' }} icon + + + + {{ event | recentEvents: 'entryType' }} icon + +
+ + + Org avatar + + + + Org icon + + +
- +
+ + + info No events found. Star entries or organizations to see events for them. Read + Starring Tools and Workflows + to learn how to star entries. + + diff --git a/src/app/home-page/recent-events/recent-events.component.scss b/src/app/home-page/recent-events/recent-events.component.scss deleted file mode 100644 index d9768e022d..0000000000 --- a/src/app/home-page/recent-events/recent-events.component.scss +++ /dev/null @@ -1,5 +0,0 @@ -// The padding between events is 1.6 rem, this puts it in the middle -.custom-margin { - margin-top: 0.8rem; - margin-bottom: 0.8rem; -} diff --git a/src/app/home-page/recent-events/recent-events.component.ts b/src/app/home-page/recent-events/recent-events.component.ts index 6e1869d012..e99820d796 100644 --- a/src/app/home-page/recent-events/recent-events.component.ts +++ b/src/app/home-page/recent-events/recent-events.component.ts @@ -1,54 +1,99 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ID } from '@datorama/akita'; import { Dockstore } from 'app/shared/dockstore.model'; -import { Event, User } from 'app/shared/openapi'; -import { UsersService } from 'app/shared/swagger'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { Event, User, UsersService, EventsService } from 'app/shared/openapi'; import { HttpErrorResponse } from '@angular/common/http'; import { RecentEventsQuery } from '../state/recent-events.query'; import { RecentEventsService } from '../state/recent-events.service'; import { AlertService } from 'app/shared/alert/state/alert.service'; +import { EntryType } from '../../shared/enum/entry-type'; +import { finalize, takeUntil } from 'rxjs/operators'; +import { Base } from 'app/shared/base'; +import { Observable } from 'rxjs'; +import { OrgLogoService } from '../../shared/org-logo.service'; /** - * Shows recent events related to starred organization and entries + * Shows recent events related to starred organization and entries or user organization events + * If no eventType input, will default to displaying events for the user being viewed (user page) + * * Only shows when a child has been added to parent (version added to entry, collection added to organization, entry added to collection) * TODO: collapse events with the same date (refresh all adding multiple versions at the same time) */ @Component({ selector: 'app-recent-events', templateUrl: './recent-events.component.html', - styleUrls: ['./recent-events.component.scss'], + styleUrls: ['../../shared/styles/dashboard-boxes.scss'], }) -export class RecentEventsComponent implements OnInit { - events$: Observable; - loading$: Observable; - EventType = Event.TypeEnum; - noEvents$: Observable; - user: User; - username: string; - readonly starringDocUrl = `${Dockstore.DOCUMENTATION_URL}/end-user-topics/starring.html#starring-tools-and-workflows`; - homepage = true; - readonly supportedEventTypes = [ +export class RecentEventsComponent extends Base implements OnInit { + @Input() eventType: 'SELF_ORGANIZATIONS' | 'ALL_STARRED'; + // For eventsService loading + public isLoading: boolean; + // For recentEventsQuery loading + public loading$: Observable; + public events: Event[]; + public EventType = Event.TypeEnum; + public EntryType = EntryType; + public readonly starringDocUrl = `${Dockstore.DOCUMENTATION_URL}/end-user-topics/starring.html#starring-tools-and-workflows`; + public displayLimit: number; + private userPageDisplayLimit = 10; + private dashboardDisplayLimit = 4; + private username: string; + private supportedEventTypes: Event.TypeEnum[]; + private readonly supportedUserEventTypes = [ Event.TypeEnum.ADDVERSIONTOENTRY, Event.TypeEnum.CREATECOLLECTION, Event.TypeEnum.ADDTOCOLLECTION, + Event.TypeEnum.REMOVEFROMCOLLECTION, Event.TypeEnum.PUBLISHENTRY, Event.TypeEnum.UNPUBLISHENTRY, + Event.TypeEnum.MODIFYCOLLECTION, + Event.TypeEnum.DELETECOLLECTION, + Event.TypeEnum.CREATEORG, + ]; + private readonly supportedStarredEventTypes = [ + Event.TypeEnum.ADDVERSIONTOENTRY, + Event.TypeEnum.CREATECOLLECTION, + Event.TypeEnum.ADDTOCOLLECTION, + Event.TypeEnum.PUBLISHENTRY, + Event.TypeEnum.UNPUBLISHENTRY, + Event.TypeEnum.MODIFYCOLLECTION, + Event.TypeEnum.ADDUSERTOORG, + Event.TypeEnum.MODIFYORG, + Event.TypeEnum.DELETECOLLECTION, + Event.TypeEnum.REMOVEFROMCOLLECTION, + ]; + private readonly supportedOrgEventTypes = [ + Event.TypeEnum.CREATEORG, + Event.TypeEnum.MODIFYORG, + Event.TypeEnum.APPROVEORG, + Event.TypeEnum.REREQUESTORG, + Event.TypeEnum.REJECTORG, + Event.TypeEnum.ADDUSERTOORG, + Event.TypeEnum.APPROVEORGINVITE, + Event.TypeEnum.CREATECOLLECTION, + Event.TypeEnum.MODIFYCOLLECTION, + Event.TypeEnum.DELETECOLLECTION, + Event.TypeEnum.REMOVEFROMCOLLECTION, + Event.TypeEnum.ADDTOCOLLECTION, ]; + constructor( private recentEventsQuery: RecentEventsQuery, private recentEventsService: RecentEventsService, private activatedRoute: ActivatedRoute, private usersService: UsersService, - private alertService: AlertService + private alertService: AlertService, + private eventsService: EventsService, + private orgLogoService: OrgLogoService ) { + super(); this.username = this.activatedRoute.snapshot.paramMap.get('username'); } ngOnInit() { - if (this.username) { + if (!this.eventType && this.username) { + // On user page, get user, then get user's events this.usersService.listUser(this.username).subscribe( (currentUser: User) => { this.recentEventsService.get(currentUser); @@ -57,15 +102,58 @@ export class RecentEventsComponent implements OnInit { this.alertService.detailedError(error); } ); + this.loading$ = this.recentEventsQuery.selectLoading(); + this.displayLimit = this.userPageDisplayLimit; + this.supportedEventTypes = this.supportedUserEventTypes; + this.getAUsersEvents(); } else { - this.recentEventsService.get(); + // On dashboard, get my events + this.isLoading = true; + this.displayLimit = this.dashboardDisplayLimit; + this.supportedEventTypes = this.eventType === 'SELF_ORGANIZATIONS' ? this.supportedOrgEventTypes : this.supportedStarredEventTypes; + this.getMyEventsByType(); } + } + + /** + * Get another user's events when viewing their user page + */ + private getAUsersEvents() { + this.recentEventsQuery + .selectAll() + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe((events) => { + this.events = this.filterByEventType(events); + }); + } + + /** + * Get my events based on eventType input + */ + private getMyEventsByType() { + this.eventsService + .getEvents(this.eventType) + .pipe( + finalize(() => (this.isLoading = false)), + takeUntil(this.ngUnsubscribe) + ) + .subscribe( + (events) => { + this.events = this.filterByEventType(events); + }, + (error: HttpErrorResponse) => { + this.alertService.detailedError(error); + } + ); + } - this.events$ = this.recentEventsQuery.selectAll({ - filterBy: (entity) => this.supportedEventTypes.includes(entity.type), - }); - this.loading$ = this.recentEventsQuery.selectLoading(); - this.noEvents$ = this.events$.pipe(map((events) => !events || events.length === 0)); + /** + * Filter events by supported event types + * @param events + * @returns Event[] + */ + private filterByEventType(events: Event[]): Event[] { + return events.filter((event) => this.supportedEventTypes.includes(event.type)); } add(recentEvent: Event) { diff --git a/src/app/home-page/recent-events/recent-events.module.ts b/src/app/home-page/recent-events/recent-events.module.ts index a665ecac89..0bca81cb79 100644 --- a/src/app/home-page/recent-events/recent-events.module.ts +++ b/src/app/home-page/recent-events/recent-events.module.ts @@ -8,11 +8,23 @@ import { FlexLayoutModule } from '@angular/flex-layout'; import { MatDividerModule } from '@angular/material/divider'; import { RouterModule } from '@angular/router'; import { MatIconModule } from '@angular/material/icon'; +import { PipeModule } from '../../shared/pipe/pipe.module'; +import { ImgFallbackModule } from '../../shared/modules/img-fallback.module'; @NgModule({ declarations: [RecentEventsComponent, EntryToDisplayNamePipe], providers: [], - imports: [RefreshAlertModule, MatIconModule, CommonModule, MatCardModule, FlexLayoutModule, MatDividerModule, RouterModule], + imports: [ + RefreshAlertModule, + MatIconModule, + CommonModule, + MatCardModule, + FlexLayoutModule, + MatDividerModule, + RouterModule, + PipeModule, + ImgFallbackModule, + ], exports: [RecentEventsComponent], }) export class RecentEventsModule {} diff --git a/src/app/home-page/widget/entry-box/entry-box.component.html b/src/app/home-page/widget/entry-box/entry-box.component.html index 2d5dc03f17..92da92052b 100644 --- a/src/app/home-page/widget/entry-box/entry-box.component.html +++ b/src/app/home-page/widget/entry-box/entry-box.component.html @@ -40,7 +40,7 @@
- +
Search your {{ entryTypeLowerCase | titlecase }}s... diff --git a/src/app/home-page/widget/organization-box/my-organizations-dialog.component/my-organizations-dialog.component.html b/src/app/home-page/widget/organization-box/my-organizations-dialog.component/my-organizations-dialog.component.html new file mode 100644 index 0000000000..733a543431 --- /dev/null +++ b/src/app/home-page/widget/organization-box/my-organizations-dialog.component/my-organizations-dialog.component.html @@ -0,0 +1,15 @@ +
+

My Organizations

+ {{ data.length }} +
+
+
+ + {{ org.displayName }} + +
{{ org.lastUpdateDate | date: 'MMM d, yyyy' }}
+
+
+
+ +
diff --git a/src/app/home-page/widget/organization-box/my-organizations-dialog.component/my-organizations-dialog.component.ts b/src/app/home-page/widget/organization-box/my-organizations-dialog.component/my-organizations-dialog.component.ts new file mode 100644 index 0000000000..6229d7edc2 --- /dev/null +++ b/src/app/home-page/widget/organization-box/my-organizations-dialog.component/my-organizations-dialog.component.ts @@ -0,0 +1,19 @@ +import { Component, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { OrganizationUpdateTime } from 'app/shared/openapi'; + +@Component({ + selector: 'app-my-organizations-dialog', + templateUrl: './my-organizations-dialog.component.html', +}) +export class MyOrganizationsDialogComponent { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: Array + ) {} + + // Close dialog and return org name to parent for navigation + public dialogNavigate(orgName: string) { + this.dialogRef.close({ data: orgName }); + } +} diff --git a/src/app/home-page/widget/organization-box/organization-box.component.html b/src/app/home-page/widget/organization-box/organization-box.component.html index aa63f2a854..14658320e6 100644 --- a/src/app/home-page/widget/organization-box/organization-box.component.html +++ b/src/app/home-page/widget/organization-box/organization-box.component.html @@ -4,26 +4,20 @@
Organizations - {{ totalOrgs }} + {{ totalOrgs }}
- -
- - - -
My Organizations
-
- No matching search results. -
-
- - {{ org.displayName }} - -
{{ org.lastUpdateDate | date: 'MMM d, yyyy' }}
+ + diff --git a/src/app/home-page/widget/organization-box/organization-box.component.ts b/src/app/home-page/widget/organization-box/organization-box.component.ts index 6e4734396e..320ea9be3e 100644 --- a/src/app/home-page/widget/organization-box/organization-box.component.ts +++ b/src/app/home-page/widget/organization-box/organization-box.component.ts @@ -4,13 +4,12 @@ import { RequestsQuery } from 'app/loginComponents/state/requests.query'; import { RequireAccountsModalComponent } from 'app/organizations/registerOrganization/requireAccountsModal/require-accounts-modal.component'; import { Base } from 'app/shared/base'; import { Dockstore } from 'app/shared/dockstore.model'; -import { Event, OrganizationUser, OrganizationUpdateTime } from 'app/shared/openapi'; -import { UsersService } from '../../../shared/openapi'; -import { finalize, takeUntil } from 'rxjs/operators'; -import { bootstrap4mediumModalSize } from 'app/shared/constants'; -import { HttpResponse, HttpErrorResponse } from '@angular/common/http'; -import { AlertService } from 'app/shared/alert/state/alert.service'; +import { Event, UsersService, OrganizationUser, OrganizationUpdateTime } from 'app/shared/openapi'; +import { bootstrap4mediumModalSize, bootstrap4largeModalSize } from 'app/shared/constants'; import { Observable } from 'rxjs'; +import { finalize, takeUntil } from 'rxjs/operators'; +import { MyOrganizationsDialogComponent } from './my-organizations-dialog.component/my-organizations-dialog.component'; +import { Router } from '@angular/router'; @Component({ selector: 'app-organization-box', @@ -19,11 +18,10 @@ import { Observable } from 'rxjs'; }) export class OrganizationBoxComponent extends Base implements OnInit { Dockstore = Dockstore; - public listOfOrganizations: Array = []; public pendingRequests$: Observable>; public pendingInvites$: Observable>; - public filterText: string = ''; public totalOrgs: number = 0; + public listOfOrgs: Array; public EventType = Event.TypeEnum; public isLoading = true; @@ -31,44 +29,41 @@ export class OrganizationBoxComponent extends Base implements OnInit { private usersService: UsersService, private requestsQuery: RequestsQuery, private matDialog: MatDialog, - private alertService: AlertService + private router: Router ) { super(); } ngOnInit(): void { - this.getMyOrganizations(); - this.pendingRequests$ = this.requestsQuery.myPendingOrganizationRequests$; - this.pendingInvites$ = this.requestsQuery.myOrganizationInvites$; - } - - private getMyOrganizations() { this.usersService - .getUserDockstoreOrganizations(null, this.filterText, 'response') + .getUserDockstoreOrganizations() .pipe( finalize(() => (this.isLoading = false)), takeUntil(this.ngUnsubscribe) ) - .subscribe( - (myOrgs: HttpResponse) => { - this.listOfOrganizations = myOrgs.body.slice(0, 7); - // Update total orgs only when no search filter applied (i.e. non-filtered total) - // Handles cases with no filter param and empty filter param - const url = new URL(myOrgs.url); - if (!url.searchParams.get('filter')) { - this.totalOrgs = myOrgs.body.length; - } - }, - (error: HttpErrorResponse) => { - this.listOfOrganizations = []; - this.alertService.detailedError(error); - } - ); + .subscribe((myOrgs) => { + this.totalOrgs = myOrgs.length; + this.listOfOrgs = myOrgs; + }); + this.pendingRequests$ = this.requestsQuery.myPendingOrganizationRequests$; + this.pendingInvites$ = this.requestsQuery.myOrganizationInvites$; } - onTextChange(event: any) { - this.isLoading = true; - this.getMyOrganizations(); + /** + * Open dialog diaplaying user's organizations + * When user selects an organization, close dialog and navigate + * + * NOTE: Remove my organizations dialog once my organizations page is implemented + */ + viewMyOrganizations(): void { + const dialogRef = this.matDialog.open(MyOrganizationsDialogComponent, { width: bootstrap4largeModalSize, data: this.listOfOrgs }); + + // Navigate to selected Organization + dialogRef.afterClosed().subscribe((result) => { + if (result?.data) { + this.router.navigateByUrl('/organizations/' + result.data); + } + }); } /** diff --git a/src/app/home-page/widget/starred-box/starred-box.component.html b/src/app/home-page/widget/starred-box/starred-box.component.html index 7738241d54..98757c5670 100644 --- a/src/app/home-page/widget/starred-box/starred-box.component.html +++ b/src/app/home-page/widget/starred-box/starred-box.component.html @@ -33,148 +33,16 @@
- -
-
Recent Activity
-
-
- User avatar -
-
-
- {{ event.initiatorUser?.username }} created the {{ event.version?.referenceType | lowercase }} - {{ event.version?.name }} in {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ - (event?.tool | entryToDisplayName) || (event?.workflow | entryToDisplayName) || (event?.apptool | entryToDisplayName) - }} -
-
{{ 'on ' + (event.dbCreateDate | date: 'medium') }}
- -
-
-
- {{ event.initiatorUser?.username }} created the collection - {{ event.collection?.name }} - - in organization - {{ event.organization?.displayName }} -
-
{{ event.dbCreateDate | date: 'medium' }}
-
-
-
- {{ event.initiatorUser.username }} added the {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ event?.apptool ? event?.apptool.workflowName : event?.tool ? event?.tool.name : event?.workflow.workflowName }} - to the collection - {{ event.collection?.name }} - - in organization - {{ event.organization?.displayName }} -
-
{{ event.dbCreateDate | date: 'medium' }}
-
-
-
- {{ event.initiatorUser.username }} added {{ event.user.username }} to the organization - {{ event.organization?.displayName }} -
-
{{ event.dbCreateDate | date: 'medium' }}
-
-
-
- {{ event.initiatorUser.username }} edited the collection - {{ event.collection?.name }} - -
-
{{ event.dbCreateDate | date: 'medium' }}
-
-
-
- {{ event.initiatorUser.username }} removed the {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ event?.apptool ? event?.apptool.workflowName : event?.tool ? event?.tool.name : event?.workflow.workflowName }} - from the collection - {{ event.collection?.name }} - -
-
{{ event.dbCreateDate | date: 'medium' }}
-
-
-
- {{ event.user.username }} published the {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ - (event?.tool | entryToDisplayName) || (event?.workflow | entryToDisplayName) || (event?.apptool | entryToDisplayName) - }} -
-
{{ 'on ' + (event.dbCreateDate | date: 'medium') }}
-
-
-
- {{ event.user.username }} unpublished the {{ event?.tool || event?.apptool ? 'tool' : 'workflow' }} - {{ - (event?.tool | entryToDisplayName) || (event?.workflow | entryToDisplayName) || (event?.apptool | entryToDisplayName) - }} -
-
{{ 'on ' + (event.dbCreateDate | date: 'medium') }}
-
-
-
- Org avatar -
+ + - -
+ +
You do not have any starred entries or organizations.
diff --git a/src/app/home-page/widget/starred-box/starred-box.component.ts b/src/app/home-page/widget/starred-box/starred-box.component.ts index 6851baa608..eb9da780f7 100644 --- a/src/app/home-page/widget/starred-box/starred-box.component.ts +++ b/src/app/home-page/widget/starred-box/starred-box.component.ts @@ -1,10 +1,7 @@ -import { HttpErrorResponse } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; -import { AlertService } from 'app/shared/alert/state/alert.service'; import { Base } from 'app/shared/base'; import { Dockstore } from 'app/shared/dockstore.model'; -import { Event, UsersService, EventsService } from 'app/shared/openapi'; -import { finalize, takeUntil } from 'rxjs/operators'; +import { UsersService } from 'app/shared/openapi'; @Component({ selector: 'app-starred-box', @@ -12,32 +9,20 @@ import { finalize, takeUntil } from 'rxjs/operators'; styleUrls: ['./starred-box.component.scss', '../../../shared/styles/dashboard-boxes.scss'], }) export class StarredBoxComponent extends Base implements OnInit { - Dockstore = Dockstore; - totalStarredWorkflows: number = 0; - totalStarredTools: number = 0; - totalStarredServices: number = 0; - totalStarredOrganizations: number = 0; - events: Array = []; - public isLoading = true; - EventType = Event.TypeEnum; - private readonly supportedEventTypes = [ - Event.TypeEnum.ADDVERSIONTOENTRY, - Event.TypeEnum.CREATECOLLECTION, - Event.TypeEnum.ADDTOCOLLECTION, - Event.TypeEnum.PUBLISHENTRY, - Event.TypeEnum.UNPUBLISHENTRY, - Event.TypeEnum.MODIFYCOLLECTION, - Event.TypeEnum.ADDUSERTOORG, - ]; + public Dockstore = Dockstore; + public totalStarredWorkflows: number = 0; + public totalStarredTools: number = 0; + public totalStarredServices: number = 0; + public totalStarredOrganizations: number = 0; + // Hides starred services, remove when implemented - serviceImplemented: boolean = false; + public serviceImplemented: boolean = false; - constructor(private usersService: UsersService, private eventsService: EventsService, private alertService: AlertService) { + constructor(private usersService: UsersService) { super(); } ngOnInit(): void { - this.getMyEvents(); this.usersService.getStarredServices().subscribe((starredServices) => { this.totalStarredServices = starredServices.length; }); @@ -51,21 +36,4 @@ export class StarredBoxComponent extends Base implements OnInit { this.totalStarredOrganizations = starredOrganizations.length; }); } - - private getMyEvents() { - this.eventsService - .getEvents('ALL_STARRED') - .pipe( - finalize(() => (this.isLoading = false)), - takeUntil(this.ngUnsubscribe) - ) - .subscribe( - (events) => { - this.events = events.filter((event) => this.supportedEventTypes.includes(event.type)).slice(0, 4); - }, - (error: HttpErrorResponse) => { - this.alertService.detailedError(error); - } - ); - } } diff --git a/src/app/shared/entry-to-display-name.pipe.ts b/src/app/shared/entry-to-display-name.pipe.ts index 0648b3ba88..6d6a59f69b 100644 --- a/src/app/shared/entry-to-display-name.pipe.ts +++ b/src/app/shared/entry-to-display-name.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { DockstoreTool, Workflow } from './swagger'; +import { DockstoreTool, Workflow } from './openapi'; /** * This pipe converts and entry (DockstoreTool or Workflow) to a name that is missing the registry or source control diff --git a/src/app/shared/entry/recent-events.pipe.ts b/src/app/shared/entry/recent-events.pipe.ts new file mode 100644 index 0000000000..443ad36363 --- /dev/null +++ b/src/app/shared/entry/recent-events.pipe.ts @@ -0,0 +1,58 @@ +import { Inject, Pipe, PipeTransform } from '@angular/core'; +import { Event } from 'app/shared/openapi'; +import { EntryToDisplayNamePipe } from '../entry-to-display-name.pipe'; +import { EntryType } from '../../shared/enum/entry-type'; + +@Pipe({ + name: 'recentEvents', +}) +// TO DO: Accommodate for notebooks and services when we can retrieve those events +export class RecentEventsPipe implements PipeTransform { + private EntryType = EntryType; + constructor(@Inject(EntryToDisplayNamePipe) private entryToDisplayNamePipe: EntryToDisplayNamePipe) {} + + /** + * Takes in an event object and extracts details used in the RecentEventsComponent + * + * @param {Event} event + * @param {string} type 'displayName' | 'entryLink' | 'entryType' | 'orgLink' | 'collectionLink' + * @returns String | null + */ + transform(event: Event, type: 'displayName' | 'entryLink' | 'entryType' | 'orgLink' | 'collectionLink'): string | null { + if (!event || !type) { + return null; + } + + switch (type) { + case 'displayName': { + if (event.workflow) { + return this.entryToDisplayNamePipe.transform(event.workflow); + } else if (event.tool) { + return this.entryToDisplayNamePipe.transform(event.tool); + } else if (event.apptool) { + return this.entryToDisplayNamePipe.transform(event.apptool); + } + break; + } + case 'entryLink': { + if (event.workflow) { + return '/workflows/' + event.workflow.full_workflow_path; + } else if (event.tool) { + return '/tools/' + event.tool.tool_path; + } else if (event.apptool) { + return '/tools/' + event.apptool.full_workflow_path; + } + break; + } + case 'entryType': { + return event.tool || event.apptool ? this.EntryType.Tool : this.EntryType.BioWorkflow; + } + case 'orgLink': { + return '/organizations/' + event.organization.name; + } + case 'collectionLink': { + return '/organizations/' + event.organization.name + '/collections/' + event.collection.name; + } + } + } +} diff --git a/src/app/shared/pipe/pipe.module.ts b/src/app/shared/pipe/pipe.module.ts index 5eb3cc4d75..199a876c77 100644 --- a/src/app/shared/pipe/pipe.module.ts +++ b/src/app/shared/pipe/pipe.module.ts @@ -12,6 +12,8 @@ import { SelectTabPipe } from '../entry/select-tab.pipe'; import { BaseUrlPipe } from '../entry/base-url.pipe'; import { DescriptorLanguageVersionsPipe } from '../entry/descriptor-language-versions.pipe'; import { DescriptorLanguagePipe } from '../entry/descriptor-language.pipe'; +import { RecentEventsPipe } from '../entry/recent-events.pipe'; +import { EntryToDisplayNamePipe } from '../entry-to-display-name.pipe'; const DECLARATIONS: any[] = [ FilePathPipe, @@ -26,10 +28,12 @@ const DECLARATIONS: any[] = [ BaseUrlPipe, DescriptorLanguageVersionsPipe, DescriptorLanguagePipe, + RecentEventsPipe, ]; @NgModule({ imports: [CommonModule], declarations: DECLARATIONS, exports: DECLARATIONS, + providers: [EntryToDisplayNamePipe], }) export class PipeModule {} diff --git a/src/app/shared/styles/dashboard-boxes.scss b/src/app/shared/styles/dashboard-boxes.scss index ad39b7a59c..47b787af74 100644 --- a/src/app/shared/styles/dashboard-boxes.scss +++ b/src/app/shared/styles/dashboard-boxes.scss @@ -9,7 +9,7 @@ img[alt='User avatar'] { margin: 0.8rem; } -img[alt='Org avatar'] { +img.entry-img { width: 6rem; max-height: 6rem; object-fit: scale-down; @@ -29,6 +29,11 @@ button[mat-icon-button] { border-bottom: 0.1rem solid mat.get-color-from-palette($dockstore-app-gray, 3); } +// Ensure recent activity events are uniform +.event { + min-height: 8rem; +} + // Overide mat-card padding to extend mat-divider to edges mat-divider { margin: 0 -16px; diff --git a/src/materialColorScheme.scss b/src/materialColorScheme.scss index f426638e92..dc085477b9 100644 --- a/src/materialColorScheme.scss +++ b/src/materialColorScheme.scss @@ -197,6 +197,8 @@ $workflow-selection-color: #d2fbf0; $service-color: #ff6c44; $service-selection-color: #ffdbcf; +$org-selection-color: #dde1f2; + // Color used to indicate success in general (of an operation, verified badge, etc). $accent-3-dark: #00bfa5 !important; diff --git a/src/styles.scss b/src/styles.scss index 3b66037870..ea1b9f4853 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -904,6 +904,12 @@ app-workflow-sidebar-accordion background: $service-selection-color !important; } +// Set the background to the color associated with organizations (purple). +// Used to set the background color in bubbles. +.org-background { + background: $org-selection-color !important; +} + // Styles an element as a "bubble", which is a small very-rounded rectangular element that typically contains a word or short phrase. // Bubbles always render in a 12px font, are 28px tall, and default to gray background. .bubble {