Add modules and lazy loading

- core modules contains core services
- shared module contains "dumb" components (directives, models)
  - used by nearly all modules
- site, it's children and projector are now feature modules
  - full lazy loading with independent routing
  - routing for children (extremely helpful for plugins (later))
This commit is contained in:
Sean Engelhardt 2018-07-23 16:42:17 +02:00 committed by FinnStutzenstein
parent 6b09427565
commit 76ce18cfd8
94 changed files with 790 additions and 216 deletions

View File

@ -3,7 +3,7 @@
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json --host=0.0.0.0", "start": "ng serve --proxy-config proxy.conf.json --host=0.0.0.0 --aot",
"build": "ng build", "build": "ng build",
"test": "ng test", "test": "ng test",
"lint": "ng lint", "lint": "ng lint",

View File

@ -1,30 +1,15 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './site/login/login.component'; import { LoginComponent } from './site/login/login.component';
import { ProjectorComponent } from './projector-container/projector/projector.component';
import { ProjectorContainerComponent } from './projector-container/projector-container.component';
import { SiteComponent } from './site/site.component';
import { StartComponent } from './site/start/start.component';
import { AgendaComponent } from './site/agenda/agenda.component';
import { MotionsComponent } from './site/motions/motions.component';
import { AuthGuard } from './core/services/auth-guard.service'; import { AuthGuard } from './core/services/auth-guard.service';
/**
* Global app routing
*/
const routes: Routes = [ const routes: Routes = [
{ path: 'projector/:id', component: ProjectorComponent },
{ path: 'real-projector/:id', component: ProjectorContainerComponent },
{ path: 'login', component: LoginComponent }, { path: 'login', component: LoginComponent },
{ { path: 'projector', loadChildren: './projector-container/projector-container.module#ProjectorContainerModule' },
path: '', { path: '', loadChildren: './site/site.module#SiteModule', canActivate: [AuthGuard] },
component: SiteComponent,
canActivate: [AuthGuard],
children: [
{ path: '', component: StartComponent },
{ path: 'agenda', component: AgendaComponent },
{ path: 'motions', component: MotionsComponent }
]
},
{ path: '**', redirectTo: '' } { path: '**', redirectTo: '' }
]; ];

View File

@ -1,7 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { AutoupdateService } from 'app/core/services/autoupdate.service';
import { OperatorService } from 'app/core/services/operator.service';
/** /**
* Angular's global App Component * Angular's global App Component
@ -18,11 +16,7 @@ export class AppComponent {
* @param autoupdate * @param autoupdate
* @param translate * @param translate
*/ */
constructor( constructor(private translate: TranslateService) {
private operator: OperatorService,
private autoupdate: AutoupdateService,
private translate: TranslateService
) {
// manually add the supported languages // manually add the supported languages
translate.addLangs(['en', 'de', 'fr']); translate.addLangs(['en', 'de', 'fr']);
// this language will be used as a fallback when a translation isn't found in the current language // this language will be used as a fallback when a translation isn't found in the current language

View File

@ -1,54 +1,18 @@
// angular modules // angular modules
import { BrowserModule, Title } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { HttpClientModule, HttpClient, HttpClientXsrfModule } from '@angular/common/http';
import { HttpClientModule, HttpClient, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http';
// MaterialUI modules // Elementary App Components
import {
MatButtonModule,
MatCheckboxModule,
MatToolbarModule,
MatCardModule,
MatInputModule,
MatProgressSpinnerModule,
MatSidenavModule,
MatSnackBarModule
} from '@angular/material';
import { MatListModule } from '@angular/material/list';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatMenuModule } from '@angular/material/menu';
// FontAwesome modules
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
// App components and services
import { AppComponent } from './app.component';
import { LoginComponent } from './site/login/login.component';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { ProjectorComponent } from './projector-container/projector/projector.component'; import { AppComponent } from './app.component';
import { MotionsComponent } from './site/motions/motions.component'; import { CoreModule } from './core/core.module';
import { AgendaComponent } from './site/agenda/agenda.component';
import { SiteComponent } from './site/site.component';
import { StartComponent } from './site/start/start.component';
import { AddHeaderInterceptor } from './core/http-interceptor';
import { ProjectorContainerComponent } from './projector-container/projector-container.component';
// Root Services
import { AuthGuard } from './core/services/auth-guard.service';
import { AuthService } from './core/services/auth.service';
import { AutoupdateService } from './core/services/autoupdate.service';
import { DataStoreService } from './core/services/dataStore.service';
import { OperatorService } from './core/services/operator.service';
import { WebsocketService } from './core/services/websocket.service';
// translation module. // translation module.
import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { PruningTranslationLoader } from './core/pruning-loader'; import { PruningTranslationLoader } from './core/pruning-loader';
import { OsPermsDirective } from './core/directives/os-perms.directive'; import { LoginModule } from './site/login/login.module';
/** /**
* For the translation module. Loads a Custom 'translation loader' and provides it as loader. * For the translation module. Loads a Custom 'translation loader' and provides it as loader.
@ -57,23 +21,11 @@ import { OsPermsDirective } from './core/directives/os-perms.directive';
export function HttpLoaderFactory(http: HttpClient) { export function HttpLoaderFactory(http: HttpClient) {
return new PruningTranslationLoader(http); return new PruningTranslationLoader(http);
} }
/**
//add font-awesome icons to library. * Global App Module. Keep it as clean as possible.
//will blow up the code. */
library.add(fas);
@NgModule({ @NgModule({
declarations: [ declarations: [AppComponent],
AppComponent,
LoginComponent,
ProjectorComponent,
MotionsComponent,
AgendaComponent,
SiteComponent,
StartComponent,
ProjectorContainerComponent,
OsPermsDirective
],
imports: [ imports: [
BrowserModule, BrowserModule,
HttpClientModule, HttpClientModule,
@ -81,20 +33,7 @@ library.add(fas);
cookieName: 'OpenSlidesCsrfToken', cookieName: 'OpenSlidesCsrfToken',
headerName: 'X-CSRFToken' headerName: 'X-CSRFToken'
}), }),
FormsModule, BrowserAnimationsModule, //TODO
BrowserAnimationsModule,
MatButtonModule,
MatCheckboxModule,
MatToolbarModule,
MatCardModule,
MatInputModule,
MatProgressSpinnerModule,
MatSidenavModule,
MatListModule,
MatExpansionModule,
MatMenuModule,
MatSnackBarModule,
FontAwesomeModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,
@ -102,21 +41,9 @@ library.add(fas);
deps: [HttpClient] deps: [HttpClient]
} }
}), }),
AppRoutingModule AppRoutingModule,
], CoreModule,
providers: [ LoginModule
Title,
AuthGuard,
AuthService,
AutoupdateService,
DataStoreService,
OperatorService,
WebsocketService,
{
provide: HTTP_INTERCEPTORS,
useClass: AddHeaderInterceptor,
multi: true
}
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

View File

@ -0,0 +1,13 @@
import { CoreModule } from './core.module';
describe('CoreModule', () => {
let coreModule: CoreModule;
beforeEach(() => {
coreModule = new CoreModule(parent);
});
it('should create an instance', () => {
expect(coreModule).toBeTruthy();
});
});

View File

@ -0,0 +1,46 @@
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Title } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
// Core Services, Directives
import { AuthGuard } from './services/auth-guard.service';
import { AuthService } from './services/auth.service';
import { AutoupdateService } from './services/autoupdate.service';
import { DataStoreService } from './services/dataStore.service';
import { OperatorService } from './services/operator.service';
import { WebsocketService } from './services/websocket.service';
import { AddHeaderInterceptor } from './http-interceptor';
/** Global Core Module. Contains all global (singleton) services
*
*/
@NgModule({
imports: [CommonModule],
providers: [
Title,
AuthGuard,
AuthService,
AutoupdateService,
DataStoreService,
OperatorService,
WebsocketService,
{
provide: HTTP_INTERCEPTORS,
useClass: AddHeaderInterceptor,
multi: true
}
]
})
export class CoreModule {
/** make sure CoreModule is imported only by one NgModule, the AppModule */
constructor(
@Optional()
@SkipSelf()
parentModule: CoreModule
) {
if (parentModule) {
throw new Error('CoreModule is already loaded. Import only in AppModule');
}
}
}

View File

@ -3,24 +3,24 @@ import { Injectable } from '@angular/core';
import { OpenSlidesComponent } from 'app/openslides.component'; import { OpenSlidesComponent } from 'app/openslides.component';
import { WebsocketService } from './websocket.service'; import { WebsocketService } from './websocket.service';
// the Models // the Models
import { Item } from 'app/core/models/agenda/item'; import { Item } from 'app/shared/models/agenda/item';
import { Assignment } from 'app/core/models/assignments/assignment'; import { Assignment } from 'app/shared/models/assignments/assignment';
import { ChatMessage } from 'app/core/models/core/chat-message'; import { ChatMessage } from 'app/shared/models/core/chat-message';
import { Config } from 'app/core/models/core/config'; import { Config } from 'app/shared/models/core/config';
import { Countdown } from 'app/core/models/core/countdown'; import { Countdown } from 'app/shared/models/core/countdown';
import { ProjectorMessage } from 'app/core/models/core/projector-message'; import { ProjectorMessage } from 'app/shared/models/core/projector-message';
import { Projector } from 'app/core/models/core/projector'; import { Projector } from 'app/shared/models/core/projector';
import { Tag } from 'app/core/models/core/tag'; import { Tag } from 'app/shared/models/core/tag';
import { Mediafile } from 'app/core/models/mediafiles/mediafile'; import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
import { Category } from 'app/core/models/motions/category'; import { Category } from 'app/shared/models/motions/category';
import { MotionBlock } from 'app/core/models/motions/motion-block'; import { MotionBlock } from 'app/shared/models/motions/motion-block';
import { MotionChangeReco } from 'app/core/models/motions/motion-change-reco'; import { MotionChangeReco } from 'app/shared/models/motions/motion-change-reco';
import { Motion } from 'app/core/models/motions/motion'; import { Motion } from 'app/shared/models/motions/motion';
import { Workflow } from 'app/core/models/motions/workflow'; import { Workflow } from 'app/shared/models/motions/workflow';
import { Topic } from 'app/core/models/topics/topic'; import { Topic } from 'app/shared/models/topics/topic';
import { Group } from 'app/core/models/users/group'; import { Group } from 'app/shared/models/users/group';
import { PersonalNote } from 'app/core/models/users/personal-note'; import { PersonalNote } from 'app/shared/models/users/personal-note';
import { User } from 'app/core/models/users/user'; import { User } from 'app/shared/models/users/user';
/** /**
* Handles the initial update and automatic updates using the {@link WebsocketService} * Handles the initial update and automatic updates using the {@link WebsocketService}

View File

@ -4,7 +4,7 @@ import { Observable, of, BehaviorSubject } from 'rxjs';
import { tap, map } from 'rxjs/operators'; import { tap, map } from 'rxjs/operators';
import { ImproperlyConfiguredError } from 'app/core/exceptions'; import { ImproperlyConfiguredError } from 'app/core/exceptions';
import { BaseModel, ModelId } from 'app/core/models/base-model'; import { BaseModel, ModelId } from 'app/shared/models/base.model';
/** /**
* represents a collection on the Django server, uses an ID to access a {@link BaseModel}. * represents a collection on the Django server, uses an ID to access a {@link BaseModel}.

View File

@ -3,7 +3,7 @@ import { Observable, BehaviorSubject } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { tap, catchError, share } from 'rxjs/operators'; import { tap, catchError, share } from 'rxjs/operators';
import { OpenSlidesComponent } from 'app/openslides.component'; import { OpenSlidesComponent } from 'app/openslides.component';
import { Group } from 'app/core/models/users/group'; import { Group } from 'app/shared/models/users/group';
/** /**
* The operator represents the user who is using OpenSlides. * The operator represents the user who is using OpenSlides.

View File

@ -0,0 +1,13 @@
import { ProjectorContainerModule } from './projector-container.module';
describe('ProjectorContainerModule', () => {
let projectorContainerModule: ProjectorContainerModule;
beforeEach(() => {
projectorContainerModule = new ProjectorContainerModule();
});
it('should create an instance', () => {
expect(projectorContainerModule).toBeTruthy();
});
});

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProjectorContainerComponent } from './projector-container.component';
import { SharedModule } from 'app/shared/shared.module';
import { ProjectorComponent } from './projector/projector.component';
import { ProjectorContainerRoutingModule } from './projector/projector-container.routing.module';
@NgModule({
imports: [CommonModule, ProjectorContainerRoutingModule, SharedModule],
declarations: [ProjectorContainerComponent, ProjectorComponent]
})
export class ProjectorContainerModule {}

View File

@ -0,0 +1,15 @@
import { NgModule, Component } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ProjectorContainerComponent } from '../projector-container.component';
import { ProjectorComponent } from './projector.component';
const routes: Routes = [
{ path: '', component: ProjectorContainerComponent },
{ path: 'real', component: ProjectorComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ProjectorContainerRoutingModule {}

View File

@ -1,8 +1,8 @@
import { Directive, Input, ElementRef, TemplateRef, ViewContainerRef, OnInit } from '@angular/core'; import { Directive, Input, ElementRef, TemplateRef, ViewContainerRef, OnInit } from '@angular/core';
import { OperatorService } from 'app/core/services/operator.service'; import { OperatorService } from 'app/core/services/operator.service';
import { OpenSlidesComponent } from '../../openslides.component'; import { OpenSlidesComponent } from 'app/openslides.component';
import { Group } from 'app/core/models/users/group'; import { Group } from 'app/shared/models/users/group';
/** /**
* Directive to check if the {@link OperatorService} has the correct permissions to access certain functions * Directive to check if the {@link OperatorService} has the correct permissions to access certain functions

View File

@ -1,5 +1,4 @@
// import { Serializable } from 'app/core/models/serializable'; import { Deserializable } from '../deserializable.model';
import { Deserializable } from 'app/core/models/deserializable.model';
/** /**
* Representation of the content object in agenda item * Representation of the content object in agenda item

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
import { Speaker } from './speaker'; import { Speaker } from './speaker';
import { ContentObject } from './content-object'; import { ContentObject } from './content-object';

View File

@ -1,4 +1,4 @@
import { Deserializable } from 'app/core/models/deserializable.model'; import { Deserializable } from '../deserializable.model';
/** /**
* Representation of a speaker in an agenda item * Representation of a speaker in an agenda item

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base-model'; import { BaseModel } from '../base.model';
import { AssignmentUser } from './assignment-user'; import { AssignmentUser } from './assignment-user';
import { Poll } from './poll'; import { Poll } from './poll';

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of chat messages. * Representation of chat messages.

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a config variable * Representation of a config variable

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a countdown * Representation of a countdown

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a projector message. * Representation of a projector message.

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a projector. Has the nested property "projectiondefaults" * Representation of a projector. Has the nested property "projectiondefaults"

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a tag. * Representation of a tag.

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
import { File } from './file'; import { File } from './file';
/** /**

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a motion category. Has the nested property "File" * Representation of a motion category. Has the nested property "File"

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a motion block. * Representation of a motion block.

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a motion change recommendation. * Representation of a motion change recommendation.

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of Motion. * Representation of Motion.

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
import { WorkflowState } from './workflow-state'; import { WorkflowState } from './workflow-state';
/** /**

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a topic. * Representation of a topic.

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of user group. * Representation of user group.

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of users personal note. * Representation of users personal note.

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/core/models/base-model'; import { BaseModel } from '../base.model';
/** /**
* Representation of a user in contrast to the operator. * Representation of a user in contrast to the operator.

View File

@ -0,0 +1,13 @@
import { SharedModule } from './shared.module';
describe('SharedModule', () => {
let sharedModule: SharedModule;
beforeEach(() => {
sharedModule = new SharedModule();
});
it('should create an instance', () => {
expect(sharedModule).toBeTruthy();
});
});

View File

@ -0,0 +1,78 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
// MaterialUI modules
import {
MatButtonModule,
MatCheckboxModule,
MatToolbarModule,
MatCardModule,
MatInputModule,
MatProgressSpinnerModule,
MatSidenavModule,
MatSnackBarModule
} from '@angular/material';
import { MatListModule } from '@angular/material/list';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatMenuModule } from '@angular/material/menu';
// FontAwesome modules
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
// ngx-translate
import { TranslateModule } from '@ngx-translate/core';
// directives
import { OsPermsDirective } from './directives/os-perms.directive';
library.add(fas);
/**
* Share Module for all "dumb" components and pipes.
*
* These components don not import and inject services from core or other features
* in their constructors.
*
* Should receive all data though attributes in the template of the component using them.
* No dependency to the rest of our application.
*/
@NgModule({
imports: [
CommonModule,
FormsModule,
MatButtonModule,
MatCheckboxModule,
MatToolbarModule,
MatCardModule,
MatInputModule,
MatProgressSpinnerModule,
MatSidenavModule,
MatListModule,
MatExpansionModule,
MatMenuModule,
MatSnackBarModule,
FontAwesomeModule
],
exports: [
FormsModule,
MatButtonModule,
MatCheckboxModule,
MatToolbarModule,
MatCardModule,
MatInputModule,
MatProgressSpinnerModule,
MatSidenavModule,
MatListModule,
MatExpansionModule,
MatMenuModule,
MatSnackBarModule,
FontAwesomeModule,
TranslateModule,
OsPermsDirective
],
declarations: [OsPermsDirective]
})
export class SharedModule {}

View File

@ -8,7 +8,7 @@
<!-- download button on the right --> <!-- download button on the right -->
<span class='spacer'></span> <span class='spacer'></span>
<button mat-icon-button (click)='drawer.toggle()'> <button mat-icon-button (click)='downloadAgendaButton()'>
<fa-icon icon='download'></fa-icon> <fa-icon icon='download'></fa-icon>
</button> </button>

View File

@ -1,19 +1,19 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MotionsComponent } from './motions.component'; import { AgendaListComponent } from './agenda-list.component';
describe('MotionsComponent', () => { describe('AgendaListComponent', () => {
let component: MotionsComponent; let component: AgendaListComponent;
let fixture: ComponentFixture<MotionsComponent>; let fixture: ComponentFixture<AgendaListComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [MotionsComponent] declarations: [AgendaListComponent]
}).compileComponents(); }).compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(MotionsComponent); fixture = TestBed.createComponent(AgendaListComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -3,11 +3,11 @@ import { Title } from '@angular/platform-browser';
import { BaseComponent } from 'app/base.component'; import { BaseComponent } from 'app/base.component';
@Component({ @Component({
selector: 'app-agenda', selector: 'app-agenda-list',
templateUrl: './agenda.component.html', templateUrl: './agenda-list.component.html',
styleUrls: ['./agenda.component.css'] styleUrls: ['./agenda-list.component.css']
}) })
export class AgendaComponent extends BaseComponent implements OnInit { export class AgendaListComponent extends BaseComponent implements OnInit {
constructor(titleService: Title) { constructor(titleService: Title) {
super(titleService); super(titleService);
} }
@ -16,4 +16,8 @@ export class AgendaComponent extends BaseComponent implements OnInit {
//TODO translate //TODO translate
super.setTitle('Agenda'); super.setTitle('Agenda');
} }
downloadAgendaButton() {
console.log('Clock Download Button');
}
} }

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AgendaListComponent } from './agenda-list/agenda-list.component';
const routes: Routes = [{ path: '', component: AgendaListComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AgendaRoutingModule {}

View File

@ -0,0 +1,13 @@
import { AgendaModule } from './agenda.module';
describe('AgendaModule', () => {
let agendaModule: AgendaModule;
beforeEach(() => {
agendaModule = new AgendaModule();
});
it('should create an instance', () => {
expect(agendaModule).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AgendaRoutingModule } from './agenda-routing.module';
import { SharedModule } from '../../shared/shared.module';
import { AgendaListComponent } from './agenda-list/agenda-list.component';
@NgModule({
imports: [CommonModule, AgendaRoutingModule, SharedModule],
declarations: [AgendaListComponent]
})
export class AgendaModule {}

View File

@ -0,0 +1,3 @@
<p>
assignment-list works!
</p>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AssignmentListComponent } from './assignment-list.component';
describe('AssignmentListComponent', () => {
let component: AssignmentListComponent;
let fixture: ComponentFixture<AssignmentListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AssignmentListComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AssignmentListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-assignment-list',
templateUrl: './assignment-list.component.html',
styleUrls: ['./assignment-list.component.css']
})
export class AssignmentListComponent implements OnInit {
constructor() {}
ngOnInit() {}
}

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AssignmentListComponent } from './assignment-list/assignment-list.component';
const routes: Routes = [{ path: '', component: AssignmentListComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AssignmentsRoutingModule {}

View File

@ -0,0 +1,13 @@
import { AssignmentsModule } from './assignments.module';
describe('AssignmentsModule', () => {
let assignmentsModule: AssignmentsModule;
beforeEach(() => {
assignmentsModule = new AssignmentsModule();
});
it('should create an instance', () => {
expect(assignmentsModule).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AssignmentsRoutingModule } from './assignments-routing.module';
import { SharedModule } from '../../shared/shared.module';
import { AssignmentListComponent } from './assignment-list/assignment-list.component';
@NgModule({
imports: [CommonModule, AssignmentsRoutingModule, SharedModule],
declarations: [AssignmentListComponent]
})
export class AssignmentsModule {}

View File

@ -1,29 +1,29 @@
<mat-card class="login-card"> <mat-card class="login-card">
<mat-card-header> <mat-card-header>
<mat-card-title>OpenSides 3 - Login</mat-card-title> <mat-card-title>OpenSides 3 - Login</mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<form class="login-full-widht"> <form class="login-full-widht">
<table class="example-full-width" cellspacing="0"> <table class="example-full-width" cellspacing="0">
<tr> <tr>
<td> <td>
<mat-form-field class="login-full-widht"> <mat-form-field class="login-full-widht">
<input matInput placeholder="Username" [(ngModel)]="username" name="username" required> <input matInput placeholder="Username" [(ngModel)]="username" name="username" required>
</mat-form-field> </mat-form-field>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<mat-form-field class="example-full-width"> <mat-form-field class="example-full-width">
<input matInput placeholder="Password" [(ngModel)]="password" type="password" name="password" required> <input matInput placeholder="Password" [(ngModel)]="password" type="password" name="password" required>
</mat-form-field> </mat-form-field>
</td> </td>
</tr> </tr>
</table> </table>
</form> </form>
<mat-spinner [style.display]="showSpinner ? 'block' : 'none'"></mat-spinner> <!-- <mat-spinner [style.display]="showSpinner ? 'block' : 'none'"></mat-spinner> -->
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button (click)="formLogin()" color="primary">Login</button> <button mat-raised-button (click)="formLogin()" color="primary">Login</button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>

View File

@ -0,0 +1,13 @@
import { LoginModule } from './login.module';
describe('LoginModule', () => {
let loginModule: LoginModule;
beforeEach(() => {
loginModule = new LoginModule();
});
it('should create an instance', () => {
expect(loginModule).toBeTruthy();
});
});

View File

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LoginComponent } from './login.component';
import { SharedModule } from '../../shared/shared.module';
@NgModule({
imports: [CommonModule, SharedModule],
declarations: [LoginComponent]
})
export class LoginModule {}

View File

@ -0,0 +1,3 @@
<p>
mediafile-list works!
</p>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MediafileListComponent } from './mediafile-list.component';
describe('MediafileListComponent', () => {
let component: MediafileListComponent;
let fixture: ComponentFixture<MediafileListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MediafileListComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MediafileListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-mediafile-list',
templateUrl: './mediafile-list.component.html',
styleUrls: ['./mediafile-list.component.css']
})
export class MediafileListComponent implements OnInit {
constructor() {}
ngOnInit() {}
}

View File

@ -0,0 +1,11 @@
import { NgModule, Component } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MediafileListComponent } from './mediafile-list/mediafile-list.component';
const routes: Routes = [{ path: '', component: MediafileListComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class MediafilesRoutingModule {}

View File

@ -0,0 +1,13 @@
import { MediafilesModule } from './mediafiles.module';
describe('MediafilesModule', () => {
let mediafilesModule: MediafilesModule;
beforeEach(() => {
mediafilesModule = new MediafilesModule();
});
it('should create an instance', () => {
expect(mediafilesModule).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MediafilesRoutingModule } from './mediafiles-routing.module';
import { SharedModule } from '../../shared/shared.module';
import { MediafileListComponent } from './mediafile-list/mediafile-list.component';
@NgModule({
imports: [CommonModule, MediafilesRoutingModule, SharedModule],
declarations: [MediafileListComponent]
})
export class MediafilesModule {}

View File

@ -9,7 +9,7 @@
<!-- download button on the right --> <!-- download button on the right -->
<span class='spacer'></span> <span class='spacer'></span>
<button mat-icon-button (click)='drawer.toggle()'> <button mat-icon-button (click)='downloadMotionsButton()'>
<fa-icon icon='download'></fa-icon> <fa-icon icon='download'></fa-icon>
</button> </button>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MotionListComponent } from './motion-list.component';
describe('MotionListComponent', () => {
let component: MotionListComponent;
let fixture: ComponentFixture<MotionListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MotionListComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MotionListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -3,11 +3,11 @@ import { Title } from '@angular/platform-browser';
import { BaseComponent } from 'app/base.component'; import { BaseComponent } from 'app/base.component';
@Component({ @Component({
selector: 'app-motions', selector: 'app-motion-list',
templateUrl: './motions.component.html', templateUrl: './motion-list.component.html',
styleUrls: ['./motions.component.css'] styleUrls: ['./motion-list.component.css']
}) })
export class MotionsComponent extends BaseComponent implements OnInit { export class MotionListComponent extends BaseComponent implements OnInit {
constructor(titleService: Title) { constructor(titleService: Title) {
super(titleService); super(titleService);
} }
@ -15,4 +15,8 @@ export class MotionsComponent extends BaseComponent implements OnInit {
ngOnInit() { ngOnInit() {
super.setTitle('Motions'); super.setTitle('Motions');
} }
downloadMotionsButton() {
console.log('Download Motions Button');
}
} }

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MotionListComponent } from './motion-list/motion-list.component';
const routes: Routes = [{ path: '', component: MotionListComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class MotionsRoutingModule {}

View File

@ -0,0 +1,13 @@
import { MotionsModule } from './motions.module';
describe('MotionsModule', () => {
let motionsModule: MotionsModule;
beforeEach(() => {
motionsModule = new MotionsModule();
});
it('should create an instance', () => {
expect(motionsModule).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MotionsRoutingModule } from './motions-routing.module';
import { SharedModule } from '../../shared/shared.module';
import { MotionListComponent } from './motion-list/motion-list.component';
@NgModule({
imports: [CommonModule, MotionsRoutingModule, SharedModule],
declarations: [MotionListComponent]
})
export class MotionsModule {}

View File

@ -0,0 +1,3 @@
<p>
settings-list works!
</p>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SettingsListComponent } from './settings-list.component';
describe('SettingsListComponent', () => {
let component: SettingsListComponent;
let fixture: ComponentFixture<SettingsListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SettingsListComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SettingsListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-settings-list',
templateUrl: './settings-list.component.html',
styleUrls: ['./settings-list.component.css']
})
export class SettingsListComponent implements OnInit {
constructor() {}
ngOnInit() {}
}

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SettingsListComponent } from './settings-list/settings-list.component';
const routes: Routes = [{ path: '', component: SettingsListComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SettingsRoutingModule {}

View File

@ -0,0 +1,13 @@
import { SettingsModule } from './settings.module';
describe('SettingsModule', () => {
let settingsModule: SettingsModule;
beforeEach(() => {
settingsModule = new SettingsModule();
});
it('should create an instance', () => {
expect(settingsModule).toBeTruthy();
});
});

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../../shared/shared.module';
import { SettingsRoutingModule } from './settings-routing.module';
import { SettingsListComponent } from './settings-list/settings-list.component';
@NgModule({
imports: [CommonModule, SettingsRoutingModule, SharedModule],
declarations: [SettingsListComponent]
})
export class SettingsModule {}

View File

@ -0,0 +1,35 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SiteComponent } from './site.component';
import { StartComponent } from './start/start.component';
// import { LoginComponent } from './login/login.component';
/**
* Routung to all OpenSlides apps
*
* TODO: Plugins will have to append to the Routes-Array
*/
const routes: Routes = [
// { path: 'login', component: LoginComponent },
{
path: '',
component: SiteComponent,
children: [
{ path: '', component: StartComponent },
{ path: 'agenda', loadChildren: './agenda/agenda.module#AgendaModule' },
{ path: 'assignments', loadChildren: './assignments/assignments.module#AssignmentsModule' },
{ path: 'mediafiles', loadChildren: './mediafiles/mediafiles.module#MediafilesModule' },
{ path: 'motions', loadChildren: './motions/motions.module#MotionsModule' },
{ path: 'settings', loadChildren: './settings/settings.module#SettingsModule' },
{ path: 'users', loadChildren: './users/users.module#UsersModule' }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SiteRoutingModule {}

View File

@ -21,18 +21,34 @@
<!-- navigation --> <!-- navigation -->
<mat-nav-list> <mat-nav-list>
<a mat-list-item routerLink='/' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'> <a *appOsPerms="['core.can_see_frontpage']" mat-list-item routerLink='/' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'>
<fa-icon icon='home'></fa-icon> <fa-icon icon='home'></fa-icon>
<span translate>Home</span> <span translate>Home</span>
</a> </a>
<a mat-list-item routerLink='/agenda' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'> <a *appOsPerms="['agenda.can_see']" mat-list-item routerLink='/agenda' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'>
<fa-icon icon='calendar'></fa-icon> <fa-icon icon='calendar'></fa-icon>
<span translate>Agenda</span> <span translate>Agenda</span>
</a> </a>
<a mat-list-item routerLink='/motions' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'> <a *appOsPerms="['motions.can_see']" mat-list-item routerLink='/motions' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'>
<fa-icon icon='file-alt'></fa-icon> <fa-icon icon='file-alt'></fa-icon>
<span translate>Motions</span> <span translate>Motions</span>
</a> </a>
<a *appOsPerms="['assignments.can_see']" mat-list-item routerLink='/assignments' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'>
<fa-icon icon='chart-pie'></fa-icon>
<span translate>Assignments</span>
</a>
<a *appOsPerms="['users.can_see_name']" mat-list-item routerLink='/users' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'>
<fa-icon icon='user'></fa-icon>
<span translate>Participants</span>
</a>
<a *appOsPerms="['mediafiles.can_see']" mat-list-item routerLink='/mediafiles' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'>
<fa-icon icon='paperclip'></fa-icon>
<span translate>Files</span>
</a>
<a *appOsPerms="['core.can_manage_config']" mat-list-item routerLink='/settings' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'>
<fa-icon icon='cog'></fa-icon>
<span translate>Settings</span>
</a>
</mat-nav-list> </mat-nav-list>
</mat-sidenav> </mat-sidenav>

View File

@ -5,8 +5,6 @@ import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/l
import { AuthService } from 'app/core/services/auth.service'; import { AuthService } from 'app/core/services/auth.service';
import { AutoupdateService } from 'app/core/services/autoupdate.service'; import { AutoupdateService } from 'app/core/services/autoupdate.service';
import { OperatorService } from 'app/core/services/operator.service'; import { OperatorService } from 'app/core/services/operator.service';
import { Subject } from 'rxjs';
import { tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; //showcase import { TranslateService } from '@ngx-translate/core'; //showcase
import { BaseComponent } from 'app/base.component'; import { BaseComponent } from 'app/base.component';

View File

@ -0,0 +1,13 @@
import { SiteModule } from './site.module';
describe('SiteModule', () => {
let siteModule: SiteModule;
beforeEach(() => {
siteModule = new SiteModule();
});
it('should create an instance', () => {
expect(siteModule).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SiteRoutingModule } from './site-routing.module';
import { SharedModule } from 'app/shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { SiteComponent } from './site.component';
import { StartComponent } from './start/start.component';
@NgModule({
imports: [CommonModule, SharedModule, SiteRoutingModule, TranslateModule.forChild()],
declarations: [SiteComponent, StartComponent]
})
export class SiteModule {}

View File

@ -6,8 +6,7 @@ import { TranslateService } from '@ngx-translate/core'; //showcase
// for testing the DS and BaseModel // for testing the DS and BaseModel
import { OperatorService } from 'app/core/services/operator.service'; import { OperatorService } from 'app/core/services/operator.service';
import { User } from 'app/core/models/users/user'; import { User } from 'app/shared/models/users/user';
import { Group } from 'app/core/models/users/group';
@Component({ @Component({
selector: 'app-start', selector: 'app-start',

View File

@ -0,0 +1,3 @@
<p>
user-list works!
</p>

View File

@ -1,19 +1,19 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AgendaComponent } from './agenda.component'; import { UserListComponent } from './user-list.component';
describe('AgendaComponent', () => { describe('UserListComponent', () => {
let component: AgendaComponent; let component: UserListComponent;
let fixture: ComponentFixture<AgendaComponent>; let fixture: ComponentFixture<UserListComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [AgendaComponent] declarations: [UserListComponent]
}).compileComponents(); }).compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(AgendaComponent); fixture = TestBed.createComponent(UserListComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
constructor() {}
ngOnInit() {}
}

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UserListComponent } from './user-list/user-list.component';
const routes: Routes = [{ path: '', component: UserListComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class UsersRoutingModule {}

View File

@ -0,0 +1,13 @@
import { UsersModule } from './users.module';
describe('UsersModule', () => {
let usersModule: UsersModule;
beforeEach(() => {
usersModule = new UsersModule();
});
it('should create an instance', () => {
expect(usersModule).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UsersRoutingModule } from './users-routing.module';
import { SharedModule } from '../../shared/shared.module';
import { UserListComponent } from './user-list/user-list.component';
@NgModule({
imports: [CommonModule, UsersRoutingModule, SharedModule],
declarations: [UserListComponent]
})
export class UsersModule {}