feat: add chain management components
This commit is contained in:
8
src/app/pages/settings/chains/chain-add/chain-add.html
Normal file
8
src/app/pages/settings/chains/chain-add/chain-add.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<h3>{{'settings.chain.new_chain'|translate|upperfirst}}</h3>
|
||||||
|
<app-chain-form [form]="form"/>
|
||||||
|
<app-action-btn
|
||||||
|
(click)="save()"
|
||||||
|
[disabled]="this.form.invalid"
|
||||||
|
class="top-auto"
|
||||||
|
text="{{'common.save'|translate|upperfirst}}"
|
||||||
|
/>
|
||||||
11
src/app/pages/settings/chains/chain-add/chain-add.scss
Normal file
11
src/app/pages/settings/chains/chain-add/chain-add.scss
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
69
src/app/pages/settings/chains/chain-add/chain-add.spec.ts
Normal file
69
src/app/pages/settings/chains/chain-add/chain-add.spec.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ChainAdd } from './chain-add';
|
||||||
|
import { ChainDAO } from '../../../../dao/ChainDAO';
|
||||||
|
import { vi } from 'vitest';
|
||||||
|
import { provideTranslateService } from '@ngx-translate/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { ChainFormGroup } from '../chain-formgroup';
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { ImageStorage } from '../../../../services/image-storage';
|
||||||
|
|
||||||
|
describe('ChainAdd', () => {
|
||||||
|
let component: ChainAdd;
|
||||||
|
let fixture: ComponentFixture<ChainAdd>;
|
||||||
|
|
||||||
|
let chainDAO: Partial<ChainDAO>;
|
||||||
|
let imageStorage: Partial<ImageStorage>;
|
||||||
|
let router: Partial<Router>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
chainDAO = {
|
||||||
|
insert: vi.fn().mockResolvedValue({
|
||||||
|
rows: [],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
imageStorage = {
|
||||||
|
saveImage: vi.fn().mockResolvedValue('mock.jpg'),
|
||||||
|
};
|
||||||
|
|
||||||
|
router = {
|
||||||
|
navigate: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ChainAdd],
|
||||||
|
providers: [
|
||||||
|
provideTranslateService(),
|
||||||
|
{ provide: ChainDAO, useValue: chainDAO },
|
||||||
|
{ provide: ImageStorage, useValue: imageStorage },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ChainAdd);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', async () => {
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert chain and store image', async () => {
|
||||||
|
const saveSpy = vi.spyOn(component, 'save');
|
||||||
|
(<any>component).form = new ChainFormGroup({
|
||||||
|
name: new FormControl('Mock'),
|
||||||
|
image: new FormControl(new File([], 'mock')),
|
||||||
|
});
|
||||||
|
await fixture.whenStable();
|
||||||
|
const actionBtn = fixture.debugElement.query(By.css('app-action-btn'));
|
||||||
|
actionBtn.triggerEventHandler('click');
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(saveSpy).toHaveBeenCalled();
|
||||||
|
expect(imageStorage.saveImage).toHaveBeenCalledOnce();
|
||||||
|
expect(chainDAO.insert).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
});
|
||||||
45
src/app/pages/settings/chains/chain-add/chain-add.ts
Normal file
45
src/app/pages/settings/chains/chain-add/chain-add.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { ImageStorage } from '../../../../services/image-storage';
|
||||||
|
import { ChainDAO } from '../../../../dao/ChainDAO';
|
||||||
|
import { Chain } from '../../../../models/Chain';
|
||||||
|
import { ChainFormGroup } from '../chain-formgroup';
|
||||||
|
import { ChainForm } from "../chain-form/chain-form";
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { ActionBtn } from "../../../../components/action-btn/action-btn";
|
||||||
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
|
import { UpperfirstPipe } from "../../../../pipes/upperfirst-pipe";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-chain-add',
|
||||||
|
imports: [MatFormFieldModule, MatInputModule, ReactiveFormsModule, ChainForm, ActionBtn, TranslatePipe, UpperfirstPipe],
|
||||||
|
templateUrl: './chain-add.html',
|
||||||
|
styleUrl: './chain-add.scss',
|
||||||
|
})
|
||||||
|
export class ChainAdd {
|
||||||
|
private readonly chainDAO = inject(ChainDAO);
|
||||||
|
private readonly imageStorage = inject(ImageStorage);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
readonly form = new ChainFormGroup
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
const name = this.form.controls.name.value;
|
||||||
|
const img = this.form.controls.image.value;
|
||||||
|
try {
|
||||||
|
//TODO: the sqlite bridge can't handle null as param
|
||||||
|
const chain = new Chain(name, '');
|
||||||
|
if (img) {
|
||||||
|
const imgFileName = await this.imageStorage.saveImage(img);
|
||||||
|
chain.image = imgFileName;
|
||||||
|
}
|
||||||
|
await this.chainDAO.insert(chain);
|
||||||
|
this.router.navigate(['settings', 'chains'])
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
//TODO: report problem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
9
src/app/pages/settings/chains/chain-edit/chain-edit.html
Normal file
9
src/app/pages/settings/chains/chain-edit/chain-edit.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<h3>{{'settings.chain.edit_chain'|translate|upperfirst}}</h3>
|
||||||
|
<app-chain-form [form]="form"/>
|
||||||
|
<app-action-btn
|
||||||
|
(click)="update()"
|
||||||
|
(keydown)="update()"
|
||||||
|
[disabled]="this.disabled"
|
||||||
|
class="top-auto"
|
||||||
|
text="{{'common.update'|translate|upperfirst}}"
|
||||||
|
/>
|
||||||
5
src/app/pages/settings/chains/chain-edit/chain-edit.scss
Normal file
5
src/app/pages/settings/chains/chain-edit/chain-edit.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
102
src/app/pages/settings/chains/chain-edit/chain-edit.spec.ts
Normal file
102
src/app/pages/settings/chains/chain-edit/chain-edit.spec.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ChainEdit } from './chain-edit';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { Chain } from '../../../../models/Chain';
|
||||||
|
import { provideTranslateService } from '@ngx-translate/core';
|
||||||
|
import { ChainDAO } from '../../../../dao/ChainDAO';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { ImageStorage } from '../../../../services/image-storage';
|
||||||
|
|
||||||
|
const CHAINS_PATH = ['settings', 'chains'];
|
||||||
|
const CHAIN_MOCK = new Chain('Mock', '', 1);
|
||||||
|
describe('ChainEdit', () => {
|
||||||
|
let component: ChainEdit;
|
||||||
|
let fixture: ComponentFixture<ChainEdit>;
|
||||||
|
|
||||||
|
let activatedRoute: Partial<ActivatedRoute>;
|
||||||
|
let chainDAO: Partial<ChainDAO>;
|
||||||
|
let imageStorage: Partial<ImageStorage>;
|
||||||
|
let router: Partial<Router>;
|
||||||
|
|
||||||
|
const dataSubject = new BehaviorSubject({ chain: CHAIN_MOCK });
|
||||||
|
beforeEach(async () => {
|
||||||
|
activatedRoute = {
|
||||||
|
data: dataSubject,
|
||||||
|
};
|
||||||
|
chainDAO = {
|
||||||
|
update: vi.fn().mockResolvedValue({
|
||||||
|
rows: [],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
imageStorage = {
|
||||||
|
getImage: vi.fn(),
|
||||||
|
saveImage: vi.fn(),
|
||||||
|
deleteImage: vi.fn(),
|
||||||
|
};
|
||||||
|
router = {
|
||||||
|
navigate: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ChainEdit],
|
||||||
|
providers: [
|
||||||
|
provideTranslateService(),
|
||||||
|
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||||
|
{ provide: ChainDAO, useValue: chainDAO },
|
||||||
|
{ provide: ImageStorage, useValue: imageStorage },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ChainEdit);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', async () => {
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on form update', () => {
|
||||||
|
it('should update chain name after modifying name', async () => {
|
||||||
|
await fixture.whenStable();
|
||||||
|
const chainNameInput = fixture.debugElement.query(By.css('[formControlName="name"]'));
|
||||||
|
chainNameInput.triggerEventHandler('input', { target: { value: 'name update mock' } });
|
||||||
|
fixture.whenStable();
|
||||||
|
const actionBtn = fixture.debugElement.query(By.css('app-action-btn'));
|
||||||
|
actionBtn.triggerEventHandler('click');
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(chainDAO.update).toHaveBeenCalledExactlyOnceWith(component['chain'], {
|
||||||
|
id: CHAIN_MOCK.id,
|
||||||
|
});
|
||||||
|
expect(router.navigate).toHaveBeenCalledExactlyOnceWith(CHAINS_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update chain image after modifying image', async () => {
|
||||||
|
const NEW_IMG_MOCK = new File([], 'new_img_mock.png', { type: 'image/png' });
|
||||||
|
const CHAIN_MOCK = new Chain('Mock', 'img_mock.jpeg', 1);
|
||||||
|
imageStorage.getImage = vi
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue(new File([], CHAIN_MOCK.image!, { type: 'image/jpeg' }));
|
||||||
|
imageStorage.saveImage = vi.fn().mockResolvedValue(NEW_IMG_MOCK.name);
|
||||||
|
|
||||||
|
dataSubject.next({ chain: CHAIN_MOCK });
|
||||||
|
await fixture.whenStable();
|
||||||
|
//User's select a new image
|
||||||
|
component.form.controls.image.patchValue(NEW_IMG_MOCK);
|
||||||
|
await fixture.whenStable();
|
||||||
|
const actionBtn = fixture.debugElement.query(By.css('app-action-btn'));
|
||||||
|
actionBtn.triggerEventHandler('click');
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(imageStorage.getImage).toHaveBeenCalledWith(CHAIN_MOCK.image);
|
||||||
|
expect(imageStorage.saveImage).toHaveBeenCalledExactlyOnceWith(NEW_IMG_MOCK);
|
||||||
|
expect(imageStorage.deleteImage).toHaveBeenCalledExactlyOnceWith(CHAIN_MOCK.image);
|
||||||
|
expect(chainDAO.update).toHaveBeenCalledExactlyOnceWith(component['chain'], {
|
||||||
|
id: CHAIN_MOCK.id,
|
||||||
|
});
|
||||||
|
expect(router.navigate).toHaveBeenCalledExactlyOnceWith(CHAINS_PATH);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
81
src/app/pages/settings/chains/chain-edit/chain-edit.ts
Normal file
81
src/app/pages/settings/chains/chain-edit/chain-edit.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
||||||
|
import { ChainFormGroup } from '../chain-formgroup';
|
||||||
|
import { ChainForm } from '../chain-form/chain-form';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Observable, take, tap } from 'rxjs';
|
||||||
|
import { Chain } from '../../../../models/Chain';
|
||||||
|
import { ImageStorage } from '../../../../services/image-storage';
|
||||||
|
import { ActionBtn } from '../../../../components/action-btn/action-btn';
|
||||||
|
import { ChainDAO } from '../../../../dao/ChainDAO';
|
||||||
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
|
import { UpperfirstPipe } from "../../../../pipes/upperfirst-pipe";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-chain-edit',
|
||||||
|
imports: [ChainForm, ActionBtn, TranslatePipe, UpperfirstPipe],
|
||||||
|
templateUrl: './chain-edit.html',
|
||||||
|
styleUrl: './chain-edit.scss',
|
||||||
|
})
|
||||||
|
export class ChainEdit implements OnInit {
|
||||||
|
readonly form = new ChainFormGroup();
|
||||||
|
protected readonly activatedRoute = inject(ActivatedRoute);
|
||||||
|
private readonly cd = inject(ChangeDetectorRef);
|
||||||
|
private chain?: Chain;
|
||||||
|
private readonly chainDAO = inject(ChainDAO);
|
||||||
|
private readonly imageStorage = inject(ImageStorage);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
(<Observable<{ chain: Chain }>>this.activatedRoute.data)
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
tap((data) => (this.chain = new Chain(data.chain.name, data.chain.image, data.chain.id))),
|
||||||
|
tap(async (data) => await this.patchForm(data.chain)),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return (this.form.invalid || this.form.pristine)
|
||||||
|
}
|
||||||
|
|
||||||
|
async patchForm(chain: Chain) {
|
||||||
|
try {
|
||||||
|
this.form.controls.name.patchValue(chain.name);
|
||||||
|
if (chain.image) {
|
||||||
|
const imgName = chain.image;
|
||||||
|
const blob = await this.imageStorage.getImage(imgName);
|
||||||
|
const file = new File([blob], imgName, { type: blob.type });
|
||||||
|
this.form.controls.image.patchValue(file);
|
||||||
|
this.cd.detectChanges();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
//TODO: reportar error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
try {
|
||||||
|
if (this.chain) {
|
||||||
|
this.chain.name = this.form.controls.name.value;
|
||||||
|
const formImgFile = this.form.controls.image.value;
|
||||||
|
let imgName = this.chain.name;
|
||||||
|
if (formImgFile) {
|
||||||
|
if (formImgFile.name !== imgName) {
|
||||||
|
imgName = await this.imageStorage.saveImage(formImgFile);
|
||||||
|
if (this.chain.image) {
|
||||||
|
this.imageStorage.deleteImage(this.chain.image);
|
||||||
|
}
|
||||||
|
this.chain.image = imgName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.chainDAO.update(this.chain, { id: this.chain.id });
|
||||||
|
this.router.navigate(['settings', 'chains']);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
//TODO: reportar error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/app/pages/settings/chains/chain-list/chain-list.html
Normal file
2
src/app/pages/settings/chains/chain-list/chain-list.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<app-simple-list-w-actions [items]="(chains$|async) ?? []" (action)="edit($event)"/>
|
||||||
|
<app-floating-big-btn icon="add" (bigClick)="add()"/>
|
||||||
61
src/app/pages/settings/chains/chain-list/chain-list.spec.ts
Normal file
61
src/app/pages/settings/chains/chain-list/chain-list.spec.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ChainList } from './chain-list';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { Chain } from '../../../../models/Chain';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
const ADD_PATH = ['settings', 'chains', 'add'];
|
||||||
|
const EDIT_PATH = ['settings', 'chains', 'edit'];
|
||||||
|
describe('ChainList', () => {
|
||||||
|
let component: ChainList;
|
||||||
|
let fixture: ComponentFixture<ChainList>;
|
||||||
|
|
||||||
|
let activatedRoute: Partial<ActivatedRoute>;
|
||||||
|
let router: Partial<Router>;
|
||||||
|
|
||||||
|
let EDIT_PATH_MOCK: string[];
|
||||||
|
|
||||||
|
const CHAIN_MOCK = new Chain('Mock', '', 1);
|
||||||
|
const dataSubject = new BehaviorSubject({ chains: [CHAIN_MOCK] });
|
||||||
|
beforeEach(async () => {
|
||||||
|
activatedRoute = {
|
||||||
|
data: dataSubject,
|
||||||
|
};
|
||||||
|
|
||||||
|
router = {
|
||||||
|
navigate: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
EDIT_PATH_MOCK = [...EDIT_PATH, String(CHAIN_MOCK.id)];
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ChainList],
|
||||||
|
providers: [
|
||||||
|
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ChainList);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to edit on edit action', () => {
|
||||||
|
const editBtn = fixture.debugElement.query(By.css('[matIconButton]'));
|
||||||
|
editBtn.triggerEventHandler('click');
|
||||||
|
expect(router.navigate).toHaveBeenCalledExactlyOnceWith(EDIT_PATH_MOCK);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to add on add btn click', () => {
|
||||||
|
const addBtn = fixture.debugElement.query(By.css('app-floating-big-btn'));
|
||||||
|
addBtn.triggerEventHandler('bigClick');
|
||||||
|
expect(router.navigate).toHaveBeenCalledExactlyOnceWith(ADD_PATH);
|
||||||
|
});
|
||||||
|
});
|
||||||
40
src/app/pages/settings/chains/chain-list/chain-list.ts
Normal file
40
src/app/pages/settings/chains/chain-list/chain-list.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { SimpleListWActions } from '../../../../components/simple-list-w-actions/simple-list-w-actions';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { map } from 'rxjs';
|
||||||
|
import { SimpleListItem } from '../../../../components/simple-list-w-actions/SimpleListItem';
|
||||||
|
import { SimpleListItemAction } from '../../../../components/simple-list-w-actions/SimpleListItemAction';
|
||||||
|
import { Chain } from '../../../../models/Chain';
|
||||||
|
import { AsyncPipe } from '@angular/common';
|
||||||
|
import { FloatingBigBtn } from "../../../../components/floating-big-btn/floating-big-btn";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-chain-list',
|
||||||
|
imports: [SimpleListWActions, AsyncPipe, FloatingBigBtn],
|
||||||
|
templateUrl: './chain-list.html',
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class ChainList {
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
protected readonly activatedRoute = inject(ActivatedRoute);
|
||||||
|
|
||||||
|
chains$ = this.activatedRoute.data.pipe(
|
||||||
|
map((data) =>
|
||||||
|
(<Chain[]>data['chains']).map(
|
||||||
|
(c, i) =>
|
||||||
|
new SimpleListItem(String(c.id), c.name ?? '', [new SimpleListItemAction('edit', 'edit')]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
protected edit(action: {
|
||||||
|
action: string;
|
||||||
|
subject: string;
|
||||||
|
}) {
|
||||||
|
this.router.navigate(['settings', 'chains', 'edit', action.subject])
|
||||||
|
}
|
||||||
|
|
||||||
|
protected add() {
|
||||||
|
this.router.navigate(['settings', 'chains', 'add']);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/app/pages/settings/chains/chains.html
Normal file
3
src/app/pages/settings/chains/chains.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<app-simple-layout title="settings.chain.chains" [withBackBtn]="true" >
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</app-simple-layout>
|
||||||
4
src/app/pages/settings/chains/chains.scss
Normal file
4
src/app/pages/settings/chains/chains.scss
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
24
src/app/pages/settings/chains/chains.spec.ts
Normal file
24
src/app/pages/settings/chains/chains.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { Chains } from './chains';
|
||||||
|
import { provideTranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
describe('Chains', () => {
|
||||||
|
let component: Chains;
|
||||||
|
let fixture: ComponentFixture<Chains>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [Chains],
|
||||||
|
providers: [provideTranslateService({})]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(Chains);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
13
src/app/pages/settings/chains/chains.ts
Normal file
13
src/app/pages/settings/chains/chains.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { MatListModule } from '@angular/material/list';
|
||||||
|
import { SimpleLayout } from '../../../components/simple-layout/simple-layout';
|
||||||
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-chains',
|
||||||
|
imports: [MatListModule, SimpleLayout, RouterOutlet],
|
||||||
|
templateUrl: './chains.html',
|
||||||
|
styleUrl: './chains.scss',
|
||||||
|
})
|
||||||
|
export class Chains {}
|
||||||
Reference in New Issue
Block a user