Skip to content

Commit

Permalink
Improve home page view (#4740)
Browse files Browse the repository at this point in the history
* Allow k8s namespaces to be added as favourites

* Improve the home page

* Fix lint issue

* Tweaks and refactoring

* Add separator to layout menu

* Improve incremental loading

* Various tidyups and improvements

* Tidy ups and improvemts to async loading

* Fix progress spinner alignment

* Unscubribe

* Minor bug fixes and a few tidy ups

* Further tidy ups

* Fix front end unit tests

* Unit test fix

* Further unit tests fixes

* Endpoint card unit test fixes

* Fix unit tests

* Tidy up favorite card

* Further refinement to the UI

* Fix missing dates from recents on CF view

* Get app deploy from home screen working

* Add message for when no connected endpoints

* Unit test fixes

* Kubernetes Home Card unit test fixes

* Fix unit test error

* Move components to remove dependency on large shared module

* Fix unit test and add deploy tiles to CF Home Card

* Fix word wrap on favorite name

* Add tool tip

* Style tidy ups

* Minor tidy ups

* Fix test imports

* Fix unit tests

* e2e Debugging

* Fix for org delete e2e test

* E2e Test fix

* Address PR feedback so far

* Address PR feedback

* Fix connected endpoint state check on home screen

* Address PR feedback

* Restore apps link

* Fix unit test
  • Loading branch information
nwmac authored Nov 16, 2020
1 parent 8a36a1f commit 26b0734
Show file tree
Hide file tree
Showing 110 changed files with 2,342 additions and 1,053 deletions.
38 changes: 38 additions & 0 deletions src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Compiler, Injector } from '@angular/core';
import { Action, Store } from '@ngrx/store';
import moment from 'moment';
import { combineLatest, Observable, of } from 'rxjs';
Expand Down Expand Up @@ -236,6 +237,33 @@ export interface CFBasePipelineRequestActionMeta {
flatten?: boolean;
}

function cfShortcuts(id: string) {
return [
{
title: 'View Organizations',
link: ['/cloud-foundry', id, 'organizations'],
icon: 'organization',
iconFont: 'stratos-icons'
},
{
title: 'View Applications',
link: ['/applications', id],
icon: 'apps'
},
{
title: 'Deploy an Application',
link: ['/applications', 'new', id],
icon: 'publish'
},
{
title: 'View Cloud Foundry Info',
link: ['/cloud-foundry', id],
icon: 'cloud_foundry',
iconFont: 'stratos-icons'
},
];
}

export function generateCFEntities(): StratosBaseCatalogEntity[] {
const endpointDefinition: StratosEndpointExtensionDefinition = {
urlValidationRegexString: urlValidationExpression,
Expand All @@ -246,6 +274,16 @@ export function generateCFEntities(): StratosBaseCatalogEntity[] {
iconFont: 'stratos-icons',
logoUrl: '/core/assets/endpoint-icons/cloudfoundry.png',
authTypes: [BaseEndpointAuth.UsernamePassword, BaseEndpointAuth.SSO],
homeCard: {
component: (compiler: Compiler, injector: Injector) => import('./features/home/cfhome-card/cfhome-card.module').then(m => {
return compiler.compileModuleAndAllComponentsAsync(m.CFHomeCardModule).then(cm => {
const mod = cm.ngModuleFactory.create(injector);
return mod.instance.createHomeCard(mod.componentFactoryResolver);
});
}),
shortcuts: cfShortcuts,
fullView: false,
},
listDetailsComponent: CfEndpointDetailsComponent,
renderPriority: 1,
healthCheck: new EndpointHealthCheck(CF_ENDPOINT_TYPE, (endpoint) => cfEntityCatalog.cfInfo.api.get(endpoint.guid)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DatePipe } from '@angular/common';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';

import { TabNavService } from '../../../../../core/src/tab-nav.service';
import { generateCfBaseTestModules } from '../../../../test-framework/cloud-foundry-endpoint-service.helper';
Expand All @@ -24,6 +25,15 @@ describe('ApplicationWallComponent', () => {
DatePipe,
TabNavService,
CloudFoundryService,
{
provide: ActivatedRoute,
useValue: {
snapshot: {
params: {},
queryParams: {}
}
}
}
]
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { animate, query, style, transition, trigger } from '@angular/animations';
import { Component, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
Expand All @@ -12,6 +13,7 @@ import { CfAppsDataSource } from '../../../shared/components/list/list-types/app
import { CfOrgSpaceDataService, initCfOrgSpaceService } from '../../../shared/data-services/cf-org-space-service.service';
import { CloudFoundryService } from '../../../shared/data-services/cloud-foundry.service';
import { CfCurrentUserPermissions } from '../../../user-permissions/cf-user-permissions-checkers';
import { goToAppWall } from '../../cf/cf.helpers';

@Component({
selector: 'app-application-wall',
Expand Down Expand Up @@ -48,7 +50,15 @@ export class ApplicationWallComponent implements OnDestroy {
public cloudFoundryService: CloudFoundryService,
private store: Store<CFAppState>,
private cfOrgSpaceService: CfOrgSpaceDataService,
activatedRoute: ActivatedRoute,
) {
// If we have an endpoint ID, select it and redirect
const { endpointId } = activatedRoute.snapshot.params;
if (endpointId) {
goToAppWall(this.store, endpointId);
return;
}

this.cfIds$ = cloudFoundryService.cFEndpoints$.pipe(
map(endpoints => endpoints.map(endpoint => endpoint.guid)),
);
Expand All @@ -65,6 +75,8 @@ export class ApplicationWallComponent implements OnDestroy {
}

ngOnDestroy(): void {
this.initCfOrgSpaceService.unsubscribe();
if (this.initCfOrgSpaceService) {
this.initCfOrgSpaceService.unsubscribe();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ const applicationsRoutes: Routes = [
{
path: 'new',
component: NewApplicationBaseStepComponent,
pathMatch: 'full'
},
{
path: 'new/:endpointId',
component: NewApplicationBaseStepComponent,
pathMatch: 'full'
},
{
path: 'create',
Expand All @@ -59,6 +65,11 @@ const applicationsRoutes: Routes = [
extensionsActionsKey: StratosActionType.Applications
}
},
{
path: ':endpointId',
component: ApplicationWallComponent,
pathMatch: 'full'
},
{
path: ':endpointId/:id',
component: ApplicationBaseComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { RouterNav } from '../../../../../store/src/actions/router.actions';
import { selectPaginationState } from '../../../../../store/src/selectors/pagination.selectors';
import { CfAppsDataSource } from '../../../shared/components/list/list-types/app/cf-apps-data-source';
import { CfOrgSpaceDataService } from '../../../shared/data-services/cf-org-space-service.service';
import { AUTO_SELECT_CF_URL_PARAM } from '../new-application-base-step/new-application-base-step.component';
import { ApplicationDeploySourceTypes } from './deploy-application-steps.types';

@Component({
Expand Down Expand Up @@ -78,6 +79,11 @@ export class DeployApplicationComponent implements OnInit, OnDestroy {
}

ngOnInit(): void {
// Has the endpoint ID been specified in the URL?
const endpoint = this.activatedRoute.snapshot.queryParams[AUTO_SELECT_CF_URL_PARAM];
if (endpoint) {
this.cfOrgSpaceService.cf.select.next(endpoint);
}

if (this.appGuid) {
this.deployButtonText = 'Redeploy';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';

import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state';
Expand All @@ -11,10 +12,14 @@ import {
AUTO_SELECT_DEPLOY_TYPE_URL_PARAM,
} from '../deploy-application/deploy-application-steps.types';

interface IAppTileData extends ITileData {
export const AUTO_SELECT_CF_URL_PARAM = 'auto-select-endpoint';


export interface IAppTileData extends ITileData {
type: string;
subType?: string;
}

@Component({
selector: 'app-new-application-base-step',
templateUrl: './new-application-base-step.component.html',
Expand All @@ -36,6 +41,12 @@ export class NewApplicationBaseStepComponent {
if (tile.data.subType) {
query[AUTO_SELECT_DEPLOY_TYPE_URL_PARAM] = tile.data.subType;
}
const endpoint = this.activatedRoute.snapshot.params.endpointId;
if (endpoint) {
query[AUTO_SELECT_CF_URL_PARAM] = endpoint;
query[BASE_REDIRECT_QUERY] += `/${endpoint}`;
}

this.store.dispatch(new RouterNav({
path: `${baseUrl}/${type}`,
query
Expand All @@ -45,7 +56,10 @@ export class NewApplicationBaseStepComponent {

constructor(
private store: Store<CFAppState>,
appDeploySourceTypes: ApplicationDeploySourceTypes) {
appDeploySourceTypes: ApplicationDeploySourceTypes,
private activatedRoute: ActivatedRoute,

) {
this.sourceTypes = appDeploySourceTypes.getTypes();
this.tileSelectorConfig = [
...this.sourceTypes.map(type =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CloudFoundrySharedModule } from '../../shared/cf-shared.module';
import {
CFEndpointsListConfigService,
} from '../../shared/components/list/list-types/cf-endpoints/cf-endpoints-list-config.service';
import { CFHomeCardModule } from '../home/cfhome-card/cfhome-card.module';
import { AddOrganizationComponent } from './add-organization/add-organization.component';
import {
CreateOrganizationStepComponent,
Expand Down Expand Up @@ -138,7 +139,8 @@ import { RemoveUserComponent } from './users/remove-user/remove-user.component';
CloudFoundrySectionRoutingModule,
RouterModule,
NgxChartsModule,
CloudFoundrySharedModule
CloudFoundrySharedModule,
CFHomeCardModule,
],
declarations: [
CloudFoundryBaseComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,33 @@ export class CloudFoundryEndpointService {
return fetchTotalResults(action, store, pmf);
}

// Fetch the cound of organisations in a Cloud Foundry
public static fetchOrgCount(store: Store<CFAppState>, pmf: PaginationMonitorFactory, cfGuid: string): Observable<number> {
const getAllOrgsAction = CloudFoundryEndpointService.createGetAllOrganizations(cfGuid);
return fetchTotalResults(getAllOrgsAction, store, pmf);
}

public static fetchOrgs(store: Store<CFAppState>, pmf: PaginationMonitorFactory, cfGuid: string):
Observable<APIResource<IOrganization>[]> {
const getAllOrgsAction = CloudFoundryEndpointService.createGetAllOrganizations(cfGuid);
return getPaginationObservables<APIResource<IOrganization>>({
store,
action: getAllOrgsAction,
paginationMonitor: pmf.create(
getAllOrgsAction.paginationKey,
cfEntityFactory(organizationEntityType),
getAllOrgsAction.flattenPagination
)
}, getAllOrgsAction.flattenPagination).entities$;
}

constructor(
public activeRouteCfOrgSpace: ActiveRouteCfOrgSpace,
private store: Store<CFAppState>,
private cfUserService: CfUserService,
private pmf: PaginationMonitorFactory,
) {
this.cfGuid = activeRouteCfOrgSpace.cfGuid;

this.cfEndpointEntityService = stratosEntityCatalog.endpoint.store.getEntityService(this.cfGuid);

this.cfInfoEntityService = cfEntityCatalog.cfInfo.store.getEntityService(this.cfGuid);
Expand All @@ -152,16 +171,7 @@ export class CloudFoundryEndpointService {
private constructCoreObservables() {
this.endpoint$ = this.cfEndpointEntityService.waitForEntity$;

const getAllOrgsAction = CloudFoundryEndpointService.createGetAllOrganizations(this.cfGuid);
this.orgs$ = getPaginationObservables<APIResource<IOrganization>>({
store: this.store,
action: getAllOrgsAction,
paginationMonitor: this.pmf.create(
getAllOrgsAction.paginationKey,
cfEntityFactory(organizationEntityType),
getAllOrgsAction.flattenPagination
)
}, getAllOrgsAction.flattenPagination).entities$;
this.orgs$ = CloudFoundryEndpointService.fetchOrgs(this.store, this.pmf, this.cfGuid);

this.info$ = this.cfInfoEntityService.waitForEntity$;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@
<app-tile-group>
<app-tile>
<app-card-cf-recent-apps *ngIf="!(detailsLoading$ | async)" [allApps$]="cfSpaceService.apps$"
[loading$]="cfSpaceService.loadingApps$" (refresh)="cfSpaceService.fetchApps()"></app-card-cf-recent-apps>
[endpoint]="cfEndpointService.cfGuid"
[loading$]="cfSpaceService.loadingApps$" (refresh)="cfSpaceService.fetchApps()"></app-card-cf-recent-apps>
</app-tile>
</app-tile-group>
</app-loading-page>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ import {
generateCfBaseTestModules,
} from '../../../../../../../../test-framework/cloud-foundry-endpoint-service.helper';
import { CloudFoundrySpaceServiceMock } from '../../../../../../../../test-framework/cloud-foundry-space.service.mock';
import {
CardCfRecentAppsComponent,
} from '../../../../../../../shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component';
import {
CompactAppCardComponent,
} from '../../../../../../../shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component';
import {
CardCfSpaceDetailsComponent,
} from '../../../../../../../shared/components/cards/card-cf-space-details/card-cf-space-details.component';
Expand All @@ -22,6 +16,10 @@ import {
import {
CloudFoundryUserProvidedServicesService,
} from '../../../../../../../shared/services/cloud-foundry-user-provided-services.service';
import { CardCfRecentAppsComponent } from '../../../../../../home/card-cf-recent-apps/card-cf-recent-apps.component';
import {
CompactAppCardComponent,
} from '../../../../../../home/card-cf-recent-apps/compact-app-card/compact-app-card.component';
import { CloudFoundryEndpointService } from '../../../../../services/cloud-foundry-endpoint.service';
import { CloudFoundryOrganizationService } from '../../../../../services/cloud-foundry-organization.service';
import { CloudFoundrySpaceService } from '../../../../../services/cloud-foundry-space.service';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
<app-tile-group>
<app-tile>
<app-card-cf-recent-apps *ngIf="!(detailsLoading$ | async)" [allApps$]="cfOrgService.apps$"
[endpoint]="cfEndpointService.cfGuid"
[loading$]="cfOrgService.loadingApps$" (refresh)="cfOrgService.fetchApps()"></app-card-cf-recent-apps>
</app-tile>
</app-tile-group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ import {
import {
CardCfOrgUserDetailsComponent,
} from '../../../../../shared/components/cards/card-cf-org-user-details/card-cf-org-user-details.component';
import {
CardCfRecentAppsComponent,
} from '../../../../../shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component';
import {
CompactAppCardComponent,
} from '../../../../../shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component';
import { CfUserPermissionDirective } from '../../../../../shared/directives/cf-user-permission/cf-user-permission.directive';
import { CardCfRecentAppsComponent } from '../../../../home/card-cf-recent-apps/card-cf-recent-apps.component';
import { CompactAppCardComponent } from '../../../../home/card-cf-recent-apps/compact-app-card/compact-app-card.component';
import { CloudFoundryOrganizationService } from '../../../services/cloud-foundry-organization.service';
import { CloudFoundryOrganizationSummaryComponent } from './cloud-foundry-organization-summary.component';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<app-tile-group>
<app-tile>
<app-card-cf-recent-apps *ngIf="!(detailsLoading$ | async)"
[endpoint]="cfEndpointService.cfGuid"
[allApps$]="cfEndpointService.appsPagObs.entities$"
[loading$]="cfEndpointService.appsPagObs.fetchingEntities$" (refresh)="cfEndpointService.fetchApps()">
</app-card-cf-recent-apps>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,8 @@ import {
generateTestCfEndpointServiceProvider,
} from '../../../../../test-framework/cloud-foundry-endpoint-service.helper';
import { CardCfInfoComponent } from '../../../../shared/components/cards/card-cf-info/card-cf-info.component';
import {
CardCfRecentAppsComponent,
} from '../../../../shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component';
import {
CompactAppCardComponent,
} from '../../../../shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component';
import { CardCfRecentAppsComponent } from '../../../home/card-cf-recent-apps/card-cf-recent-apps.component';
import { CompactAppCardComponent } from '../../../home/card-cf-recent-apps/compact-app-card/compact-app-card.component';
import { CloudFoundrySummaryTabComponent } from './cloud-foundry-summary-tab.component';

describe('CloudFoundrySummaryTabComponent', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<ng-container *ngIf="(hasEntities$ | async) !== false; else placeholder">
<mat-card class="recent-apps-card" [ngClass]="{'recent-apps-card__plain': mode === 'plain'}" *ngIf="show$ | async">
<mat-card-header class="recent-apps-card__header">
<mat-card-title class="recent-apps-card__title">Recently updated applications</mat-card-title>
<app-polling-indicator *ngIf="canRefresh" [manualPoll]="!(loading$ | async)" [isPolling]="(loading$ | async)"
(poll)="refresh.emit()"></app-polling-indicator>
</mat-card-header>
<mat-card-content class="recent-apps-card__content" *ngIf="recentApps$ | async as apps">
<div *ngIf="apps.length === 0">
There are no applications.
</div>
<div *ngIf="apps.length > 0" class="recent-apps-card__scroller">
<app-compact-app-card [showDate]="showDate" [dateMode]="dateMode" *ngFor="let app of apps" [endpoint]="endpoint" [app]="app"></app-compact-app-card>
</div>
</mat-card-content>
</mat-card>
</ng-container>

<ng-template #placeholder>
<mat-card class="recent-apps-card" [ngClass]="{'recent-apps-card__plain': mode === 'plain'}">
<mat-card-header class="recent-apps-card__header">
<mat-card-title class="recent-apps-card__title">Recently updated applications</mat-card-title>
</mat-card-header>
<mat-card-content class="recent-apps-card__content">
<div class="recent-apps-card__scroller">
<app-compact-app-card [showDate]="showDate" [dateMode]="dateMode" *ngFor="let app of placeholders" [endpoint]="endpoint" [app]="app"></app-compact-app-card>
</div>
</mat-card-content>
</mat-card>
</ng-template>
Loading

0 comments on commit 26b0734

Please sign in to comment.