Fixes dynamic growing cells in workflows

- The cells in the table of a workflow are dynamically growing
- The colors of the states get new labels
- If multiple selection in motion-list is active, the button to change the view won't be seen
This commit is contained in:
GabrielMeyer 2019-07-12 10:14:35 +02:00 committed by FinnStutzenstein
parent 93e37720dc
commit bb55110245
12 changed files with 153 additions and 134 deletions

View File

@ -9,7 +9,7 @@ import { ViewUser } from 'app/site/users/models/view-user';
import { ViewTag } from 'app/site/tags/models/view-tag'; import { ViewTag } from 'app/site/tags/models/view-tag';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { ViewItem } from 'app/site/agenda/models/view-item'; import { ViewItem } from 'app/site/agenda/models/view-item';
import { ViewWorkflow, StateCssClassMapping } from './view-workflow'; import { ViewWorkflow } from './view-workflow';
import { ViewCategory } from './view-category'; import { ViewCategory } from './view-category';
import { ViewMotionBlock } from './view-motion-block'; import { ViewMotionBlock } from './view-motion-block';
import { BaseViewModel } from 'app/site/base/base-view-model'; import { BaseViewModel } from 'app/site/base/base-view-model';
@ -349,7 +349,7 @@ export class ViewMotion extends BaseViewModelWithAgendaItemAndListOfSpeakers<Mot
* @returns a string representing a color * @returns a string representing a color
*/ */
public get stateCssColor(): string { public get stateCssColor(): string {
return this.state ? StateCssClassMapping[this.state.css_class] : ''; return this.state ? this.state.css_class : '';
} }
// This is set by the repository // This is set by the repository

View File

@ -2,14 +2,6 @@ import { Workflow } from 'app/shared/models/motions/workflow';
import { WorkflowState } from 'app/shared/models/motions/workflow-state'; import { WorkflowState } from 'app/shared/models/motions/workflow-state';
import { BaseViewModel } from '../../base/base-view-model'; import { BaseViewModel } from '../../base/base-view-model';
export const StateCssClassMapping = {
success: 'green',
danger: 'red',
default: 'grey',
primary: 'lightblue',
warning: 'yellow'
};
export interface WorkflowTitleInformation { export interface WorkflowTitleInformation {
name: string; name: string;
} }

View File

@ -16,7 +16,7 @@
</div> </div>
<div class="extra-controls-slot"> <div class="extra-controls-slot">
<div *ngIf="isCategoryAvailable()"> <div *ngIf="isCategoryAvailable() && !isMultiSelect">
<button <button
mat-button mat-button
*ngIf="selectedView !== 'tiles'" *ngIf="selectedView !== 'tiles'"

View File

@ -26,105 +26,111 @@
<div class="scrollable-matrix"> <div class="scrollable-matrix">
<table mat-table [dataSource]="getTableDataSource()"> <table mat-table [dataSource]="getTableDataSource()">
<ng-container matColumnDef="perm" sticky> <ng-container matColumnDef="perm" sticky>
<mat-header-cell class="group-head-table-cell" *matHeaderCellDef translate>Permissions</mat-header-cell> <th mat-header-cell class="group-head-table-cell" *matHeaderCellDef translate>Permissions</th>
<mat-cell *matCellDef="let perm"> <td mat-cell *matCellDef="let perm">
<div class="permission-name"> <div class="permission-name">
{{ perm.name | translate }} {{ perm.name | translate }}
</div> </div>
</mat-cell> </td>
</ng-container> </ng-container>
<div *ngFor="let state of workflow.states; trackBy: trackByIndex"> <ng-container [matColumnDef]="getColumnDef(state)" *ngFor="let state of workflow.states; trackBy: trackByIndex">
<ng-container [matColumnDef]="getColumnDef(state)"> <th mat-header-cell *matHeaderCellDef (click)="onClickStateName(state)">
<mat-header-cell *matHeaderCellDef (click)="onClickStateName(state)"> <div class="clickable-cell stretch-to-fill-parent">
<div class="clickable-cell"> <div class="inner-table">
<div class="inner-table"> {{ state.name | translate }}
{{ state.name | translate }}
</div>
</div> </div>
</mat-header-cell> </div>
<mat-cell *matCellDef="let perm"> </th>
<div class="inner-table" *ngIf="perm.type === 'check'"> <td mat-cell *matCellDef="let perm">
<mat-checkbox <div class="inner-table" *ngIf="perm.type === 'check'">
[checked]="state[perm.selector]" <mat-checkbox
(change)="onToggleStatePerm(state, perm.selector, $event)" [checked]="state[perm.selector]"
></mat-checkbox> (change)="onToggleStatePerm(state, perm.selector, $event)"
></mat-checkbox>
</div>
<div
*ngIf="perm.type === 'input'"
>
<div class="inner-table">
{{ (state[perm.selector] | translate) || '' }}
</div> </div>
<div <div
class="clickable-cell" class="clickable-cell stretch-to-fill-parent"
*ngIf="perm.type === 'input'"
(click)="onClickInputPerm(perm, state)" (click)="onClickInputPerm(perm, state)"
></div>
</div>
<div class="inner-table" *ngIf="perm.type === 'color'">
<mat-basic-chip
[matMenuTriggerFor]="colorMenu"
[matMenuTriggerData]="{ state: state }"
[disableRipple]="true"
[ngClass]="state[perm.selector]"
> >
<div class="inner-table"> {{ state[perm.selector] | translate }}
{{ (state[perm.selector] | translate) || '' }} </mat-basic-chip>
</div>
<div
*ngIf="perm.type === 'state'"
>
<div class="inner-table">
<div *ngIf="!state.next_states_id.length">
-
</div>
<div *ngIf="state.next_states_id.length">
<div
*ngFor="
let nextstate of state.getNextStates(workflow.workflow);
let last = last
"
>
{{ nextstate.name | translate }}<span *ngIf="!last">,&nbsp;</span>
</div>
</div> </div>
</div> </div>
<div class="inner-table" *ngIf="perm.type === 'color'">
<mat-basic-chip
[matMenuTriggerFor]="colorMenu"
[matMenuTriggerData]="{ state: state }"
[disableRipple]="true"
[ngClass]="getStateCssColor(state[perm.selector])"
>
{{ state[perm.selector] | translate }}
</mat-basic-chip>
</div>
<div <div
class="clickable-cell" class="clickable-cell stretch-to-fill-parent"
*ngIf="perm.type === 'state'"
[matMenuTriggerFor]="nextStatesMenu" [matMenuTriggerFor]="nextStatesMenu"
[matMenuTriggerData]="{ state: state }" [matMenuTriggerData]="{ state: state }"
> ></div>
<div class="inner-table"> </div>
<div *ngIf="!state.next_states_id.length"> <div
- *ngIf="perm.type === 'amendment'"
</div> >
<div *ngIf="state.next_states_id.length"> <div class="inner-table">
<span {{ getMergeAmendmentLabel(state.merge_amendment_into_final) | translate }}
*ngFor="
let nextstate of state.getNextStates(workflow.workflow);
let last = last
"
>
{{ nextstate.name | translate }}<span *ngIf="!last">,&nbsp;</span>
</span>
</div>
</div>
</div> </div>
<div <div
class="clickable-cell" class="clickable-cell stretch-to-fill-parent"
*ngIf="perm.type === 'amendment'"
[matMenuTriggerFor]="mergeAmendmentMenu" [matMenuTriggerFor]="mergeAmendmentMenu"
[matMenuTriggerData]="{ state: state }" [matMenuTriggerData]="{ state: state }"
> ></div>
<div class="inner-table"> </div>
{{ getMergeAmendmentLabel(state.merge_amendment_into_final) | translate }} <div
*ngIf="perm.type === 'restriction'"
>
<div class="inner-table">
<div *ngIf="!state.restriction.length">
-
</div>
<div *ngIf="state.restriction.length">
<div *ngFor="let restriction of state.restriction; let last = last">
{{ getRestrictionLabel(restriction) | translate
}}<span *ngIf="!last">,&nbsp;</span>
</div>
</div> </div>
</div> </div>
<div <div
class="clickable-cell" class="clickable-cell stretch-to-fill-parent"
*ngIf="perm.type === 'restriction'"
[matMenuTriggerFor]="restrictionMenu" [matMenuTriggerFor]="restrictionMenu"
[matMenuTriggerData]="{ state: state }" [matMenuTriggerData]="{ state: state }"
> ></div>
<div class="inner-table"> </div>
<div *ngIf="!state.restriction.length"> </td>
- </ng-container>
</div>
<div *ngIf="state.restriction.length">
<span *ngFor="let restriction of state.restriction; let last = last">
{{ getRestrictionLabel(restriction) | translate
}}<span *ngIf="!last">,&nbsp;</span>
</span>
</div>
</div>
</div>
</mat-cell>
</ng-container>
</div>
<mat-header-row *matHeaderRowDef="headerRowDef"></mat-header-row> <tr mat-header-row *matHeaderRowDef="headerRowDef"></tr>
<mat-row *matRowDef="let row; columns: headerRowDef"></mat-row> <tr mat-row *matRowDef="let row; columns: headerRowDef"></tr>
</table> </table>
</div> </div>
</div> </div>

View File

@ -5,35 +5,25 @@
} }
table { table {
.mat-header-cell { .mat-header-cell,
min-width: 150px;
position: relative;
}
.mat-cell { .mat-cell {
min-width: 150px; min-width: 200px;
position: relative; position: relative;
} }
// scaled up version of an inner table
.clickable-cell { .clickable-cell {
position: absolute;
display: flex; display: flex;
width: 100%;
height: 100%;
}
.clickable-cell:hover {
cursor: pointer; cursor: pointer;
background-color: rgba(0, 0, 0, 0.025); &:hover {
background-color: rgba(0, 0, 0, 0.025);
}
} }
.inner-table { .inner-table {
text-align: center;
align-items: center; align-items: center;
margin: auto; margin: auto;
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
padding: 0 10px; padding: 0 10px;
} }

View File

@ -10,7 +10,7 @@ import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BaseViewComponent } from 'app/site/base/base-view'; import { BaseViewComponent } from 'app/site/base/base-view';
import { ViewWorkflow, StateCssClassMapping } from 'app/site/motions/models/view-workflow'; import { ViewWorkflow } from 'app/site/motions/models/view-workflow';
import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service'; import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service';
import { WorkflowState, MergeAmendment } from 'app/shared/models/motions/workflow-state'; import { WorkflowState, MergeAmendment } from 'app/shared/models/motions/workflow-state';
import { PromptService } from 'app/core/ui-services/prompt.service'; import { PromptService } from 'app/core/ui-services/prompt.service';
@ -93,7 +93,7 @@ export class WorkflowDetailComponent extends BaseViewComponent implements OnInit
/** /**
* Determine label colors. Where they should come from is currently now know * Determine label colors. Where they should come from is currently now know
*/ */
public labelColors = ['default', 'primary', 'success', 'danger', 'warning']; public labelColors: string[] = ['grey', 'red', 'green', 'lightblue', 'yellow'];
/** /**
* Holds state permissions * Holds state permissions
@ -396,14 +396,4 @@ export class WorkflowDetailComponent extends BaseViewComponent implements OnInit
public trackElement(index: number): number { public trackElement(index: number): number {
return index; return index;
} }
/**
* Translate the state's css class into a color
*
* @param colorLabel the default color label of a selected workflow
* @returns a string representing a color
*/
public getStateCssColor(colorLabel: string): string {
return StateCssClassMapping[colorLabel] || '';
}
} }

View File

@ -4,7 +4,6 @@ import { TranslateService } from '@ngx-translate/core';
import { MotionBlockSlideData, MotionBlockSlideMotionRepresentation } from './motion-block-slide-data'; import { MotionBlockSlideData, MotionBlockSlideMotionRepresentation } from './motion-block-slide-data';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { StateCssClassMapping } from 'app/site/motions/models/view-workflow';
import { BaseMotionSlideComponent } from '../base/base-motion-slide'; import { BaseMotionSlideComponent } from '../base/base-motion-slide';
import { SlideData } from 'app/core/core-services/projector-data.service'; import { SlideData } from 'app/core/core-services/projector-data.service';
@ -187,6 +186,6 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
* @returns the css color for the state of the motion in cell i and j * @returns the css color for the state of the motion in cell i and j
*/ */
public getStateCssColor(i: number, j: number): string { public getStateCssColor(i: number, j: number): string {
return StateCssClassMapping[this.getMotion(i, j).recommendation.css_class] || ''; return this.getMotion(i, j).recommendation.css_class || '';
} }
} }

View File

@ -0,0 +1,15 @@
# Generated by Finn Stutzenstein on 2019-07-17 08:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("motions", "0029_motioncommentsection_weight")]
operations = [
migrations.AlterField(
model_name="state",
name="css_class",
field=models.CharField(default="lightblue", max_length=255),
)
]

View File

@ -0,0 +1,28 @@
# Generated by Finn Stutzenstein on 2019-07-17 08:43
from django.db import migrations
LABEL_MAPPING = {
"default": "grey",
"primary": "lightblue",
"success": "green",
"danger": "red",
"warning": "yellow",
}
def rename_css_classes(apps, schema_editor):
State = apps.get_model("motions", "State")
for state in State.objects.all():
old_class = state.css_class
state.css_class = LABEL_MAPPING.get(old_class, "lightblue")
state.save(skip_autoupdate=True)
class Migration(migrations.Migration):
dependencies = [("motions", "0030_state_css_classes_1")]
operations = [migrations.RunPython(rename_css_classes)]

View File

@ -993,11 +993,10 @@ class State(RESTModelMixin, models.Model):
next_states = models.ManyToManyField("self", symmetrical=False, blank=True) next_states = models.ManyToManyField("self", symmetrical=False, blank=True)
"""A many-to-many relation to all states, that can be choosen from this state.""" """A many-to-many relation to all states, that can be choosen from this state."""
css_class = models.CharField(max_length=255, default="primary") css_class = models.CharField(max_length=255, default="lightblue")
""" """
A css class string for showing the state name in a coloured label based on bootstrap, A css class string for showing the state name in a coloured label. Currently supported
e.g. 'danger' (red), 'success' (green), 'warning' (yellow), 'default' (grey). values are grey, red, green, lightblue and yellow. The default is lightblue.
Default value is 'primary' (blue).
""" """
restriction = JSONField(default=list) restriction = JSONField(default=list)

View File

@ -26,7 +26,7 @@ def create_builtin_workflows(sender, **kwargs):
name="accepted", name="accepted",
workflow=workflow_1, workflow=workflow_1,
recommendation_label="Acceptance", recommendation_label="Acceptance",
css_class="success", css_class="green",
merge_amendment_into_final=1, merge_amendment_into_final=1,
) )
state_1_2.save(skip_autoupdate=True) state_1_2.save(skip_autoupdate=True)
@ -34,7 +34,7 @@ def create_builtin_workflows(sender, **kwargs):
name="rejected", name="rejected",
workflow=workflow_1, workflow=workflow_1,
recommendation_label="Rejection", recommendation_label="Rejection",
css_class="danger", css_class="red",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_1_3.save(skip_autoupdate=True) state_1_3.save(skip_autoupdate=True)
@ -42,7 +42,7 @@ def create_builtin_workflows(sender, **kwargs):
name="not decided", name="not decided",
workflow=workflow_1, workflow=workflow_1,
recommendation_label="No decision", recommendation_label="No decision",
css_class="default", css_class="grey",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_1_4.save(skip_autoupdate=True) state_1_4.save(skip_autoupdate=True)
@ -77,7 +77,7 @@ def create_builtin_workflows(sender, **kwargs):
name="accepted", name="accepted",
workflow=workflow_2, workflow=workflow_2,
recommendation_label="Acceptance", recommendation_label="Acceptance",
css_class="success", css_class="green",
merge_amendment_into_final=1, merge_amendment_into_final=1,
) )
state_2_3.save(skip_autoupdate=True) state_2_3.save(skip_autoupdate=True)
@ -85,14 +85,14 @@ def create_builtin_workflows(sender, **kwargs):
name="rejected", name="rejected",
workflow=workflow_2, workflow=workflow_2,
recommendation_label="Rejection", recommendation_label="Rejection",
css_class="danger", css_class="red",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_2_4.save(skip_autoupdate=True) state_2_4.save(skip_autoupdate=True)
state_2_5 = State( state_2_5 = State(
name="withdrawed", name="withdrawed",
workflow=workflow_2, workflow=workflow_2,
css_class="default", css_class="grey",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_2_5.save(skip_autoupdate=True) state_2_5.save(skip_autoupdate=True)
@ -100,7 +100,7 @@ def create_builtin_workflows(sender, **kwargs):
name="adjourned", name="adjourned",
workflow=workflow_2, workflow=workflow_2,
recommendation_label="Adjournment", recommendation_label="Adjournment",
css_class="default", css_class="grey",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_2_6.save(skip_autoupdate=True) state_2_6.save(skip_autoupdate=True)
@ -108,7 +108,7 @@ def create_builtin_workflows(sender, **kwargs):
name="not concerned", name="not concerned",
workflow=workflow_2, workflow=workflow_2,
recommendation_label="No concernment", recommendation_label="No concernment",
css_class="default", css_class="grey",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_2_7.save(skip_autoupdate=True) state_2_7.save(skip_autoupdate=True)
@ -116,14 +116,14 @@ def create_builtin_workflows(sender, **kwargs):
name="refered to committee", name="refered to committee",
workflow=workflow_2, workflow=workflow_2,
recommendation_label="Referral to committee", recommendation_label="Referral to committee",
css_class="default", css_class="grey",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_2_8.save(skip_autoupdate=True) state_2_8.save(skip_autoupdate=True)
state_2_9 = State( state_2_9 = State(
name="needs review", name="needs review",
workflow=workflow_2, workflow=workflow_2,
css_class="default", css_class="grey",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_2_9.save(skip_autoupdate=True) state_2_9.save(skip_autoupdate=True)
@ -131,7 +131,7 @@ def create_builtin_workflows(sender, **kwargs):
name="rejected (not authorized)", name="rejected (not authorized)",
workflow=workflow_2, workflow=workflow_2,
recommendation_label="Rejection (not authorized)", recommendation_label="Rejection (not authorized)",
css_class="default", css_class="grey",
merge_amendment_into_final=-1, merge_amendment_into_final=-1,
) )
state_2_10.save(skip_autoupdate=True) state_2_10.save(skip_autoupdate=True)

View File

@ -177,7 +177,7 @@ def all_data():
"id": 1, "id": 1,
"name": "submitted", "name": "submitted",
"recommendation_label": None, "recommendation_label": None,
"css_class": "primary", "css_class": "lightblue",
"restriction": [], "restriction": [],
"allow_support": True, "allow_support": True,
"allow_create_poll": True, "allow_create_poll": True,
@ -193,7 +193,7 @@ def all_data():
"id": 2, "id": 2,
"name": "accepted", "name": "accepted",
"recommendation_label": "Acceptance", "recommendation_label": "Acceptance",
"css_class": "success", "css_class": "green",
"restriction": [], "restriction": [],
"allow_support": False, "allow_support": False,
"allow_create_poll": False, "allow_create_poll": False,
@ -209,7 +209,7 @@ def all_data():
"id": 3, "id": 3,
"name": "rejected", "name": "rejected",
"recommendation_label": "Rejection", "recommendation_label": "Rejection",
"css_class": "danger", "css_class": "red",
"restriction": [], "restriction": [],
"allow_support": False, "allow_support": False,
"allow_create_poll": False, "allow_create_poll": False,
@ -225,7 +225,7 @@ def all_data():
"id": 4, "id": 4,
"name": "not decided", "name": "not decided",
"recommendation_label": "No decision", "recommendation_label": "No decision",
"css_class": "default", "css_class": "grey",
"restriction": [], "restriction": [],
"allow_support": False, "allow_support": False,
"allow_create_poll": False, "allow_create_poll": False,