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

Feat/1045 tile component #808

Merged
merged 13 commits into from
Dec 19, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,23 @@

<div class="page-content">
<div class="container">Loaded resource: {{ this.resource()?.name }}</div>

<app-tile-component
[title]="'Templates'"
[actionName]="'New Template'"
[canAction]="true"
[lists]="templatesData()"
(tileAction)="add()"
(listAction)="doListAction($event)"
></app-tile-component>

<app-tile-component
[title]="'Functions'"
[actionName]="'New function'"
[canAction]="true"
[lists]="functionsData()"
(tileAction)="add()"
(listAction)="doListAction($event)"
></app-tile-component>
</div>
</app-page>
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentRef } from '@angular/core';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { ResourceEditPageComponent } from './resource-edit-page.component';
import { ActivatedRoute } from '@angular/router';
import { of, Subject } from 'rxjs';
import { RouterTestingModule } from '@angular/router/testing';

describe('ResourceEditComponent', () => {
describe('ResourceEditPageComponent', () => {
let component: ResourceEditPageComponent;
let componentRef: ComponentRef<ResourceEditPageComponent>;
let fixture: ComponentFixture<ResourceEditPageComponent>;

const mockRoute: any = { queryParamMap: of() };
Expand All @@ -30,7 +28,6 @@ describe('ResourceEditComponent', () => {

fixture = TestBed.createComponent(ResourceEditPageComponent);
component = fixture.componentInstance;
componentRef = fixture.componentRef;
fixture.detectChanges();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { Component, computed, inject, Signal } from '@angular/core';
import { Component, computed, inject, Signal, signal } from '@angular/core';
import { LoadingIndicatorComponent } from '../../shared/elements/loading-indicator.component';
import { PageComponent } from '../../layout/page/page.component';
import { ActivatedRoute } from '@angular/router';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { toSignal } from '@angular/core/rxjs-interop';
import { ResourceService } from '../../resource/resource.service';
import { Resource } from '../../resource/resource';
import { EntryAction, TileListEntry, TileListEntryOutput } from '../../shared/tile/tile-list/tile-list.component';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TileComponent } from '../../shared/tile/tile.component';

@Component({
selector: 'app-resources-edit',
selector: 'app-resources-edit-page',
standalone: true,
imports: [LoadingIndicatorComponent, PageComponent],
imports: [LoadingIndicatorComponent, PageComponent, TileComponent],
templateUrl: './resource-edit-page.component.html',
})
export class ResourceEditPageComponent {
private modalService = inject(NgbModal);
private resourceService = inject(ResourceService);
private route = inject(ActivatedRoute);

Expand All @@ -33,4 +37,68 @@ export class ResourceEditPageComponent {
return false;
}
});

templatesData = signal([
{
title: 'Instance Templates',
entries: [
{ name: 'startJob_0.sh', description: 'startJob_0.sh', id: 0 },
{ name: 'startJob_1.sh', description: 'job 2 again', id: 1 },
] as TileListEntry[],
canEdit: true,
canDelete: true,
},
{
title: 'Resource Type Templates',
entries: [{ name: 'seg', description: 'segmentation', id: 121 }] as TileListEntry[],
canOverwrite: false,
},
]);

functionsData = signal([
{
title: 'Resource Instance Functions',
entries: [
{ name: 'Function1', description: 'bla', id: 42 },
{ name: 'Function 2', description: 'whatever', id: 69 },
],
canEdit: true,
canDelete: true,
},
{
title: 'Resource Type Functions',
entries: [{ name: 'seg', description: 'segmentation', id: 666 }] as TileListEntry[],
canOverwrite: false,
},
]);

add() {
this.modalService.open('This would open a modal to add something');
}

doListAction($event: TileListEntryOutput) {
switch ($event.action) {
case EntryAction.edit:
this.editFunction($event.id);
return;
case EntryAction.delete:
this.deleteFunction($event.id);
return;
case EntryAction.overwrite:
this.overwriteFunction($event.id);
return;
}
}

private editFunction(id: number) {
this.modalService.open('This would open a modal to edit function with id:' + id);
}

private deleteFunction(id: number) {
this.modalService.open('This would open a modal to delete function with id:' + id);
}

private overwriteFunction(id: number) {
this.modalService.open('This would open a modal to overwrite function with id:' + id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.title {
font-size: 16px;
font-weight: bold;
margin-bottom: 0.5em;
}

ul {
padding-left: 0;
}

.list-entry {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 0.5em;

.list-entry-text {
display: flex;
flex-direction: column;

.desc {
color: gray;
}
}

.list-entry-actions {
display: flex;
gap: 0.5em
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { TileListComponent } from './tile-list.component';
import { ComponentRef } from '@angular/core';

describe('TileListComponent', () => {
let component: TileListComponent;
let componentRef: ComponentRef<TileListComponent>;
let fixture: ComponentFixture<TileListComponent>;

const tileListData = [
{ name: 'startJob_0.sh', description: 'startJob_0.sh', id: 0 },
{ name: 'startJob_1.sh', description: 'job 2 again', id: 1 },
];

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TileListComponent],
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(TileListComponent);
component = fixture.componentInstance;
componentRef = fixture.componentRef;
componentRef.setInput('title', 'my title');
componentRef.setInput('data', tileListData);
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Component, input, output } from '@angular/core';
import { ButtonComponent } from '../../button/button.component';
import { IconComponent } from '../../icon/icon.component';

export interface TileListEntry {
id: number;
name: string;
description: string;
}

export enum EntryAction {
edit = 'edit',
delete = 'delete',
overwrite = 'overwrite',
}

export interface TileListEntryOutput {
action: EntryAction;
id: number;
}

@Component({
selector: 'app-tile-list',
template: `
<div class="title">{{ title() }}</div>

@if (!data() || data().length <= 0) {
<div class="no-content">
<span>No {{ title() }} for this resource</span>
</div>
}
<ul>
@for (entry of data(); track entry) {
<li class="list-entry">
<div class="list-entry-text">
<span>{{ entry.name }}</span>
<span class="desc">{{ entry.description }}</span>
</div>
<div class="list-entry-actions">
@if (canEdit()) {
<app-button [size]="'sm'" [variant]="'light'" (click)="edit.emit({ action: EntryAction.edit, id: entry.id })"
>Edit
</app-button>
} @if (canDelete()) {
<app-button
[size]="'sm'"
[variant]="'light'"
(click)="delete.emit({ action: EntryAction.delete, id: entry.id })"
>
Delete
</app-button>
} @if (canOverwrite()) {
<app-button
[size]="'sm'"
[variant]="'light'"
(click)="overwrite.emit({ action: EntryAction.overwrite, id: entry.id })"
>
Overwrite
</app-button>
}
</div>
</li>
}
</ul>
`,
styleUrls: ['tile-list.component.scss'],
standalone: true,
imports: [ButtonComponent, IconComponent],
})
export class TileListComponent {
title = input.required<string>();
data = input.required<TileListEntry[]>();
canEdit = input<boolean>(false);
canDelete = input<boolean>(false);
canOverwrite = input<boolean>(false);
edit = output<TileListEntryOutput>();
delete = output<TileListEntryOutput>();
overwrite = output<TileListEntryOutput>();
protected readonly EntryAction = EntryAction;
}
35 changes: 35 additions & 0 deletions AMW_angular/io/src/app/shared/tile/tile.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.tile {
border: 1px solid #ddd;
padding: 1em;
margin: 0.5em;

.tile-header {
display: flex;
flex-direction: row;
justify-content: space-between;
}

.tile-title {
font-weight: bold;
font-size: 18px;
}

.tile-action-bar, span {
padding-left: 0.5em;
}

.tile-body {
margin: 1em 0 0.5em;

.no-content {
font-style: italic;
}
}

}

.list-element {
font-weight: bold;
}


42 changes: 42 additions & 0 deletions AMW_angular/io/src/app/shared/tile/tile.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { TileComponent } from './tile.component';
import { ComponentRef } from '@angular/core';

describe('TileComponent', () => {
let component: TileComponent;
let componentRef: ComponentRef<TileComponent>;
let fixture: ComponentFixture<TileComponent>;

const tileData = [
{
title: 'Instance Templates',
entries: [
{ name: 'startJob_0.sh', description: 'startJob_0.sh', id: 0 },
{ name: 'startJob_1.sh', description: 'job 2 again', id: 1 },
],
canEdit: true,
canDelete: true,
},
];

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TileComponent],
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(TileComponent);
component = fixture.componentInstance;
componentRef = fixture.componentRef;
componentRef.setInput('title', 'my title');
componentRef.setInput('actionName', 'myAction');
componentRef.setInput('canAction', true);
componentRef.setInput('lists', tileData);
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading
Loading