Projector templates
- Projector base template - Projection button - projection dialog - motion slide (template and motions/projector.py) - user slide (template and users/projector.py) - motion and user list view with projector column permission check.
This commit is contained in:
parent
965d23be50
commit
42f8b74d8e
@ -45,7 +45,6 @@ export class ProjectionDialogService extends OpenSlidesComponent {
|
||||
ProjectorElementBuildDeskriptor,
|
||||
ProjectionDialogReturnType
|
||||
>(ProjectionDialogComponent, {
|
||||
minWidth: '500px',
|
||||
maxHeight: '90vh',
|
||||
data: descriptor
|
||||
});
|
||||
|
@ -1,8 +1,7 @@
|
||||
<h2 mat-dialog-title>{{ projectorElementBuildDescriptor.getTitle() }}</h2>
|
||||
<h2 mat-dialog-title translate>Project
|
||||
<span *ngIf="projectorElementBuildDescriptor.projectionDefaultName === 'motions'" translate>Motion</span>
|
||||
{{ projectorElementBuildDescriptor.getTitle() }}?</h2>
|
||||
<mat-dialog-content>
|
||||
<mat-card>
|
||||
<mat-card-title> <span translate>Projectors</span> </mat-card-title>
|
||||
<mat-card-content>
|
||||
<div class="projectors" *ngFor="let projector of projectors" [ngClass]="isProjectedOn(projector) ? 'projected' : ''">
|
||||
<mat-checkbox [checked]="isProjectorSelected(projector)" (change)="toggleProjector(projector)">
|
||||
{{ projector.name | translate }}
|
||||
@ -11,13 +10,10 @@
|
||||
<mat-icon>videocam</mat-icon>
|
||||
</span>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card *ngIf="options.length > 0">
|
||||
<mat-card-title>
|
||||
<span translate>Slide options</span>
|
||||
</mat-card-title>
|
||||
<mat-card-content>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div *ngIf="options.length > 0">
|
||||
<div *ngFor="let option of options">
|
||||
<div *ngIf="isDecisionOption(option)">
|
||||
<mat-checkbox [checked]="projectorElement[option.key]" (change)="projectorElement[option.key] = !projectorElement[option.key]">
|
||||
@ -33,8 +29,7 @@
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button (click)="onOk()" color="primary" translate>OK</button>
|
||||
|
@ -1,15 +1,12 @@
|
||||
mat-dialog-content {
|
||||
overflow: inherit;
|
||||
min-width: auto;
|
||||
|
||||
div.projectors {
|
||||
padding: 15px;
|
||||
padding: 15px 0;
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
mat-card {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
<button type="button" mat-icon-button (click)="onClick($event)">
|
||||
<button type="button" mat-mini-fab (click)="onClick($event)"
|
||||
[ngClass]="isProjected() ? 'projectorbutton-active' : 'projectorbutton-inactive'">
|
||||
<mat-icon>videocam</mat-icon>
|
||||
</button>
|
||||
|
@ -0,0 +1,8 @@
|
||||
.projectorbutton-active {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.projectorbutton-inactive {
|
||||
background-color: white;
|
||||
color: grey;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Projectable, ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { ProjectionDialogService } from 'app/core/services/projection-dialog.service';
|
||||
import { ProjectorService } from '../../../core/services/projector.service';
|
||||
|
||||
/**
|
||||
*/
|
||||
@ -16,7 +17,10 @@ export class ProjectorButtonComponent implements OnInit {
|
||||
/**
|
||||
* The consotructor
|
||||
*/
|
||||
public constructor(private projectionDialogService: ProjectionDialogService) {}
|
||||
public constructor(
|
||||
private projectionDialogService: ProjectionDialogService,
|
||||
private projectorService: ProjectorService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initialization function
|
||||
@ -27,4 +31,17 @@ export class ProjectorButtonComponent implements OnInit {
|
||||
event.stopPropagation();
|
||||
this.projectionDialogService.openProjectDialogFor(this.object);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @returns true, if the object is projected on one projector.
|
||||
*/
|
||||
public isProjected(): boolean {
|
||||
if (this.object) {
|
||||
return this.projectorService.isProjected(this.object);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,12 +36,12 @@ export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile>
|
||||
public fontActions: string[];
|
||||
|
||||
/**
|
||||
* Columns to display in Mediafile table when fill width is available
|
||||
* Columns to display in Mediafile table when desktop view is available
|
||||
*/
|
||||
public displayedColumnsDesktop: string[] = ['title', 'info', 'indicator', 'menu'];
|
||||
|
||||
/**
|
||||
* Columns to display in Mediafile table when fill width is available
|
||||
* Columns to display in Mediafile table when mobile view is available
|
||||
*/
|
||||
public displayedColumnsMobile: string[] = ['title', 'menu'];
|
||||
|
||||
|
@ -27,16 +27,16 @@
|
||||
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
||||
<!-- Selector column -->
|
||||
<ng-container matColumnDef="selector">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="checkbox-cell"></mat-header-cell>
|
||||
<mat-cell *matCellDef="let motion" class="checkbox-cell">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header></mat-header-cell>
|
||||
<mat-cell *matCellDef="let motion">
|
||||
<mat-icon>{{ isSelected(motion) ? 'check_circle' : '' }}</mat-icon>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Projector column -->
|
||||
<ng-container matColumnDef="projector">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="icon-cell">Projector</mat-header-cell>
|
||||
<mat-cell *matCellDef="let motion" class="icon-cell">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Projector</mat-header-cell>
|
||||
<mat-cell *matCellDef="let motion">
|
||||
<os-projector-button [object]="motion"></os-projector-button>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
@ -84,13 +84,14 @@
|
||||
grey: motion.state.css_class === 'default',
|
||||
lightblue: motion.state.css_class === 'primary'
|
||||
}"
|
||||
[disabled]="true"
|
||||
>
|
||||
{{ getStateLabel(motion) }}
|
||||
</mat-basic-chip>
|
||||
|
||||
<!-- recommendation -->
|
||||
<span *ngIf="motion.recommendation">
|
||||
<mat-basic-chip class="bluegrey"> {{ getRecommendationLabel(motion) }} </mat-basic-chip>
|
||||
<span *ngIf="motion.recommendation && motion.state.next_states_id.length > 0">
|
||||
<mat-basic-chip class="bluegrey" [disabled]="true">{{ getRecommendationLabel(motion) }} </mat-basic-chip>
|
||||
</span>
|
||||
</div>
|
||||
</mat-cell>
|
||||
|
@ -25,6 +25,8 @@ import { WorkflowState } from '../../../../shared/models/motions/workflow-state'
|
||||
import { WorkflowRepositoryService } from '../../services/workflow-repository.service';
|
||||
import { MotionPdfExportService } from '../../services/motion-pdf-export.service';
|
||||
import { MotionExportDialogComponent } from '../motion-export-dialog/motion-export-dialog.component';
|
||||
import { OperatorService } from '../../../../core/services/operator.service';
|
||||
import { ViewportService } from '../../../../core/services/viewport.service';
|
||||
|
||||
/**
|
||||
* Component that displays all the motions in a Table using DataSource.
|
||||
@ -36,18 +38,14 @@ import { MotionExportDialogComponent } from '../motion-export-dialog/motion-expo
|
||||
})
|
||||
export class MotionListComponent extends ListViewBaseComponent<ViewMotion> implements OnInit {
|
||||
/**
|
||||
* Use for minimal width. Please note the 'selector' row for multiSelect mode,
|
||||
* to be able to display an indicator for the state of selection
|
||||
* TODO: Remove projector, if columnsToDisplayFullWidth is used..
|
||||
* Columns to display in table when desktop view is available
|
||||
*/
|
||||
public columnsToDisplayMinWidth = ['projector', 'identifier', 'title', 'state', 'speakers'];
|
||||
public displayedColumnsDesktop: string[] = ['identifier', 'title', 'state', 'speakers'];
|
||||
|
||||
/**
|
||||
* Use for maximal width. Please note the 'selector' row for multiSelect mode,
|
||||
* to be able to display an indicator for the state of selection
|
||||
* TODO: Needs vp.desktop check
|
||||
* Columns to display in table when mobile view is available
|
||||
*/
|
||||
public columnsToDisplayFullWidth = ['projector', 'identifier', 'title', 'state', 'speakers'];
|
||||
public displayedColumnsMobile = ['identifier', 'title'];
|
||||
|
||||
/**
|
||||
* Value of the configuration variable `motions_statutes_enabled` - are statutes enabled?
|
||||
@ -82,6 +80,7 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
* @param userRepo
|
||||
* @param sortService
|
||||
* @param filterService
|
||||
* @param vp
|
||||
* @param perms LocalPermissionService
|
||||
*/
|
||||
public constructor(
|
||||
@ -97,8 +96,10 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
private workflowRepo: WorkflowRepositoryService,
|
||||
private motionRepo: MotionRepositoryService,
|
||||
private motionCsvExport: MotionCsvExportService,
|
||||
private operator: OperatorService,
|
||||
private pdfExport: MotionPdfExportService,
|
||||
private dialog: MatDialog,
|
||||
private vp: ViewportService,
|
||||
public multiselectService: MotionMultiselectService,
|
||||
public sortService: MotionSortListService,
|
||||
public filterService: MotionFilterListService,
|
||||
@ -221,10 +222,14 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
* Returns current definitions for the listView table
|
||||
*/
|
||||
public getColumnDefinition(): string[] {
|
||||
if (this.isMultiSelect) {
|
||||
return ['selector'].concat(this.columnsToDisplayMinWidth);
|
||||
let columns = this.vp.isMobile ? this.displayedColumnsMobile : this.displayedColumnsDesktop;
|
||||
if (this.operator.hasPerms('core.can_manage_projector')) {
|
||||
columns = ['projector'].concat(columns);
|
||||
}
|
||||
return this.columnsToDisplayMinWidth;
|
||||
if (this.isMultiSelect) {
|
||||
columns = ['selector'].concat(columns);
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -318,7 +318,7 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
|
||||
public getTitle(): string {
|
||||
if (this.identifier) {
|
||||
return this.identifier + ' - ' + this.title;
|
||||
return 'Motion ' + this.identifier;
|
||||
}
|
||||
return this.title;
|
||||
}
|
||||
@ -480,7 +480,7 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
}
|
||||
],
|
||||
projectionDefaultName: 'motions',
|
||||
getTitle: () => this.getTitle()
|
||||
getTitle: () => this.identifier
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,15 @@
|
||||
<div id="container" [osResized]="resizeSubject" [ngStyle]="containerStyle" #container>
|
||||
<div id="projector" [ngStyle]="projectorStyle">
|
||||
<div class="header" [ngStyle]="headerFooterStyle" *ngIf="enableHeaderAndFooter">
|
||||
<div *ngIf="enableTitle">
|
||||
Header Title
|
||||
<div id="header" [ngStyle]="headerFooterStyle" *ngIf="enableHeaderAndFooter">
|
||||
<!-- TODO: Logo <img *ngIf="enableLogo" id="logo"> -->
|
||||
<div *ngIf="enableTitle" id="eventdata">
|
||||
<div
|
||||
*ngIf="eventName"
|
||||
class="event-name"
|
||||
[ngClass]="!eventDescription ? 'titleonly' : ''"
|
||||
[innerHTML]="eventName"
|
||||
></div>
|
||||
<div *ngIf="eventDescription" class="event-description" [innerHTML]="eventDescription"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -10,8 +17,12 @@
|
||||
<os-slide-container [slideData]="slide" [scroll]="scroll" [scale]="scale"></os-slide-container>
|
||||
</div>
|
||||
|
||||
<div class="footer" [ngStyle]="headerFooterStyle" *ngIf="enableHeaderAndFooter">
|
||||
Footer
|
||||
<div id="footer" [ngStyle]="headerFooterStyle" *ngIf="enableHeaderAndFooter">
|
||||
<div class="footertext">
|
||||
<span *ngIf="eventDate"> {{ eventDate }} </span>
|
||||
<span *ngIf="eventDate && eventLocation"> | </span>
|
||||
<span *ngIf="eventLocation"> {{ eventLocation }} </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,29 +9,76 @@
|
||||
transform-origin: left top;
|
||||
overflow: hidden;
|
||||
|
||||
.header {
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
height: 70px;
|
||||
box-shadow: 0 0 7px rgba(0, 0, 0, 0.6);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
margin-bottom: 20px;
|
||||
z-index: 1;
|
||||
|
||||
#logo {
|
||||
padding-left: 50px;
|
||||
padding-top: 10px;
|
||||
height: 50px;
|
||||
margin-right: 25px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#eventdata {
|
||||
padding-left: 50px;
|
||||
padding-top: 12px;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
line-height: 1.1;
|
||||
|
||||
.event-name {
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
|
||||
&.titleonly {
|
||||
padding-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.event-description {
|
||||
font-size: 18px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
left: 50px;
|
||||
right: 50px;
|
||||
}
|
||||
.footer {
|
||||
position: absolute;
|
||||
color: white;
|
||||
|
||||
#footer {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
height: 35px;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
|
||||
.footertext {
|
||||
font-size: 16px;
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
padding-top: 5px;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
span {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,10 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
|
||||
public enableHeaderAndFooter = true;
|
||||
public enableTitle = true;
|
||||
public enableLogo = true;
|
||||
public eventName;
|
||||
public eventDescription;
|
||||
public eventDate;
|
||||
public eventLocation;
|
||||
|
||||
/**
|
||||
* Listen to all related config variables. Register the resizeSubject.
|
||||
@ -166,6 +170,10 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
|
||||
this.configService
|
||||
.get<string>('projector_background_color')
|
||||
.subscribe(val => (this.projectorStyle['background-color'] = val));
|
||||
this.configService.get<string>('general_event_name').subscribe(val => (this.eventName = val));
|
||||
this.configService.get<string>('general_event_description').subscribe(val => (this.eventDescription = val));
|
||||
this.configService.get<string>('general_event_date').subscribe(val => (this.eventDate = val));
|
||||
this.configService.get<string>('general_event_location').subscribe(val => (this.eventLocation = val));
|
||||
|
||||
// Watches for resizing of the container.
|
||||
this.resizeSubject.subscribe(() => {
|
||||
|
@ -1,3 +1,6 @@
|
||||
#slide {
|
||||
width: calc(100% - 100px);
|
||||
}
|
||||
::ng-deep #slide {
|
||||
z-index: 5;
|
||||
height: 100%;
|
||||
|
@ -95,7 +95,7 @@ export class SlideContainerComponent extends BaseComponent {
|
||||
*/
|
||||
public slideStyle: { 'font-size': string; 'margin-top': string } = {
|
||||
'font-size': '100%',
|
||||
'margin-top': '50px'
|
||||
'margin-top': '100px'
|
||||
};
|
||||
|
||||
/**
|
||||
@ -124,7 +124,7 @@ export class SlideContainerComponent extends BaseComponent {
|
||||
let value = this._scroll;
|
||||
value *= -50;
|
||||
if (this.headerEnabled) {
|
||||
value += 50; // Default offset for the header
|
||||
value += 100; // Default offset for the header
|
||||
}
|
||||
this.slideStyle['margin-top'] = `${value}px`;
|
||||
} else {
|
||||
|
@ -25,16 +25,16 @@
|
||||
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
||||
<!-- Selector column -->
|
||||
<ng-container matColumnDef="selector">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="icon-cell"></mat-header-cell>
|
||||
<mat-cell *matCellDef="let user" (click)="selectItem(user, $event)" class="icon-cell">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header></mat-header-cell>
|
||||
<mat-cell *matCellDef="let user" (click)="selectItem(user, $event)">
|
||||
<mat-icon>{{ isSelected(user) ? 'check_circle' : '' }}</mat-icon>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Projector column -->
|
||||
<ng-container matColumnDef="projector">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="icon-cell">Projector</mat-header-cell>
|
||||
<mat-cell *matCellDef="let user" class="icon-cell">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Projector</mat-header-cell>
|
||||
<mat-cell *matCellDef="let user">
|
||||
<os-projector-button [object]="user"></os-projector-button>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
@ -49,7 +49,7 @@
|
||||
<ng-container matColumnDef="group">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Group</mat-header-cell>
|
||||
<mat-cell *matCellDef="let user">
|
||||
<div class="groupsCell">
|
||||
<div class='groupsCell'>
|
||||
<span *ngIf="user.groups && user.groups.length">
|
||||
<mat-icon>people</mat-icon>
|
||||
{{ user.groups }}
|
||||
|
@ -9,6 +9,10 @@
|
||||
}
|
||||
|
||||
.os-listview-table {
|
||||
.mat-column-projector {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.mat-column-name {
|
||||
flex: 1 0 200px;
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ import { UserRepositoryService } from '../../services/user-repository.service';
|
||||
import { ViewUser } from '../../models/view-user';
|
||||
import { UserFilterListService } from '../../services/user-filter-list.service';
|
||||
import { UserSortListService } from '../../services/user-sort-list.service';
|
||||
import { ViewportService } from '../../../../core/services/viewport.service';
|
||||
import { OperatorService } from '../../../../core/services/operator.service';
|
||||
|
||||
/**
|
||||
* Component for the user list view.
|
||||
@ -25,6 +27,16 @@ import { UserSortListService } from '../../services/user-sort-list.service';
|
||||
styleUrls: ['./user-list.component.scss']
|
||||
})
|
||||
export class UserListComponent extends ListViewBaseComponent<ViewUser> implements OnInit {
|
||||
/**
|
||||
* Columns to display in table when desktop view is available
|
||||
*/
|
||||
public displayedColumnsDesktop: string[] = ['name', 'group'];
|
||||
|
||||
/**
|
||||
* Columns to display in table when mobile view is available
|
||||
*/
|
||||
public displayedColumnsMobile = ['name'];
|
||||
|
||||
/**
|
||||
* Stores the observed configuration if the presence view is available to administrators
|
||||
*/
|
||||
@ -48,6 +60,8 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
||||
* @param groupRepo: The user group repository
|
||||
* @param router the router service
|
||||
* @param route the local route
|
||||
* @param operator
|
||||
* @param vp
|
||||
* @param csvExport CSV export Service,
|
||||
* @param promptService
|
||||
* @param groupRepo
|
||||
@ -64,6 +78,8 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
||||
private choiceService: ChoiceService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private operator: OperatorService,
|
||||
private vp: ViewportService,
|
||||
protected csvExport: CsvExportService,
|
||||
private promptService: PromptService,
|
||||
public filterService: UserFilterListService,
|
||||
@ -243,10 +259,15 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
||||
* @returns column definition
|
||||
*/
|
||||
public getColumnDefinition(): string[] {
|
||||
// TODO: no projector in mobile view.
|
||||
const columns = ['projector', 'name', 'group', 'presence'];
|
||||
let columns = this.vp.isMobile ? this.displayedColumnsMobile : this.displayedColumnsDesktop;
|
||||
if (this.operator.hasPerms('core.can_manage_projector')) {
|
||||
columns = ['projector'].concat(columns);
|
||||
}
|
||||
if (this.operator.hasPerms('users.can_manage')) {
|
||||
columns = columns.concat(['presence']);
|
||||
}
|
||||
if (this.isMultiSelect) {
|
||||
return ['selector'].concat(columns);
|
||||
columns = ['selector'].concat(columns);
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
@ -6,10 +6,16 @@
|
||||
height: 30px;
|
||||
margin: 12px;
|
||||
z-index: 2;
|
||||
padding-right: 50px;
|
||||
padding-top: 5px;
|
||||
|
||||
mat-icon {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
span {
|
||||
padding-left: 5px;
|
||||
font-size: 16px;
|
||||
font-size: 24px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,15 @@
|
||||
export interface MotionsMotionSlideData {
|
||||
identifier: string;
|
||||
title: string;
|
||||
text: string;
|
||||
reason?: string;
|
||||
is_child: boolean;
|
||||
show_meta_box: boolean;
|
||||
submitter?: string[];
|
||||
recommender?: string;
|
||||
recommendation?: string;
|
||||
recommendation_extension?: string;
|
||||
amendment_paragraphs: { paragraph: string }[];
|
||||
change_recommendations: object[];
|
||||
modified_final_version?: string;
|
||||
}
|
||||
|
@ -1,4 +1,35 @@
|
||||
<div *ngIf="data">
|
||||
Motion Slide
|
||||
<h1>{{ data.data.title }}</h1>
|
||||
<div id="sidebox" *ngIf="data.data.show_meta_box">
|
||||
<!-- Submitters -->
|
||||
<h3 translate>Submitters</h3>
|
||||
<span *ngFor="let submitter of data.data.submitter; let last = last">
|
||||
{{ submitter }}<span *ngIf="!last">, </span>
|
||||
</span>
|
||||
|
||||
<!-- Recommendation -->
|
||||
<div *ngIf="data.data.recommendation && data.data.recommender">
|
||||
<h3>{{ data.data.recommender }}</h3>
|
||||
{{ data.data.recommendation | translate }} {{ data.data.recommendation_extension }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div [ngStyle]="{'width': data.data.show_meta_box ? 'calc(100% - 250px)' : '100%'}">
|
||||
<!-- Title -->
|
||||
<div class="slidetitle">
|
||||
<h1>{{ data.data.title }}</h1>
|
||||
<h2><span translate>Motion</span> {{ data.data.identifier }}</h2>
|
||||
</div>
|
||||
|
||||
<!-- Text (original) -->
|
||||
<div *ngIf="!data.data.is_child" [innerHTML]="data.data.text"></div>
|
||||
|
||||
<!-- Amendment text -->
|
||||
<div *ngIf="data.data.is_child" [innerHTML]="data.data.amendment_paragraphs[0]"></div>
|
||||
|
||||
<!-- Reason -->
|
||||
<div *ngIf="data.data.reason">
|
||||
<h3 translate>Reason</h3>
|
||||
<div [innerHTML]="data.data.reason"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,3 +1,15 @@
|
||||
div {
|
||||
background-color: red;
|
||||
#sidebox {
|
||||
width: 260px;
|
||||
right: 0;
|
||||
margin-top: 50px;
|
||||
background: #d3d3d3;
|
||||
border-radius: 7px 0 0 7px;
|
||||
padding: 3px 7px 10px 10px;
|
||||
position: fixed;
|
||||
z-index: 5;
|
||||
|
||||
h3 {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
export interface UsersUserSlideData {
|
||||
test: string;
|
||||
user: string;
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
<div id="outer">
|
||||
User Slide
|
||||
<div *ngIf="data">
|
||||
<h1>{{ data.data.user }}</h1>
|
||||
</div>
|
||||
|
@ -41,7 +41,9 @@ body {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
.title-font {
|
||||
.title-font,
|
||||
.slidetitle h1,
|
||||
.slidetitle h2 {
|
||||
font-family: OSFont Condensed, Fira Sans Condensed, Roboto-condensed, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
@ -232,6 +234,15 @@ mat-card {
|
||||
.os-listview-table {
|
||||
@extend %os-table;
|
||||
|
||||
/* multi select column */
|
||||
.mat-column-selector {
|
||||
flex: 0 0 60px;
|
||||
}
|
||||
/* projector button column */
|
||||
.mat-column-projector {
|
||||
flex: 0 0 40px;
|
||||
overflow: visible;
|
||||
}
|
||||
/** hide mat header row */
|
||||
.mat-header-row {
|
||||
display: none;
|
||||
@ -242,6 +253,7 @@ mat-card {
|
||||
@extend %os-table;
|
||||
}
|
||||
|
||||
|
||||
/* TODO: find a better way to get more vertical space in (empty/small) tables for maximize filter dialog */
|
||||
mat-paginator {
|
||||
min-height: 800px;
|
||||
@ -292,10 +304,6 @@ mat-expansion-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon-cell {
|
||||
flex: 0 0 40px;
|
||||
}
|
||||
|
||||
// ngx-file-drop requires the custom style in the global css file
|
||||
.file-drop-style {
|
||||
margin: auto;
|
||||
@ -565,6 +573,7 @@ button.mat-menu-item.selected {
|
||||
flex: 1;
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
.filter-imports {
|
||||
max-width: 50%;
|
||||
}
|
||||
@ -574,3 +583,42 @@ button.mat-menu-item.selected {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
/*** Projector slides ***/
|
||||
|
||||
#slide {
|
||||
|
||||
h3 {
|
||||
color: #222;
|
||||
margin-bottom: 10px
|
||||
}
|
||||
|
||||
.slidetitle {
|
||||
border-bottom: 5px solid #d3d3d3;
|
||||
margin-bottom: 40px;
|
||||
|
||||
h1 {
|
||||
font-size: 2.25em;
|
||||
line-height: 1.1em;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
h2 {
|
||||
color: #9a9898;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0px;
|
||||
font-size: 28px;
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
@ -107,5 +107,9 @@ def current_list_of_speakers_slide(
|
||||
def register_projector_slides() -> None:
|
||||
register_projector_slide("agenda/item-list", items_slide)
|
||||
register_projector_slide("agenda/list-of-speakers", list_of_speakers_slide)
|
||||
register_projector_slide("agenda/current-list-of-speakers", current_list_of_speakers_slide)
|
||||
register_projector_element("agenda/current-list-of-speakers-overlay", current_list_of_speakers_slide)
|
||||
register_projector_slide(
|
||||
"agenda/current-list-of-speakers", current_list_of_speakers_slide
|
||||
)
|
||||
register_projector_slide(
|
||||
"agenda/current-list-of-speakers-overlay", current_list_of_speakers_slide
|
||||
)
|
||||
|
@ -54,4 +54,4 @@ def clock_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def register_projector_slides() -> None:
|
||||
register_projector_slide("core/countdown", countdown_slide)
|
||||
register_projector_slide("core/projector-message", message_slide)
|
||||
register_projector_element("core/clock", clock_slide)
|
||||
register_projector_slide("core/clock", clock_slide)
|
||||
|
@ -173,7 +173,9 @@ class ProjectorViewSet(ModelViewSet):
|
||||
elements = request.data.get("elements")
|
||||
preview = request.data.get("preview")
|
||||
history_element = request.data.get("append_to_history")
|
||||
delete_last_history_element = request.data.get("delete_last_history_element", False)
|
||||
delete_last_history_element = request.data.get(
|
||||
"delete_last_history_element", False
|
||||
)
|
||||
|
||||
changed_data = {}
|
||||
if elements is not None:
|
||||
|
@ -45,12 +45,11 @@ def motion_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]:
|
||||
* show_meta_box
|
||||
* reason
|
||||
* modified_final_version
|
||||
* state
|
||||
* state_extension
|
||||
* recommendation
|
||||
* recommendation_extension
|
||||
* recommender
|
||||
* change_recommendations
|
||||
* submitter
|
||||
* poll
|
||||
"""
|
||||
mode = element.get("mode")
|
||||
motion_id = element.get("id")
|
||||
@ -81,11 +80,6 @@ def motion_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]:
|
||||
return_value["modified_final_version"] = motion["modified_final_version"]
|
||||
|
||||
if show_meta_box:
|
||||
state = get_state(all_data, motion, motion["state_id"])
|
||||
return_value["state"] = state["name"]
|
||||
if state["show_state_extension_field"]:
|
||||
return_value["state_extension"] = motion["state_extension"]
|
||||
|
||||
if (
|
||||
not get_config(all_data, "motions_disable_recommendation_on_projector")
|
||||
and motion["recommendation_id"]
|
||||
@ -101,6 +95,9 @@ def motion_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"recommendation_extension"
|
||||
]
|
||||
|
||||
return_value["recommender"] = get_config(
|
||||
all_data, "motions_recommendations_by"
|
||||
)
|
||||
return_value["change_recommendations"] = motion["change_recommendations"]
|
||||
|
||||
return_value["submitter"] = [
|
||||
@ -110,15 +107,6 @@ def motion_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]:
|
||||
)
|
||||
]
|
||||
|
||||
for poll in motion["polls"][::-1]:
|
||||
if poll["has_votes"]:
|
||||
return_value["poll"] = {
|
||||
"yes": poll["yes"],
|
||||
"no": poll["no"],
|
||||
"abstain": poll["abstain"],
|
||||
}
|
||||
break
|
||||
|
||||
return return_value
|
||||
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from ..utils.projector import AllData, register_projector_slide
|
||||
from ..utils.projector import (
|
||||
AllData,
|
||||
ProjectorElementException,
|
||||
register_projector_slide,
|
||||
)
|
||||
|
||||
|
||||
# Important: All functions have to be prune. This means, that thay can only
|
||||
@ -12,8 +16,22 @@ from ..utils.projector import AllData, register_projector_slide
|
||||
def user_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
User slide.
|
||||
|
||||
The returned dict can contain the following fields:
|
||||
* user
|
||||
"""
|
||||
return {"error": "TODO"}
|
||||
user_id = element.get("id")
|
||||
|
||||
if user_id is None:
|
||||
return {"error": "id is required for user slide"}
|
||||
|
||||
try:
|
||||
user = all_data["users/user"][user_id]
|
||||
except KeyError:
|
||||
raise ProjectorElementException(f"user with id {user_id} does not exist")
|
||||
|
||||
return_value = {"user": get_user_name(all_data, user["id"])}
|
||||
return return_value
|
||||
|
||||
|
||||
def get_user_name(all_data: AllData, user_id: int) -> str:
|
||||
|
@ -166,7 +166,5 @@ def test_motion_slide(all_data):
|
||||
"is_child": False,
|
||||
"show_meta_box": True,
|
||||
"reason": "",
|
||||
"state": "submitted",
|
||||
"submitter": ["Administrator"],
|
||||
"poll": {"yes": "10.000000", "no": "-1.000000", "abstain": "20.000000"},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user