TSK-1711: Task routing upload (#1729)
* TSK-1711: create task-routing module, routing upload component, file drag and drop directive * TSK-1711: update drag and drop hover effect * Update routing-upload.service.ts * TSK-1711: Implement integration with backend * TSK-1711: fix linting and tests * TSK-1711: Add guards for task-routing Route, update REST endpoints * TSK-1711: fix unit tests breaking * TSK-1711: should not display unknown error for 404
This commit is contained in:
parent
2892d4a2cb
commit
d922d8a178
|
@ -65,6 +65,11 @@ const routes: Routes = [
|
|||
redirectTo: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'task-routing',
|
||||
canActivate: [DomainGuard],
|
||||
loadChildren: () => import('../task-routing/task-routing.module').then((m) => m.TaskRoutingModule)
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
<a mat-tab-link class="administration-overview__navbar-links" routerLink="/taskana/administration/access-items-management"
|
||||
[active]="selectedTab == 'access-items-management'" (click)="selectedTab = 'access-items-management'">Access Items
|
||||
Management</a>
|
||||
<a mat-tab-link class="administration-overview__navbar-links" routerLink="/taskana/administration/task-routing"
|
||||
[active]="selectedTab == 'task-routing'" (click)="selectedTab = 'task-routing'" *ngIf="(routingAccess$ | async)">Task Routing</a>
|
||||
</nav>
|
||||
<div class="administration-overview__domain">
|
||||
<mat-form-field appearance="legacy">
|
||||
|
|
|
@ -8,6 +8,7 @@ import { DomainService } from '../../../shared/services/domain/domain.service';
|
|||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TaskanaEngineService } from '../../../shared/services/taskana-engine/taskana-engine.service';
|
||||
|
||||
const domainServiceSpy: Partial<DomainService> = {
|
||||
getDomains: jest.fn().mockReturnValue(of(['domain a', 'domain b'])),
|
||||
|
@ -30,7 +31,7 @@ describe('AdministrationOverviewComponent', () => {
|
|||
BrowserAnimationsModule
|
||||
],
|
||||
declarations: [AdministrationOverviewComponent],
|
||||
providers: [{ provide: DomainService, useValue: domainServiceSpy }]
|
||||
providers: [{ provide: DomainService, useValue: domainServiceSpy }, TaskanaEngineService]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { Observable, of, Subject } from 'rxjs';
|
||||
import { DomainService } from '../../../shared/services/domain/domain.service';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { TaskanaEngineService } from '../../../shared/services/taskana-engine/taskana-engine.service';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-administration-overview',
|
||||
|
@ -16,8 +17,13 @@ export class AdministrationOverviewComponent implements OnInit {
|
|||
|
||||
destroy$ = new Subject<void>();
|
||||
url$: Observable<any>;
|
||||
routingAccess$: Observable<boolean> = of(false);
|
||||
|
||||
constructor(private router: Router, private domainService: DomainService) {
|
||||
constructor(
|
||||
private router: Router,
|
||||
private domainService: DomainService,
|
||||
private taskanaEngineService: TaskanaEngineService
|
||||
) {
|
||||
router.events.pipe(takeUntil(this.destroy$)).subscribe((e) => {
|
||||
const urlPaths = this.router.url.split('/');
|
||||
if (this.router.url.includes('detail')) {
|
||||
|
@ -29,6 +35,8 @@ export class AdministrationOverviewComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.routingAccess$ = this.taskanaEngineService.isCustomRoutingRulesEnabled$;
|
||||
this.routingAccess$.pipe(takeUntil(this.destroy$)).subscribe();
|
||||
this.domainService
|
||||
.getDomains()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
(click)="toggleSidenav()">Classifications</a>
|
||||
<a mat-list-item class="navlist__item navlist__admin-access-items" [routerLink]=[accessUrl]
|
||||
[routerLinkActive]="['active__sub']" (click)="toggleSidenav()" *ngIf="administrationAccess">Access Items</a>
|
||||
<a mat-list-item class="navlist__item navlist__admin-task-routing" [routerLink]=[routingUrl]
|
||||
[routerLinkActive]="['active__sub']" (click)="toggleSidenav()" *ngIf="(routingAccess$ | async)">Task Routing</a>
|
||||
</div>
|
||||
<a mat-list-item class="navlist__item navlist__monitor" [routerLink]=[monitorUrl] [routerLinkActive]="['active']"
|
||||
*ngIf="monitorAccess" (click)="toggleSidenav()">Monitor</a>
|
||||
|
|
|
@ -8,7 +8,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|||
import { RouterModule } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { TaskanaEngineService } from '../../services/taskana-engine/taskana-engine.service';
|
||||
import { TaskanaEngineServiceMock } from '../../services/taskana-engine/taskana-engine.mock.service';
|
||||
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
|
@ -22,9 +21,10 @@ const SidenavServiceSpy: Partial<SidenavService> = {
|
|||
toggleSidenav: jest.fn().mockReturnValue(EMPTY)
|
||||
};
|
||||
|
||||
const TaskanaEngineServiceSpy: Partial<TaskanaEngineServiceMock> = {
|
||||
const TaskanaEngineServiceSpy: Partial<TaskanaEngineService> = {
|
||||
hasRole: jest.fn().mockReturnValue(EMPTY),
|
||||
isHistoryProviderEnabled: jest.fn().mockReturnValue(EMPTY)
|
||||
isHistoryProviderEnabled: jest.fn().mockReturnValue(EMPTY),
|
||||
isCustomRoutingRulesEnabled$: EMPTY
|
||||
};
|
||||
|
||||
describe('SidenavListComponent', () => {
|
||||
|
|
|
@ -4,6 +4,8 @@ import { MonitorGuard } from 'app/shared/guards/monitor.guard';
|
|||
import { UserGuard } from 'app/shared/guards/user.guard';
|
||||
import { TaskanaEngineService } from '../../services/taskana-engine/taskana-engine.service';
|
||||
import { SidenavService } from '../../services/sidenav/sidenav.service';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-sidenav-list',
|
||||
|
@ -17,6 +19,7 @@ export class SidenavListComponent implements OnInit {
|
|||
workplaceUrl = 'taskana/workplace';
|
||||
historyUrl = 'taskana/history';
|
||||
accessUrl = 'taskana/administration/access-items-management';
|
||||
routingUrl = 'taskana/administration/task-routing';
|
||||
classificationUrl = 'taskana/administration/classifications';
|
||||
workbasketsUrl = 'taskana/administration/workbaskets';
|
||||
administrationsUrl = 'taskana/administration/workbaskets';
|
||||
|
@ -26,6 +29,7 @@ export class SidenavListComponent implements OnInit {
|
|||
monitorAccess = false;
|
||||
workplaceAccess = false;
|
||||
historyAccess = false;
|
||||
routingAccess$: Observable<boolean> = of(false);
|
||||
settingsAccess = false;
|
||||
|
||||
constructor(private taskanaEngineService: TaskanaEngineService, private sidenavService: SidenavService) {}
|
||||
|
@ -37,6 +41,8 @@ export class SidenavListComponent implements OnInit {
|
|||
this.taskanaEngineService.isHistoryProviderEnabled().subscribe((value) => {
|
||||
this.historyAccess = value;
|
||||
});
|
||||
this.routingAccess$ = this.taskanaEngineService.isCustomRoutingRulesEnabled$;
|
||||
this.routingAccess$.subscribe();
|
||||
this.settingsAccess = this.administrationAccess;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { DragAndDropDirective } from './drag-and-drop.directive';
|
||||
|
||||
describe('DragAndDropDirective', () => {
|
||||
it('should create an instance', () => {
|
||||
const directive = new DragAndDropDirective();
|
||||
expect(directive).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
import { Directive, HostListener, Output, EventEmitter, HostBinding } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[taskanaDragAndDrop]'
|
||||
})
|
||||
export class DragAndDropDirective {
|
||||
@Output() onFileDropped = new EventEmitter<any>();
|
||||
@HostBinding('class.fileover') fileOver: boolean;
|
||||
|
||||
//Dragover listener
|
||||
@HostListener('dragover', ['$event']) onDragOver(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.fileOver = true;
|
||||
}
|
||||
|
||||
//Dragleave listener
|
||||
@HostListener('dragleave', ['$event']) public onDragLeave(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.fileOver = false;
|
||||
}
|
||||
|
||||
//Drop listener
|
||||
@HostListener('drop', ['$event']) public ondrop(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.fileOver = false;
|
||||
let files = evt.dataTransfer.files;
|
||||
if (files.length > 0) {
|
||||
this.onFileDropped.emit(files);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { TaskanaEngineService } from '../services/taskana-engine/taskana-engine.service';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TaskRoutingGuard implements CanActivate {
|
||||
constructor(private taskanaEngineService: TaskanaEngineService, public router: Router) {}
|
||||
|
||||
canActivate(
|
||||
next: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
): Observable<boolean> | Promise<boolean> | boolean {
|
||||
return this.taskanaEngineService.isCustomRoutingRulesEnabled$.pipe(
|
||||
map((value) => {
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
return this.navigateToWorkplace();
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(this.navigateToWorkplace());
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
navigateToWorkplace(): boolean {
|
||||
this.router.navigate(['workplace']);
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ import {
|
|||
HttpErrorResponse,
|
||||
HttpEvent,
|
||||
HttpHandler,
|
||||
HttpHeaders,
|
||||
HttpInterceptor,
|
||||
HttpRequest,
|
||||
HttpXsrfTokenExtractor
|
||||
|
@ -22,7 +23,13 @@ export class HttpClientInterceptor implements HttpInterceptor {
|
|||
) {}
|
||||
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
let req = request.clone({ setHeaders: { 'Content-Type': 'application/hal+json' } });
|
||||
let req = request.clone();
|
||||
if (req.headers.get('Content-Type') === 'multipart/form-data') {
|
||||
const headers = new HttpHeaders();
|
||||
req = req.clone({ headers });
|
||||
} else {
|
||||
req = req.clone({ setHeaders: { 'Content-Type': 'application/hal+json' } });
|
||||
}
|
||||
let token = this.tokenExtractor.getToken() as string;
|
||||
if (token !== null) {
|
||||
req = req.clone({ setHeaders: { 'X-XSRF-TOKEN': token } });
|
||||
|
@ -36,9 +43,8 @@ export class HttpClientInterceptor implements HttpInterceptor {
|
|||
(error) => {
|
||||
this.requestInProgressService.setRequestInProgress(false);
|
||||
if (
|
||||
error.status !== 404 ||
|
||||
!(error instanceof HttpErrorResponse) ||
|
||||
error.url.indexOf('environment-information.json') === -1
|
||||
error.status !== 404 &&
|
||||
(!(error instanceof HttpErrorResponse) || error.url.indexOf('environment-information.json') === -1)
|
||||
) {
|
||||
const { key, messageVariables } = error.error.error || {
|
||||
key: 'FALLBACK',
|
||||
|
|
|
@ -4,12 +4,11 @@ import { environment } from 'environments/environment';
|
|||
import { UserInfo } from 'app/shared/models/user-info';
|
||||
import { Version } from 'app/shared/models/version';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class TaskanaEngineService {
|
||||
currentUserInfo: UserInfo;
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
// GET
|
||||
|
@ -43,6 +42,10 @@ export class TaskanaEngineService {
|
|||
return this.httpClient.get<boolean>(`${environment.taskanaRestUrl}/v1/history-provider-enabled`);
|
||||
}
|
||||
|
||||
isCustomRoutingRulesEnabled$ = this.httpClient
|
||||
.get<boolean>(`${environment.taskanaRestUrl}/v1/routing-rules/routing-rest-enabled`)
|
||||
.pipe(shareReplay(1));
|
||||
|
||||
private findRole(roles2Find: string[]) {
|
||||
return this.currentUserInfo.roles.find((role) => roles2Find.some((roleLookingFor) => role === roleLookingFor));
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.ser
|
|||
import { ClassificationsService } from 'app/shared/services/classifications/classifications.service';
|
||||
import { ObtainMessageService } from './services/obtain-message/obtain-message.service';
|
||||
import { AccessIdsService } from './services/access-ids/access-ids.service';
|
||||
import { DragAndDropDirective } from './directives/drag-and-drop.directive';
|
||||
import { GermanTimeFormatPipe } from './pipes/german-time-format.pipe';
|
||||
|
||||
const MODULES = [
|
||||
|
@ -101,6 +102,7 @@ const DECLARATIONS = [
|
|||
DialogPopUpComponent,
|
||||
WorkbasketFilterComponent,
|
||||
TaskFilterComponent,
|
||||
DragAndDropDirective,
|
||||
GermanTimeFormatPipe
|
||||
];
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RoutingUploadService } from './routing-upload.service';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { StartupService } from '../shared/services/startup/startup.service';
|
||||
import { TaskanaEngineService } from '../shared/services/taskana-engine/taskana-engine.service';
|
||||
import { WindowRefService } from '../shared/services/window/window.service';
|
||||
|
||||
describe('RoutingUploadService', () => {
|
||||
let service: RoutingUploadService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [StartupService, TaskanaEngineService, WindowRefService]
|
||||
});
|
||||
service = TestBed.inject(RoutingUploadService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { StartupService } from '../shared/services/startup/startup.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RoutingUploadService {
|
||||
constructor(private httpClient: HttpClient, private startupService: StartupService) {}
|
||||
|
||||
get url(): string {
|
||||
return this.startupService.getTaskanaRestUrl() + '/v1/routing-rules/default/';
|
||||
}
|
||||
|
||||
uploadRoutingRules(file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('excelRoutingFile', file);
|
||||
const headers = new HttpHeaders().set('Content-Type', 'multipart/form-data');
|
||||
return this.httpClient.put(this.url, formData, { headers });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<div class="routing-upload">
|
||||
|
||||
<h1>Upload custom routing rules</h1>
|
||||
<div taskanaDragAndDrop (onFileDropped)="upload($event)" class="routing-upload__upload-area"
|
||||
(click)="input.click()">
|
||||
<svg class="routing-upload__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg>
|
||||
<span class="routing-upload__description" *ngIf="!file?.name">Click to choose file or drag and drop here</span>
|
||||
<span class="routing-upload__description" *ngIf="file?.name">{{file?.name}}</span>
|
||||
</div>
|
||||
<input #input style="display: none;"
|
||||
id="routingUpload"
|
||||
type="file"
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
title="Upload task routing Excel File"
|
||||
(change)="onFileChanged($event)">
|
||||
</div>
|
|
@ -0,0 +1,45 @@
|
|||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.fileover {
|
||||
border: 4px solid $green;
|
||||
}
|
||||
|
||||
.routing-upload {
|
||||
width: 100%;
|
||||
height: calc(100vh - 104px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: -68px;
|
||||
|
||||
> h1 {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
&__upload-area {
|
||||
height: 400px;
|
||||
width: 400px;
|
||||
padding: 16px;
|
||||
background-color: $light-grey;
|
||||
border: 4px solid $light-grey;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
&__upload-area:hover {
|
||||
border: 4px solid $green;
|
||||
}
|
||||
&__icon {
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
color: $grey;
|
||||
}
|
||||
&__description {
|
||||
margin-top: 16px;
|
||||
color: $grey;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RoutingUploadComponent } from './routing-upload.component';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { StartupService } from '../../shared/services/startup/startup.service';
|
||||
import { TaskanaEngineService } from '../../shared/services/taskana-engine/taskana-engine.service';
|
||||
import { WindowRefService } from '../../shared/services/window/window.service';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { DragAndDropDirective } from '../../shared/directives/drag-and-drop.directive';
|
||||
|
||||
describe('RoutingUploadComponent', () => {
|
||||
let component: RoutingUploadComponent;
|
||||
let fixture: ComponentFixture<RoutingUploadComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [RoutingUploadComponent, DragAndDropDirective],
|
||||
imports: [HttpClientTestingModule, MatDialogModule],
|
||||
providers: [StartupService, TaskanaEngineService, WindowRefService]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RoutingUploadComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { RoutingUploadService } from '../routing-upload.service';
|
||||
import { NotificationService } from '../../shared/services/notifications/notification.service';
|
||||
import { HotToastService } from '@ngneat/hot-toast';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-routing-upload',
|
||||
templateUrl: './routing-upload.component.html',
|
||||
styleUrls: ['./routing-upload.component.scss']
|
||||
})
|
||||
export class RoutingUploadComponent implements OnInit {
|
||||
file: File | null = null;
|
||||
|
||||
constructor(
|
||||
private routingUploadService: RoutingUploadService,
|
||||
private toastService: HotToastService,
|
||||
private notificationService: NotificationService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
onFileChanged(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (!input.files?.length) return;
|
||||
this.file = input.files[0];
|
||||
this.upload(input.files);
|
||||
}
|
||||
|
||||
upload(fileList?: FileList) {
|
||||
if (typeof fileList !== 'undefined') {
|
||||
this.file = fileList[0];
|
||||
}
|
||||
this.routingUploadService.uploadRoutingRules(this.file).subscribe({
|
||||
next: (res: { amountOfImportedRow: number; result: string }) => this.toastService.success(res.result),
|
||||
error: (err) => {
|
||||
this.notificationService.showError(err.error.message.key);
|
||||
this.clearInput();
|
||||
},
|
||||
complete: () => this.clearInput()
|
||||
});
|
||||
}
|
||||
|
||||
clearInput() {
|
||||
const inputElement = document.getElementById('routingUpload') as HTMLInputElement;
|
||||
inputElement.value = '';
|
||||
this.file = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { RoutingUploadComponent } from './routing-upload/routing-upload.component';
|
||||
import { TaskRoutingGuard } from '../shared/guards/task-routing.guard';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
canActivate: [TaskRoutingGuard],
|
||||
component: RoutingUploadComponent
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class TaskRoutingRoutingModule {}
|
|
@ -0,0 +1,12 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { TaskRoutingRoutingModule } from './task-routing-routing.module';
|
||||
import { RoutingUploadComponent } from './routing-upload/routing-upload.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [RoutingUploadComponent],
|
||||
imports: [CommonModule, TaskRoutingRoutingModule, SharedModule]
|
||||
})
|
||||
export class TaskRoutingModule {}
|
Loading…
Reference in New Issue