Add gender field to users
Alters the user detail view and the list of speakers to support the gender field. Default selection of genders was set to "Female", "Male" and "Diverse". Adding genders to users is completely optional
This commit is contained in:
parent
c67aef68d9
commit
07ca50441a
@ -2,6 +2,11 @@ import { Searchable } from '../base/searchable';
|
|||||||
import { SearchRepresentation } from '../../../core/services/search.service';
|
import { SearchRepresentation } from '../../../core/services/search.service';
|
||||||
import { BaseModel } from '../base/base-model';
|
import { BaseModel } from '../base/base-model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterable pre selection of genders (sexes)
|
||||||
|
*/
|
||||||
|
export const genders = ['Female', 'Male', 'Diverse'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a user in contrast to the operator.
|
* Representation of a user in contrast to the operator.
|
||||||
* @ignore
|
* @ignore
|
||||||
@ -14,6 +19,7 @@ export class User extends BaseModel<User> implements Searchable {
|
|||||||
public title: string;
|
public title: string;
|
||||||
public first_name: string;
|
public first_name: string;
|
||||||
public last_name: string;
|
public last_name: string;
|
||||||
|
public gender: string;
|
||||||
public structure_level: string;
|
public structure_level: string;
|
||||||
public number: string;
|
public number: string;
|
||||||
public about_me: string;
|
public about_me: string;
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
<h2><span translate>List of speakers</span></h2>
|
<h2><span translate>List of speakers</span></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-slot" *osPerms="'agenda.can_manage_list_of_speakers'">
|
<div class="menu-slot" *osPerms="'agenda.can_manage_list_of_speakers'">
|
||||||
<button type="button" mat-icon-button [matMenuTriggerFor]="speakerMenu">
|
<button type="button" mat-icon-button [matMenuTriggerFor]="speakerMenu"><mat-icon>more_vert</mat-icon></button>
|
||||||
<mat-icon>more_vert</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
@ -68,6 +66,7 @@
|
|||||||
<span *ngIf="hasSpokenCount(item)" class="red-warning-text speaker-warning">
|
<span *ngIf="hasSpokenCount(item)" class="red-warning-text speaker-warning">
|
||||||
{{ hasSpokenCount(item) + 1 }}. <span translate>contribution</span>
|
{{ hasSpokenCount(item) + 1 }}. <span translate>contribution</span>
|
||||||
</span>
|
</span>
|
||||||
|
<span *ngIf="item.gender">({{ item.gender | translate }})</span>
|
||||||
</span>
|
</span>
|
||||||
<mat-button-toggle-group *osPerms="'agenda.can_manage_list_of_speakers'">
|
<mat-button-toggle-group *osPerms="'agenda.can_manage_list_of_speakers'">
|
||||||
<mat-button-toggle matTooltip="{{ 'Begin speech' | translate }}" (click)="onStartButton(item)">
|
<mat-button-toggle matTooltip="{{ 'Begin speech' | translate }}" (click)="onStartButton(item)">
|
||||||
@ -117,7 +116,6 @@
|
|||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<mat-menu #speakerMenu="matMenu">
|
<mat-menu #speakerMenu="matMenu">
|
||||||
|
|
||||||
<button mat-menu-item *ngIf="closedList" (click)="openSpeakerList()">
|
<button mat-menu-item *ngIf="closedList" (click)="openSpeakerList()">
|
||||||
<mat-icon>mic</mat-icon>
|
<mat-icon>mic</mat-icon>
|
||||||
<span translate>Open list of speakers</span>
|
<span translate>Open list of speakers</span>
|
||||||
|
@ -46,6 +46,10 @@ export class ViewSpeaker extends BaseViewModel implements Selectable {
|
|||||||
return this.user.full_name || this.user.username;
|
return this.user.full_name || this.user.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get gender(): string {
|
||||||
|
return this.user.gender || '';
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(speaker?: Speaker, user?: User) {
|
public constructor(speaker?: Speaker, user?: User) {
|
||||||
super();
|
super();
|
||||||
this._speaker = speaker;
|
this._speaker = speaker;
|
||||||
|
@ -9,8 +9,7 @@
|
|||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="title-slot">
|
<div class="title-slot">
|
||||||
<h2>
|
<h2>
|
||||||
<span *ngIf="newUser" translate>New participant</span>
|
<span *ngIf="newUser" translate>New participant</span> <span *ngIf="!newUser">{{ user.full_name }}</span>
|
||||||
<span *ngIf="!newUser">{{ user.full_name }}</span>
|
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -39,61 +38,74 @@
|
|||||||
[formGroup]="personalInfoForm"
|
[formGroup]="personalInfoForm"
|
||||||
(ngSubmit)="saveUser()"
|
(ngSubmit)="saveUser()"
|
||||||
*ngIf="user"
|
*ngIf="user"
|
||||||
(keydown)="onKeyDown($event)">
|
(keydown)="onKeyDown($event)"
|
||||||
|
>
|
||||||
<!-- <h3 translate>Personal Data</h3> -->
|
<!-- <h3 translate>Personal Data</h3> -->
|
||||||
<div *ngIf="isAllowed('seeName')">
|
<div *ngIf="isAllowed('seeName')">
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<mat-form-field
|
<mat-form-field
|
||||||
class="form16 distance force-min-with"
|
class="form16 distance force-min-with"
|
||||||
*ngIf="user.title || (editUser && isAllowed('manage'))">
|
*ngIf="user.title || (editUser && isAllowed('manage'))"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
matInput
|
matInput
|
||||||
osAutofocus
|
osAutofocus
|
||||||
placeholder="{{ 'Title' | translate }}"
|
placeholder="{{ 'Title' | translate }}"
|
||||||
formControlName="title"
|
formControlName="title"
|
||||||
[value]="user.title"/>
|
[value]="user.title"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- First name -->
|
<!-- First name -->
|
||||||
<mat-form-field
|
<mat-form-field
|
||||||
class="form37 distance force-min-with"
|
class="form37 distance force-min-with"
|
||||||
*ngIf="user.first_name || (editUser && isAllowed('manage'))">
|
*ngIf="user.first_name || (editUser && isAllowed('manage'))"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Given name' | translate }}"
|
placeholder="{{ 'Given name' | translate }}"
|
||||||
formControlName="first_name"
|
formControlName="first_name"
|
||||||
[value]="user.first_name"/>
|
[value]="user.first_name"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- Last name -->
|
<!-- Last name -->
|
||||||
<mat-form-field
|
<mat-form-field class="form37 force-min-with" *ngIf="user.last_name || (editUser && isAllowed('manage'))">
|
||||||
class="form37 force-min-with"
|
|
||||||
*ngIf="user.last_name || (editUser && isAllowed('manage'))">
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Surname' | translate }}"
|
placeholder="{{ 'Surname' | translate }}"
|
||||||
formControlName="last_name"
|
formControlName="last_name"
|
||||||
[value]="user.last_name"/>
|
[value]="user.last_name"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="isAllowed('seePersonal')">
|
<div *ngIf="isAllowed('seePersonal')">
|
||||||
<!-- E-Mail -->
|
<!-- E-Mail -->
|
||||||
<mat-form-field *ngIf="user.email || editUser">
|
<mat-form-field class="form70 distance" *ngIf="user.email || editUser">
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Email' | translate }}"
|
placeholder="{{ 'Email' | translate }}"
|
||||||
name="email"
|
name="email"
|
||||||
formControlName="email"
|
formControlName="email"
|
||||||
[value]="user.email"/>
|
[value]="user.email"
|
||||||
|
/>
|
||||||
<mat-error *ngIf="personalInfoForm.get('email').hasError('email')" translate>
|
<mat-error *ngIf="personalInfoForm.get('email').hasError('email')" translate>
|
||||||
Please enter a valid email address
|
Please enter a valid email address
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Gender -->
|
||||||
|
<mat-form-field class="form25 force-min-with" *ngIf="user.gender || editUser">
|
||||||
|
<mat-select placeholder="{{ 'Gender' | translate }}" formControlName="gender">
|
||||||
|
<mat-option>-</mat-option>
|
||||||
|
<mat-option *ngFor="let gender of genderList" [value]="gender">{{ gender | translate }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -104,19 +116,22 @@
|
|||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Structure level' | translate }}"
|
placeholder="{{ 'Structure level' | translate }}"
|
||||||
formControlName="structure_level"
|
formControlName="structure_level"
|
||||||
[value]="user.structure_level"/>
|
[value]="user.structure_level"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- Participant Number -->
|
<!-- Participant Number -->
|
||||||
<mat-form-field
|
<mat-form-field
|
||||||
class="form25 force-min-with"
|
class="form25 force-min-with"
|
||||||
*ngIf="user.participant_number || (editUser && isAllowed('manage'))">
|
*ngIf="user.participant_number || (editUser && isAllowed('manage'))"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Participant number' | translate }}"
|
placeholder="{{ 'Participant number' | translate }}"
|
||||||
formControlName="number"
|
formControlName="number"
|
||||||
[value]="user.participant_number"/>
|
[value]="user.participant_number"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -136,7 +151,8 @@
|
|||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Initial password' | translate }}"
|
placeholder="{{ 'Initial password' | translate }}"
|
||||||
formControlName="default_password"
|
formControlName="default_password"
|
||||||
[value]="user.default_password"/>
|
[value]="user.default_password"
|
||||||
|
/>
|
||||||
<mat-hint align="end">Generate</mat-hint>
|
<mat-hint align="end">Generate</mat-hint>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -144,7 +160,8 @@
|
|||||||
matSuffix
|
matSuffix
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
[disabled]="!newUser"
|
[disabled]="!newUser"
|
||||||
(click)="generatePassword()">
|
(click)="generatePassword()"
|
||||||
|
>
|
||||||
<mat-icon>sync_problem</mat-icon>
|
<mat-icon>sync_problem</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
@ -166,7 +183,8 @@
|
|||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Username' | translate }}"
|
placeholder="{{ 'Username' | translate }}"
|
||||||
formControlName="username"
|
formControlName="username"
|
||||||
[value]="user.username"/>
|
[value]="user.username"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -177,7 +195,8 @@
|
|||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Comment' | translate }}"
|
placeholder="{{ 'Comment' | translate }}"
|
||||||
formControlName="comment"
|
formControlName="comment"
|
||||||
[value]="user.comment"/>
|
[value]="user.comment"
|
||||||
|
/>
|
||||||
<mat-hint translate>Only for internal notes.</mat-hint>
|
<mat-hint translate>Only for internal notes.</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
@ -187,7 +206,8 @@
|
|||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
formControlName="is_present"
|
formControlName="is_present"
|
||||||
matTooltip="{{ 'Designates whether this user is in the room.' | translate }}"
|
matTooltip="{{ 'Designates whether this user is in the room.' | translate }}"
|
||||||
[value]="user.is_present">
|
[value]="user.is_present"
|
||||||
|
>
|
||||||
<span translate>Is present</span>
|
<span translate>Is present</span>
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
|
||||||
@ -199,7 +219,8 @@
|
|||||||
matTooltip="{{
|
matTooltip="{{
|
||||||
'Designates whether this user should be treated as active. Unselect this instead of deleting the account.'
|
'Designates whether this user should be treated as active. Unselect this instead of deleting the account.'
|
||||||
| translate
|
| translate
|
||||||
}}">
|
}}"
|
||||||
|
>
|
||||||
<span translate>Is active</span>
|
<span translate>Is active</span>
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
|
||||||
@ -207,7 +228,8 @@
|
|||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
formControlName="is_committee"
|
formControlName="is_committee"
|
||||||
[value]="user.is_committee"
|
[value]="user.is_committee"
|
||||||
matTooltip="{{ 'Designates whether this user should be treated as a committee.' | translate }}">
|
matTooltip="{{ 'Designates whether this user should be treated as a committee.' | translate }}"
|
||||||
|
>
|
||||||
<span translate>Is a committee</span>
|
<span translate>Is a committee</span>
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { genders } from 'app/shared/models/users/user';
|
||||||
import { ViewUser } from '../../models/view-user';
|
import { ViewUser } from '../../models/view-user';
|
||||||
import { UserRepositoryService } from '../../services/user-repository.service';
|
import { UserRepositoryService } from '../../services/user-repository.service';
|
||||||
import { Group } from '../../../../shared/models/users/group';
|
import { Group } from '../../../../shared/models/users/group';
|
||||||
import { DataStoreService } from '../../../../core/services/data-store.service';
|
import { DataStoreService } from '../../../../core/services/data-store.service';
|
||||||
import { OperatorService } from '../../../../core/services/operator.service';
|
import { OperatorService } from '../../../../core/services/operator.service';
|
||||||
import { BaseViewComponent } from '../../../base/base-view';
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { MatSnackBar } from '@angular/material';
|
|
||||||
import { Title } from '@angular/platform-browser';
|
|
||||||
import { PromptService } from '../../../../core/services/prompt.service';
|
import { PromptService } from '../../../../core/services/prompt.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,6 +64,11 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public groups: Group[];
|
public groups: Group[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hold the list of genders (sexes) publicly to dynamically iterate in the view
|
||||||
|
*/
|
||||||
|
public genderList = genders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for user
|
* Constructor for user
|
||||||
*
|
*
|
||||||
@ -114,6 +121,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks, if the given user id matches with the operator ones.
|
* Checks, if the given user id matches with the operator ones.
|
||||||
|
*
|
||||||
* @param userId The id to check, if it's the operator
|
* @param userId The id to check, if it's the operator
|
||||||
* @returns If the user is the operator
|
* @returns If the user is the operator
|
||||||
*/
|
*/
|
||||||
@ -178,6 +186,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
title: [''],
|
title: [''],
|
||||||
first_name: [''],
|
first_name: [''],
|
||||||
last_name: [''],
|
last_name: [''],
|
||||||
|
gender: [''],
|
||||||
structure_level: [''],
|
structure_level: [''],
|
||||||
number: [''],
|
number: [''],
|
||||||
about_me: [''],
|
about_me: [''],
|
||||||
@ -226,6 +235,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
this.personalInfoForm.get('first_name'),
|
this.personalInfoForm.get('first_name'),
|
||||||
this.personalInfoForm.get('last_name'),
|
this.personalInfoForm.get('last_name'),
|
||||||
this.personalInfoForm.get('email'),
|
this.personalInfoForm.get('email'),
|
||||||
|
this.personalInfoForm.get('gender'),
|
||||||
this.personalInfoForm.get('structure_level'),
|
this.personalInfoForm.get('structure_level'),
|
||||||
this.personalInfoForm.get('number'),
|
this.personalInfoForm.get('number'),
|
||||||
this.personalInfoForm.get('groups_id'),
|
this.personalInfoForm.get('groups_id'),
|
||||||
@ -241,6 +251,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
allowedFormFields.push(
|
allowedFormFields.push(
|
||||||
this.personalInfoForm.get('username'),
|
this.personalInfoForm.get('username'),
|
||||||
this.personalInfoForm.get('email'),
|
this.personalInfoForm.get('email'),
|
||||||
|
this.personalInfoForm.get('gender'),
|
||||||
this.personalInfoForm.get('about_me')
|
this.personalInfoForm.get('about_me')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,10 @@ export class ViewUser extends BaseProjectableModel {
|
|||||||
return this.user ? this.user.email : null;
|
return this.user ? this.user.email : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get gender(): string {
|
||||||
|
return this.user ? this.user.gender : null;
|
||||||
|
}
|
||||||
|
|
||||||
public get structure_level(): string {
|
public get structure_level(): string {
|
||||||
return this.user ? this.user.structure_level : null;
|
return this.user ? this.user.structure_level : null;
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,12 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
|||||||
updateUser.username = viewUser.username;
|
updateUser.username = viewUser.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the update user does not have a gender-field, send gender as empty string.
|
||||||
|
// This allow to delete a previously selected gender
|
||||||
|
if (!updateUser.gender) {
|
||||||
|
updateUser.gender = '';
|
||||||
|
}
|
||||||
|
|
||||||
return await this.dataSend.updateModel(updateUser);
|
return await this.dataSend.updateModel(updateUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user