Create statistics of closed list of speakers
This commit is contained in:
parent
ea830f53b0
commit
e7de593b54
@ -62,6 +62,17 @@ const ListOfSpeakersNestedModelDescriptors: NestedModelDescriptors = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object, that contains information about structure-level,
|
||||||
|
* speaking-time and finished-speakers.
|
||||||
|
* Helpful to get a relation between speakers and their structure-level.
|
||||||
|
*/
|
||||||
|
export interface SpeakingTimeStructureLevelObject {
|
||||||
|
structureLevel: string;
|
||||||
|
finishedSpeakers: ViewSpeaker[];
|
||||||
|
speakingTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository service for lists of speakers
|
* Repository service for lists of speakers
|
||||||
*
|
*
|
||||||
@ -228,6 +239,95 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
|
|||||||
return !this.getViewModelList().some(list => list.hasSpeakerSpoken(speaker));
|
return !this.getViewModelList().some(list => list.hasSpeakerSpoken(speaker));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List every speaker only once, who has spoken
|
||||||
|
*
|
||||||
|
* @returns A list with all different speakers.
|
||||||
|
*/
|
||||||
|
public getAllFirstContributions(): ViewSpeaker[] {
|
||||||
|
const speakers: ViewSpeaker[] = this.getViewModelList().flatMap(
|
||||||
|
(los: ViewListOfSpeakers) => los.finishedSpeakers
|
||||||
|
);
|
||||||
|
const firstContributions: ViewSpeaker[] = [];
|
||||||
|
for (const speaker of speakers) {
|
||||||
|
if (!firstContributions.find(s => s.user_id === speaker.user_id)) {
|
||||||
|
firstContributions.push(speaker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstContributions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps structure-level to speaker.
|
||||||
|
*
|
||||||
|
* @returns A list, which entries are `SpeakingTimeStructureLevelObject`.
|
||||||
|
*/
|
||||||
|
public getSpeakingTimeStructureLevelRelation(): SpeakingTimeStructureLevelObject[] {
|
||||||
|
let listSpeakingTimeStructureLevel: SpeakingTimeStructureLevelObject[] = [];
|
||||||
|
for (const los of this.getViewModelList()) {
|
||||||
|
for (const speaker of los.finishedSpeakers) {
|
||||||
|
const nextEntry = this.getSpeakingTimeStructureLevelObject(speaker);
|
||||||
|
listSpeakingTimeStructureLevel = this.getSpeakingTimeStructureLevelList(
|
||||||
|
nextEntry,
|
||||||
|
listSpeakingTimeStructureLevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listSpeakingTimeStructureLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper-function to create a `SpeakingTimeStructureLevelObject` by a given speaker.
|
||||||
|
*
|
||||||
|
* @param speaker, with whom structure-level and speaking-time is calculated.
|
||||||
|
*
|
||||||
|
* @returns The created `SpeakingTimeStructureLevelObject`.
|
||||||
|
*/
|
||||||
|
private getSpeakingTimeStructureLevelObject(speaker: ViewSpeaker): SpeakingTimeStructureLevelObject {
|
||||||
|
return {
|
||||||
|
structureLevel:
|
||||||
|
!speaker.user || (speaker.user && !speaker.user.structure_level)
|
||||||
|
? 'No structure level'
|
||||||
|
: speaker.user.structure_level,
|
||||||
|
finishedSpeakers: [speaker],
|
||||||
|
speakingTime: this.getSpeakingTimeAsNumber(speaker)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper-function to update entries in a given list, if already existing, or create entries otherwise.
|
||||||
|
*
|
||||||
|
* @param object A `SpeakingTimeStructureLevelObject`, that contains information about speaking-time
|
||||||
|
* and structure-level.
|
||||||
|
* @param list A list, at which speaking-time, structure-level and finished_speakers are set.
|
||||||
|
*
|
||||||
|
* @returns The updated map.
|
||||||
|
*/
|
||||||
|
private getSpeakingTimeStructureLevelList(
|
||||||
|
object: SpeakingTimeStructureLevelObject,
|
||||||
|
list: SpeakingTimeStructureLevelObject[]
|
||||||
|
): SpeakingTimeStructureLevelObject[] {
|
||||||
|
const index = list.findIndex(entry => entry.structureLevel === object.structureLevel);
|
||||||
|
if (index >= 0) {
|
||||||
|
list[index].speakingTime += object.speakingTime;
|
||||||
|
list[index].finishedSpeakers.push(...object.finishedSpeakers);
|
||||||
|
} else {
|
||||||
|
list.push(object);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function calculates speaking-time as number for a given speaker.
|
||||||
|
*
|
||||||
|
* @param speaker The speaker, whose speaking-time should be calculated.
|
||||||
|
*
|
||||||
|
* @returns A number, that represents the speaking-time.
|
||||||
|
*/
|
||||||
|
private getSpeakingTimeAsNumber(speaker: ViewSpeaker): number {
|
||||||
|
return Math.floor((new Date(speaker.end_time).valueOf() - new Date(speaker.begin_time).valueOf()) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function get the url to the speaker rest address
|
* Helper function get the url to the speaker rest address
|
||||||
*
|
*
|
||||||
|
@ -62,6 +62,24 @@ export class DurationService {
|
|||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates a given time to a readable string, that contains hours, minutes and seconds.
|
||||||
|
*
|
||||||
|
* @param duration The time as number (in seconds).
|
||||||
|
*
|
||||||
|
* @returns A readable time-string.
|
||||||
|
*/
|
||||||
|
public durationToStringWithHours(duration: number): string {
|
||||||
|
const hours = Math.floor(duration / 3600);
|
||||||
|
const minutes = `0${Math.floor((duration % 3600) / 60)}`.slice(-2);
|
||||||
|
const seconds = `0${Math.floor(duration % 60)}`.slice(-2);
|
||||||
|
if (!isNaN(+minutes) && !isNaN(+seconds)) {
|
||||||
|
return `${hours}:${minutes}:${seconds} h`;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a duration number (given in minutes or seconds)
|
* Converts a duration number (given in minutes or seconds)
|
||||||
*
|
*
|
||||||
|
@ -484,7 +484,7 @@ export class ListViewTableComponent<V extends BaseViewModel | BaseViewModelWithC
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isElementProjected = (context: PblNgridRowContext<V>) => {
|
public isElementProjected = (context: PblNgridRowContext<V>) => {
|
||||||
if (this.projectorService.isProjected(this.getProjectable(context.$implicit as V))) {
|
if (this.allowProjector && this.projectorService.isProjected(this.getProjectable(context.$implicit as V))) {
|
||||||
return 'projected';
|
return 'projected';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
<mat-card class="os-card" *osPerms="'users.can_manage'">
|
<button type="button" mat-button (click)="countUsers()" *ngIf="!this.token">
|
||||||
<button type="button" mat-button (click)="countUsers()" *ngIf="!this.token">
|
<span>{{ 'Count active users' | translate }}</span>
|
||||||
<span>{{ 'Count active users' | translate }}</span>
|
</button>
|
||||||
</button>
|
<button type="button" mat-button (click)="stopCounting()" *ngIf="this.token">
|
||||||
<button type="button" mat-button (click)="stopCounting()" *ngIf="this.token">
|
<span>{{ 'Stop counting' | translate }}</span>
|
||||||
<span>{{ 'Stop counting' | translate }}</span>
|
</button>
|
||||||
</button>
|
<div *ngIf="stats">
|
||||||
<div *ngIf="stats">
|
<p>
|
||||||
<p>
|
{{ userIds().length }} <span>{{ 'active users' | translate }}</span> ({{ stats.activeUserHandles }}
|
||||||
{{ userIds().length }} <span>{{ 'active users' | translate }}</span> ({{ stats.activeUserHandles }}
|
<span>{{ 'connections' | translate }}</span
|
||||||
<span>{{ 'connections' | translate }}</span
|
>)
|
||||||
|
</p>
|
||||||
|
<h3>{{ 'Groups' | translate }}</h3>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let groupId of groupIds()">
|
||||||
|
<strong>{{ stats.groups[groupId].name }}</strong>
|
||||||
|
<span> : {{ userInGroupIds(groupId).length }} </span>
|
||||||
|
<span>{{ 'active users' | translate }}</span>
|
||||||
|
(<span>{{ stats.groups[groupId].userHandleCount }}</span> <span>{{ 'connections' | translate }}</span
|
||||||
>)
|
>)
|
||||||
</p>
|
</li>
|
||||||
<h3>{{ 'Groups' | translate }}</h3>
|
</ul>
|
||||||
<ul>
|
</div>
|
||||||
<li *ngFor="let groupId of groupIds()">
|
|
||||||
<strong>{{ stats.groups[groupId].name }}</strong>
|
|
||||||
<span> : {{ userInGroupIds(groupId).length }} </span>
|
|
||||||
<span>{{ 'active users' | translate }}</span>
|
|
||||||
(<span>{{ stats.groups[groupId].userHandleCount }}</span> <span>{{ 'connections' | translate }}</span
|
|
||||||
>)
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</mat-card>
|
|
||||||
|
@ -34,4 +34,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<os-count-users></os-count-users>
|
<mat-card class="os-card" *osPerms="'users.can_manage'">
|
||||||
|
<h3>{{ 'Statistics' | translate }}</h3>
|
||||||
|
<div>
|
||||||
|
<h4>{{ 'Count active users' | translate }}</h4>
|
||||||
|
<os-count-users></os-count-users>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4>{{ 'Count active word requests' | translate }}</h4>
|
||||||
|
<os-user-statistics></os-user-statistics>
|
||||||
|
</div>
|
||||||
|
</mat-card>
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
<button mat-button *ngIf="!openedStatistics" (click)="changeViewOfStatistics()">
|
||||||
|
{{ 'Count active word requests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button mat-button *ngIf="openedStatistics" (click)="changeViewOfStatistics()">
|
||||||
|
{{ 'Stop counting' | translate }}
|
||||||
|
</button>
|
||||||
|
<ng-container *ngIf="openedStatistics">
|
||||||
|
<table class="user-statistics-table">
|
||||||
|
<tr>
|
||||||
|
<td>{{ 'Total number of word requests' | translate }}</td>
|
||||||
|
<td>{{ numberOfWordRequests }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ 'Unique speakers' | translate }}</td>
|
||||||
|
<td>{{ numberOfUniqueSpeakers }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ 'Total duration of word requests' | translate }}</td>
|
||||||
|
<td>{{ totalDuration }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<os-list-view-table
|
||||||
|
[listObservable]="statisticsByStructureLevelObservable"
|
||||||
|
[columns]="columnDefinition"
|
||||||
|
[filterProps]="filterProps"
|
||||||
|
[vScrollFixed]="45"
|
||||||
|
[allowProjector]="false"
|
||||||
|
[fullScreen]="false"
|
||||||
|
[showListOfSpeakers]="false"
|
||||||
|
[cssClasses]="{ 'user-statistics-table--virtual-scroll': true }"
|
||||||
|
>
|
||||||
|
<!-- Header -->
|
||||||
|
<div *pblNgridHeaderCellDef="'*'; col as col">
|
||||||
|
{{ col.label | translate }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div *pblNgridCellDef="'structureLevel'; row as object">
|
||||||
|
{{ object.structureLevel | translate }}
|
||||||
|
</div>
|
||||||
|
<div *pblNgridCellDef="'durationOfWordRequests'; row as object">
|
||||||
|
{{ parseDuration(object.speakingTime) }}
|
||||||
|
</div>
|
||||||
|
<div *pblNgridCellDef="'numberOfWordRequests'; row as object">
|
||||||
|
{{ object.finishedSpeakers.length }}
|
||||||
|
</div>
|
||||||
|
</os-list-view-table>
|
||||||
|
</ng-container>
|
@ -0,0 +1,25 @@
|
|||||||
|
.user-statistics-table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 16px auto;
|
||||||
|
|
||||||
|
&,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: 8px 8px 8px 24px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-statistics-table--virtual-scroll {
|
||||||
|
height: 500px;
|
||||||
|
|
||||||
|
.pbl-ngrid-header-cell:not(:first-child):before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
@import '~@angular/material/theming';
|
||||||
|
|
||||||
|
@mixin os-user-statistics-style($theme) {
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
$text: mat-color($foreground, secondary-text);
|
||||||
|
|
||||||
|
.user-statistics-table {
|
||||||
|
th {
|
||||||
|
font-weight: 400;
|
||||||
|
color: $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: mat-color($foreground, divider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { UserStatisticsComponent } from './user-statistics.component';
|
||||||
|
|
||||||
|
describe('UserStatisticsComponent', () => {
|
||||||
|
let component: UserStatisticsComponent;
|
||||||
|
let fixture: ComponentFixture<UserStatisticsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UserStatisticsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,149 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { PblColumnDefinition } from '@pebula/ngrid/lib/grid';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ListOfSpeakersRepositoryService,
|
||||||
|
SpeakingTimeStructureLevelObject
|
||||||
|
} from 'app/core/repositories/agenda/list-of-speakers-repository.service';
|
||||||
|
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||||
|
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
|
||||||
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'os-user-statistics',
|
||||||
|
templateUrl: './user-statistics.component.html',
|
||||||
|
styleUrls: ['./user-statistics.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class UserStatisticsComponent extends BaseViewComponent {
|
||||||
|
/**
|
||||||
|
* Returns the total duration for a whole assembly.
|
||||||
|
*/
|
||||||
|
public get totalDuration(): string {
|
||||||
|
return this.parseDuration(this.speakingTimeAsNumber, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get numberOfUniqueSpeakers(): number {
|
||||||
|
return this.uniqueSpeakers.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get numberOfWordRequests(): number {
|
||||||
|
return this._numberOfWordRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an observable containing a list. This list contains objects separated by the structure-level of speakers.
|
||||||
|
*
|
||||||
|
* Those objects hold information about number of word requests and duration of word requests for a given
|
||||||
|
* structure-level.
|
||||||
|
*/
|
||||||
|
public get statisticsByStructureLevelObservable(): Observable<SpeakingTimeStructureLevelObject[]> {
|
||||||
|
return this.relationSpeakingTimeStructureLevelSubject.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dedicated, if statistics are opened.
|
||||||
|
*/
|
||||||
|
public get openedStatistics(): boolean {
|
||||||
|
return this.statisticIsOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly columnDefinition: PblColumnDefinition[] = [
|
||||||
|
{
|
||||||
|
prop: 'structureLevel',
|
||||||
|
width: 'auto',
|
||||||
|
label: 'Structure level'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'durationOfWordRequests',
|
||||||
|
width: 'auto',
|
||||||
|
label: 'Duration of word requests'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'numberOfWordRequests',
|
||||||
|
width: 'auto',
|
||||||
|
label: 'Number of word requests'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
public readonly filterProps: string[] = ['structureLevel'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds information about hours, minutes and seconds for the total duration of word requests.
|
||||||
|
*/
|
||||||
|
private speakingTimeAsNumber = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of unique speakers.
|
||||||
|
*/
|
||||||
|
private uniqueSpeakers: ViewSpeaker[] = [];
|
||||||
|
private _numberOfWordRequests = 0;
|
||||||
|
private statisticIsOpen = false;
|
||||||
|
private relationSpeakingTimeStructureLevelSubject = new BehaviorSubject<SpeakingTimeStructureLevelObject[]>([]);
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
private losRepo: ListOfSpeakersRepositoryService,
|
||||||
|
private durationService: DurationService
|
||||||
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens or closes statistics.
|
||||||
|
*/
|
||||||
|
public changeViewOfStatistics(): void {
|
||||||
|
this.statisticIsOpen = !this.statisticIsOpen;
|
||||||
|
if (this.statisticIsOpen) {
|
||||||
|
this.startSubscription();
|
||||||
|
} else {
|
||||||
|
this.cleanSubjects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This iterates over a list of list-of-speakers. For each speaker it calculates the duration the speaker
|
||||||
|
* has spoken.
|
||||||
|
*/
|
||||||
|
private pushNextState(): void {
|
||||||
|
const list = this.losRepo.getSpeakingTimeStructureLevelRelation();
|
||||||
|
list.sort((a, b) => b.finishedSpeakers.length - a.finishedSpeakers.length);
|
||||||
|
for (const entry of list) {
|
||||||
|
this.speakingTimeAsNumber += entry.speakingTime;
|
||||||
|
this._numberOfWordRequests += entry.finishedSpeakers.length;
|
||||||
|
}
|
||||||
|
this.relationSpeakingTimeStructureLevelSubject.next(list);
|
||||||
|
this.uniqueSpeakers = this.losRepo.getAllFirstContributions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a string from a given `TimeObject`.
|
||||||
|
*/
|
||||||
|
public parseDuration(time: number, withHours: boolean = false): string {
|
||||||
|
return !withHours
|
||||||
|
? this.durationService.durationToString(time, 'm')
|
||||||
|
: this.durationService.durationToStringWithHours(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
private startSubscription(): void {
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.losRepo.getViewModelListObservable().subscribe(() => {
|
||||||
|
this.reset();
|
||||||
|
this.pushNextState();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private reset(): void {
|
||||||
|
this._numberOfWordRequests = 0;
|
||||||
|
this.speakingTimeAsNumber = 0;
|
||||||
|
}
|
||||||
|
}
|
@ -8,9 +8,17 @@ import { LegalNoticeComponent } from './components/legal-notice/legal-notice.com
|
|||||||
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
import { StartComponent } from './components/start/start.component';
|
import { StartComponent } from './components/start/start.component';
|
||||||
|
import { UserStatisticsComponent } from './components/user-statistics/user-statistics.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, CommonRoutingModule, SharedModule],
|
imports: [CommonModule, CommonRoutingModule, SharedModule],
|
||||||
declarations: [PrivacyPolicyComponent, StartComponent, LegalNoticeComponent, CountUsersComponent, ErrorComponent]
|
declarations: [
|
||||||
|
PrivacyPolicyComponent,
|
||||||
|
StartComponent,
|
||||||
|
LegalNoticeComponent,
|
||||||
|
CountUsersComponent,
|
||||||
|
ErrorComponent,
|
||||||
|
UserStatisticsComponent
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class OsCommonModule {}
|
export class OsCommonModule {}
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
@import './app/shared/components/progress-snack-bar/progress-snack-bar.component.scss-theme.scss';
|
@import './app/shared/components/progress-snack-bar/progress-snack-bar.component.scss-theme.scss';
|
||||||
@import './app/shared/components/jitsi/jitsi.component.scss-theme.scss';
|
@import './app/shared/components/jitsi/jitsi.component.scss-theme.scss';
|
||||||
@import './app/shared/components/list-view-table/list-view-table.component.scss-theme.scss';
|
@import './app/shared/components/list-view-table/list-view-table.component.scss-theme.scss';
|
||||||
|
@import './app/site/common/components/user-statistics/user-statistics.component.scss-theme.scss';
|
||||||
|
|
||||||
/** fonts */
|
/** fonts */
|
||||||
@import './assets/styles/fonts.scss';
|
@import './assets/styles/fonts.scss';
|
||||||
@ -68,6 +69,7 @@ $narrow-spacing: (
|
|||||||
@include os-progress-snack-bar-style($theme);
|
@include os-progress-snack-bar-style($theme);
|
||||||
@include os-jitsi-theme($theme);
|
@include os-jitsi-theme($theme);
|
||||||
@include os-list-view-table-theme($theme);
|
@include os-list-view-table-theme($theme);
|
||||||
|
@include os-user-statistics-style($theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load projector specific SCSS values */
|
/** Load projector specific SCSS values */
|
||||||
|
Loading…
Reference in New Issue
Block a user