Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizable gender field #4027

Merged
merged 29 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
51a289e
Update gender related domain/models files
reiterl Aug 7, 2024
5412675
Add ViewGender and GenderRepositoryService
reiterl Aug 7, 2024
524cd65
Add gender-list dummy component
reiterl Aug 7, 2024
c403ed3
Add gender route in accounts-routing
reiterl Aug 7, 2024
59117ef
Add the genders button to the account-list
reiterl Aug 8, 2024
10337fd
Add gender-controller
reiterl Aug 8, 2024
fcb1fe7
Add list and dialog part to gender-list
reiterl Aug 9, 2024
9365451
Draft version, add TODO-G for open points
reiterl Sep 6, 2024
8f214b0
Merge branch 'main' into 3803-gender
reiterl Sep 6, 2024
7ab969d
add organization/gender relation and app config
reiterl Sep 6, 2024
1e9fb92
Update gender-list deleteGenders
reiterl Sep 6, 2024
0cbd30c
Link user and gender, allow selection in user-detail
reiterl Sep 9, 2024
707ac79
Add subscription to gender list
reiterl Sep 9, 2024
da72300
Add gender delete prompt
reiterl Sep 10, 2024
38fce96
Allow sort accounts by gender
reiterl Sep 10, 2024
7215080
Add gender filter options to account list
reiterl Sep 10, 2024
27e0d37
Add gender filter/sort to participant list
reiterl Sep 10, 2024
7336de4
Update account and participant import (gender)
reiterl Sep 11, 2024
2c279a0
Merge branch 'main' into 3803-gender
reiterl Sep 11, 2024
ecac687
Use repo-search-selector and update subscriptions
reiterl Sep 13, 2024
db744a8
Move 0 -> null change to save functions
reiterl Sep 16, 2024
f6be8e2
X out gender list test
reiterl Sep 16, 2024
9bc5060
Merge branch 'main' into 3803-gender
rrenkert Sep 18, 2024
b9361bb
Merge branch 'main' into 3803-gender
Elblinator Sep 19, 2024
f41da09
Add missing subscription, disallow edit/del for some gender.id
reiterl Sep 20, 2024
136061b
Disable delete menu depending on filtered genders
reiterl Sep 25, 2024
baca70e
Allow delete only if non-default genders are selected
reiterl Sep 25, 2024
8554c74
Rename title to Genders and simplify code
reiterl Sep 26, 2024
996024d
Merge branch 'main' into 3803-gender
rrenkert Sep 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/app/domain/fieldsets/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BaseSimplifiedModelRequest } from 'src/app/site/services/model-request-

export class UserFieldsets {
public static readonly FullNameSubscription: BaseSimplifiedModelRequest = {
fieldset: [`title`, `first_name`, `last_name`, `pronoun`, `username`, `gender`, `default_vote_weight`]
fieldset: [`title`, `first_name`, `last_name`, `pronoun`, `username`, `default_vote_weight`, `gender_id`]
};
}

Expand Down
21 changes: 21 additions & 0 deletions client/src/app/domain/models/gender/gender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Id } from '../../definitions/key-types';
import { BaseModel } from '../base/base-model';
/**
* Representation of a gender.
* @ignore
*/

export class Gender extends BaseModel<Gender> {
public static COLLECTION = `gender`;

public readonly name!: string;
public organization_id!: Id; // (organization/gender_ids)[]
public user_ids!: Id[]; // user/gender_id

public constructor(input?: Partial<Gender>) {
super(Gender.COLLECTION, input);
}

public static readonly REQUESTABLE_FIELDS: (keyof Gender)[] = [`id`, `name`, `organization_id`, `user_ids`];
}
export interface Gender {}
4 changes: 2 additions & 2 deletions client/src/app/domain/models/organizations/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export class OrganizationSetting {
public saml_metadata_idp!: string;
public saml_metadata_sp!: string;
public saml_private_key!: string;
public genders!: string[];
}

export class Organization extends BaseModel<Organization> {
Expand All @@ -52,6 +51,7 @@ export class Organization extends BaseModel<Organization> {
public archived_meeting_ids!: Id[]; // (meeting/is_archived_in_organization_id)[];
public template_meeting_ids!: Id[]; // (meeting/template_for_organization_id)[];
public mediafile_ids!: Id[];
public gender_ids!: Id[]; // (gender/organization_id);
public published_mediafile_ids!: Id[];

public constructor(input?: any) {
Expand All @@ -66,7 +66,7 @@ export class Organization extends BaseModel<Organization> {
`privacy_policy`,
`login_text`,
`reset_password_verbose_errors`,
`genders`,
`gender_ids`,
`enable_electronic_voting`,
`enable_chat`,
`limit_of_meetings`,
Expand Down
2 changes: 1 addition & 1 deletion client/src/app/domain/models/users/user.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { marker as _ } from '@colsen1991/ngx-translate-extract-marker';

import { User } from './user';

export const userHeadersAndVerboseNames: { [key in keyof User]?: any } = {
export const userHeadersAndVerboseNames: { [key in keyof User | 'gender']?: any } = {
title: _(`Title`),
first_name: _(`Given name`),
last_name: _(`Surname`),
Expand Down
4 changes: 2 additions & 2 deletions client/src/app/domain/models/users/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export class User extends BaseDecimalModel<User> {
public readonly is_physical_person!: boolean;
public readonly default_password!: string;
public readonly can_change_own_password!: boolean;
public readonly gender!: string;
public readonly email!: string;
public readonly last_email_sent!: number; // comes in seconds
public readonly last_login!: number; // comes in seconds
Expand All @@ -52,6 +51,7 @@ export class User extends BaseDecimalModel<User> {

public organization_management_level!: keyof OMLMapping;
public committee_management_ids!: Id[];
public gender_id: Id; // (gender/user_ids)[]

public constructor(input?: Partial<User>) {
super(User.COLLECTION, input);
Expand All @@ -74,12 +74,12 @@ export class User extends BaseDecimalModel<User> {
`is_physical_person`,
`default_password`,
`can_change_own_password`,
`gender`,
`email`,
`default_vote_weight`,
`last_email_sent`,
`is_demo_user`,
`last_login`,
`gender_id`,
`organization_management_level`,
`is_present_in_meeting_ids`,
`committee_ids`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';

import { GenderRepositoryService } from './gender-repository.service';

xdescribe(`GenderRepositoryService`, () => {
let service: GenderRepositoryService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(GenderRepositoryService);
});

it(`should be created`, () => {
expect(service).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Injectable } from '@angular/core';
import { Id } from 'src/app/domain/definitions/key-types';
import { Identifiable } from 'src/app/domain/interfaces';
import { Gender } from 'src/app/domain/models/gender/gender';
import { BaseRepository } from 'src/app/gateways/repositories/base-repository';
import { ViewGender } from 'src/app/site/pages/organization/pages/accounts/pages/gender/view-models/view-gender';
import { Fieldsets } from 'src/app/site/services/model-request-builder';

import { Action } from '../../actions';
import { RepositoryServiceCollectorService } from '../repository-service-collector.service';
import { GenderAction } from './gender.action';

@Injectable({
providedIn: `root`
})
export class GenderRepositoryService extends BaseRepository<ViewGender, Gender> {
public constructor(repositoryServiceCollector: RepositoryServiceCollectorService) {
super(repositoryServiceCollector, Gender);
}

public getVerboseName = (plural?: boolean): string => (plural ? `Genders` : `Gender`);
public getTitle = (viewModel: ViewGender): string => viewModel.name;
public override getFieldsets(): Fieldsets<any> {
const baseFields: (keyof Gender)[] = [];
const requiredFields: (keyof Gender)[] = baseFields.concat([`name`]);
return {
...super.getFieldsets(),
required: requiredFields
};
}

public create(...genders: any[]): Action<Identifiable[]> {
const payload = genders;
return this.createAction(GenderAction.CREATE, payload);
}

public update(update: any, id: Id): Action<void> {
const payload = {
id,
...update
};
return this.createAction(GenderAction.UPDATE, payload);
}

public delete(...ids: Id[]): Action<void> {
const payload = ids.map(id => ({ id }));
return this.createAction(GenderAction.DELETE, payload);
}
}
5 changes: 5 additions & 0 deletions client/src/app/gateways/repositories/gender/gender.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class GenderAction {
public static readonly CREATE = `gender.create`;
public static readonly UPDATE = `gender.update`;
public static readonly DELETE = `gender.delete`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ export class OrganizationRepositoryService extends BaseRepository<ViewOrganizati
`default_language`,
`saml_metadata_idp`,
`saml_metadata_sp`,
`saml_private_key`,
`genders`
`saml_private_key`
);
return {
...super.getFieldsets(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
`last_name`,
`pronoun`,
`username` /* Required! To getShortName */,
`gender`,
`gender_id`,
`default_vote_weight`,
`is_physical_person`,
`is_active`,
Expand Down Expand Up @@ -224,7 +224,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
email: update.email,
username: update.username,
pronoun: update.pronoun,
gender: update.gender
gender_id: update.gender_id
};
return this.sendActionToBackend(UserAction.UPDATE_SELF, payload);
}
Expand Down Expand Up @@ -257,7 +257,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
is_active: partialUser.is_active,
is_physical_person: partialUser.is_physical_person,
default_password: partialUser.default_password,
gender: partialUser.gender,
gender_id: partialUser.gender_id,
email: partialUser.email,
default_vote_weight: toDecimal(partialUser.default_vote_weight, false) as any,
organization_management_level: partialUser.organization_management_level,
Expand Down
14 changes: 14 additions & 0 deletions client/src/app/infrastructure/definitions/relations/relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { ViewPollCandidate } from 'src/app/site/pages/meetings/pages/polls/view-models/view-poll-candidate';
import { ViewPollCandidateList } from 'src/app/site/pages/meetings/pages/polls/view-models/view-poll-candidate-list';
import { ViewMeetingUser } from 'src/app/site/pages/meetings/view-models/view-meeting-user';
import { ViewGender } from 'src/app/site/pages/organization/pages/accounts/pages/gender/view-models/view-gender';
import { ViewResource } from 'src/app/site/pages/organization/pages/resources';

import { BaseViewModel, ViewModelConstructor } from '../../../site/base/base-view-model';
Expand Down Expand Up @@ -166,6 +167,13 @@ export const RELATIONS: Relation[] = [
AField: `theme`,
BField: `theme_for_organization`
}),
...makeM2O({
OViewModel: ViewOrganization,
MViewModel: ViewGender,
OField: `genders`,
MField: `organization`,
isFullList: true
}),
// ########## Organization tags
...makeGenericM2M<ViewOrganizationTag, HasOrganizationTags>({
viewModel: ViewOrganizationTag,
Expand Down Expand Up @@ -273,6 +281,12 @@ export const RELATIONS: Relation[] = [
AField: `meeting_users`,
BField: `structure_levels`
}),
...makeM2O({
MViewModel: ViewUser,
OViewModel: ViewGender,
MField: `gender`,
OField: `users`
}),
// Vote delegations
// vote_delegated_to_id -> vote_delegations_from_ids
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { MotionsAppConfig } from 'src/app/site/pages/meetings/pages/motions';
import { PollsAppConfig } from 'src/app/site/pages/meetings/pages/polls/polls.config';
import { ProjectorAppConfig } from 'src/app/site/pages/meetings/pages/projectors/projector.config';
import { MainMenuService } from 'src/app/site/pages/meetings/services/main-menu.service';
import { GendersAppConfig } from 'src/app/site/pages/organization/pages/accounts/pages/gender/genders.config';
import { CollectionMapperService } from 'src/app/site/services/collection-mapper.service';
import { FallbackRoutesService } from 'src/app/site/services/fallback-routes.service';
import { ModelRequestBuilderService } from 'src/app/site/services/model-request-builder';
Expand Down Expand Up @@ -55,7 +56,8 @@ const appConfigs: AppConfig[] = [
MeetingSettingsAppConfig,
ChatAppConfig,
ActionWorkerAppConfig,
MeetingUserAppConfig
MeetingUserAppConfig,
GendersAppConfig
];

@Injectable({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,14 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit {

public async saveUserChanges(): Promise<void> {
if (this.self) {
const payload = this.userPersonalForm;
if (payload.gender_id === 0) {
payload.gender_id = null;
}
if (this.operator.hasPerms(Permission.userCanUpdate) && this._isUserInScope) {
await this.repo.update(this.userPersonalForm, this.self).resolve();
await this.repo.update(payload, this.self).resolve();
} else {
await this.repo.updateSelf(this.userPersonalForm, this.self);
await this.repo.updateSelf(payload, this.self);
}
}
this.isUserFormValid = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,12 @@ <h2>{{ 'Personal information' | translate }}</h2>
<!-- Gender -->
<mat-form-field class="form58 force-min-width">
<mat-label>{{ 'Gender' | translate }}</mat-label>
<mat-select formControlName="gender">
<mat-option [value]="null">-</mat-option>
@for (gender of genders; track gender) {
<mat-option [value]="gender">
{{ gender | translate }}
</mat-option>
}
</mat-select>
<os-repo-search-selector
formControlName="gender_id"
[includeNone]="true"
[repo]="genderRepo"
[subscriptionConfig]="genderListSubscriptionConfig"
></os-repo-search-selector>
</mat-form-field>
<!-- Pronoun -->
<mat-form-field class="form38 force-min-width">
Expand Down Expand Up @@ -230,7 +228,7 @@ <h4>{{ 'Name' | translate }}</h4>
@if (user.gender) {
<div>
<h4>{{ 'Gender' | translate }}</h4>
<span>{{ user.gender | translate }}</span>
<span>{{ user.gender?.name | translate }}</span>
</div>
}
<!-- Mail -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ import {
} from '@angular/forms';
import { Subscription } from 'rxjs';
import { createEmailValidator } from 'src/app/infrastructure/utils/validators/email';
import { getGenderListSubscriptionConfig } from 'src/app/site/pages/organization/pages/accounts/pages/gender/gender.subscription';
import { GenderControllerService } from 'src/app/site/pages/organization/pages/accounts/pages/gender/services/gender-controller.service';
import { OperatorService } from 'src/app/site/services/operator.service';
import { BaseUiComponent } from 'src/app/ui/base/base-ui-component';

import { GENDERS } from '../../../../../domain/models/users/user';
import { ViewUser } from '../../../../../site/pages/meetings/view-models/view-user';
import { OneOfValidator } from '../../validators';

Expand Down Expand Up @@ -123,12 +124,12 @@ export class UserDetailViewComponent extends BaseUiComponent implements OnInit,

public personalInfoForm!: UntypedFormGroup;

public genders = GENDERS;

public get isSelf(): boolean {
return this.operator.operatorId === this._user?.id;
}

public genderListSubscriptionConfig = getGenderListSubscriptionConfig();

private set _initialState(state: any | null) {
this._initialStateString = JSON.stringify(state);
}
Expand All @@ -153,6 +154,7 @@ export class UserDetailViewComponent extends BaseUiComponent implements OnInit,
public constructor(
private fb: UntypedFormBuilder,
private operator: OperatorService,
public genderRepo: GenderControllerService,
private cd: ChangeDetectorRef
) {
super();
Expand Down Expand Up @@ -306,7 +308,7 @@ export class UserDetailViewComponent extends BaseUiComponent implements OnInit,
title: [``],
first_name: [``],
last_name: [``],
gender: [``],
gender_id: [``],
email: [``, [createEmailValidator()]],
last_email_sent: [``],
default_password: [``],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { MatMenuModule } from '@angular/material/menu';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { IconContainerModule } from 'src/app/ui/modules/icon-container';
import { SearchSelectorModule } from 'src/app/ui/modules/search-selector';

import { OpenSlidesTranslationModule } from '../../../site/modules/translations/openslides-translation.module';
import { PasswordFormComponent } from './components/password-form/password-form.component';
Expand Down Expand Up @@ -51,6 +52,7 @@ const MODULES = [MatInputModule, MatMenuModule];
MatFormFieldModule,
MatCardModule,
MatSelectModule,
SearchSelectorModule,
...MODULES
]
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export class ViewSpeaker extends BaseHasMeetingUserViewModel<Speaker> {
}

public get gender(): string {
return this.user ? this.user.gender : ``;
return this.user ? this.user.gender?.name : ``;
}

public get contentType(): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ export class ParticipantDetailViewComponent extends BaseMeetingComponent {
if (payload.member_number === ``) {
payload.member_number = null;
}
if (payload.gender_id === 0) {
payload.gender_id = null;
}
const title = _(`This action will remove you from one or more groups.`);
const content = _(
`This may diminish your ability to do things in this meeting and you may not be able to revert it by youself. Are you sure you want to do this?`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ export class ParticipantCreateWizardComponent extends BaseMeetingComponent imple
.filter((id: Id | undefined) => !!id)
: []
};
if (payload.gender_id === 0) {
payload.gender_id = null;
}
if (this._accountId) {
this.repo
.update(payload, {
Expand Down
Loading
Loading