diff --git a/client/src/app/domain/fieldsets/user.ts b/client/src/app/domain/fieldsets/user.ts index 5c630d00b7..165b4c4c32 100644 --- a/client/src/app/domain/fieldsets/user.ts +++ b/client/src/app/domain/fieldsets/user.ts @@ -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`] }; } diff --git a/client/src/app/domain/models/gender/gender.ts b/client/src/app/domain/models/gender/gender.ts new file mode 100644 index 0000000000..3046600b1f --- /dev/null +++ b/client/src/app/domain/models/gender/gender.ts @@ -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 { + 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) { + super(Gender.COLLECTION, input); + } + + public static readonly REQUESTABLE_FIELDS: (keyof Gender)[] = [`id`, `name`, `organization_id`, `user_ids`]; +} +export interface Gender {} diff --git a/client/src/app/domain/models/organizations/organization.ts b/client/src/app/domain/models/organizations/organization.ts index cfb2d96df3..94ad79f30c 100644 --- a/client/src/app/domain/models/organizations/organization.ts +++ b/client/src/app/domain/models/organizations/organization.ts @@ -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 { @@ -52,6 +51,7 @@ export class Organization extends BaseModel { 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) { @@ -66,7 +66,7 @@ export class Organization extends BaseModel { `privacy_policy`, `login_text`, `reset_password_verbose_errors`, - `genders`, + `gender_ids`, `enable_electronic_voting`, `enable_chat`, `limit_of_meetings`, diff --git a/client/src/app/domain/models/users/user.constants.ts b/client/src/app/domain/models/users/user.constants.ts index bb3cdb7387..aac477bdea 100644 --- a/client/src/app/domain/models/users/user.constants.ts +++ b/client/src/app/domain/models/users/user.constants.ts @@ -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`), diff --git a/client/src/app/domain/models/users/user.ts b/client/src/app/domain/models/users/user.ts index daa58839be..4389fdd324 100644 --- a/client/src/app/domain/models/users/user.ts +++ b/client/src/app/domain/models/users/user.ts @@ -29,7 +29,6 @@ export class User extends BaseDecimalModel { 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 @@ -52,6 +51,7 @@ export class User extends BaseDecimalModel { public organization_management_level!: keyof OMLMapping; public committee_management_ids!: Id[]; + public gender_id: Id; // (gender/user_ids)[] public constructor(input?: Partial) { super(User.COLLECTION, input); @@ -74,12 +74,12 @@ export class User extends BaseDecimalModel { `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`, diff --git a/client/src/app/gateways/repositories/gender/gender-repository.service.spec.ts b/client/src/app/gateways/repositories/gender/gender-repository.service.spec.ts new file mode 100644 index 0000000000..14d24c0351 --- /dev/null +++ b/client/src/app/gateways/repositories/gender/gender-repository.service.spec.ts @@ -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(); + }); +}); diff --git a/client/src/app/gateways/repositories/gender/gender-repository.service.ts b/client/src/app/gateways/repositories/gender/gender-repository.service.ts new file mode 100644 index 0000000000..f6c4619ea5 --- /dev/null +++ b/client/src/app/gateways/repositories/gender/gender-repository.service.ts @@ -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 { + 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 { + const baseFields: (keyof Gender)[] = []; + const requiredFields: (keyof Gender)[] = baseFields.concat([`name`]); + return { + ...super.getFieldsets(), + required: requiredFields + }; + } + + public create(...genders: any[]): Action { + const payload = genders; + return this.createAction(GenderAction.CREATE, payload); + } + + public update(update: any, id: Id): Action { + const payload = { + id, + ...update + }; + return this.createAction(GenderAction.UPDATE, payload); + } + + public delete(...ids: Id[]): Action { + const payload = ids.map(id => ({ id })); + return this.createAction(GenderAction.DELETE, payload); + } +} diff --git a/client/src/app/gateways/repositories/gender/gender.action.ts b/client/src/app/gateways/repositories/gender/gender.action.ts new file mode 100644 index 0000000000..9707ea7e70 --- /dev/null +++ b/client/src/app/gateways/repositories/gender/gender.action.ts @@ -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`; +} diff --git a/client/src/app/gateways/repositories/organization-repository.service.ts b/client/src/app/gateways/repositories/organization-repository.service.ts index 4ff8d2d92d..894cb0cece 100644 --- a/client/src/app/gateways/repositories/organization-repository.service.ts +++ b/client/src/app/gateways/repositories/organization-repository.service.ts @@ -44,8 +44,7 @@ export class OrganizationRepositoryService extends BaseRepository { `last_name`, `pronoun`, `username` /* Required! To getShortName */, - `gender`, + `gender_id`, `default_vote_weight`, `is_physical_person`, `is_active`, @@ -226,7 +226,7 @@ export class UserRepositoryService extends BaseRepository { email: update.email, username: update.username, pronoun: update.pronoun, - gender: update.gender + gender_id: update.gender_id }; return this.sendActionToBackend(UserAction.UPDATE_SELF, payload); } @@ -259,7 +259,7 @@ export class UserRepositoryService extends BaseRepository { 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, diff --git a/client/src/app/infrastructure/definitions/relations/relations.ts b/client/src/app/infrastructure/definitions/relations/relations.ts index 7f483e034c..8621a66717 100644 --- a/client/src/app/infrastructure/definitions/relations/relations.ts +++ b/client/src/app/infrastructure/definitions/relations/relations.ts @@ -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'; @@ -165,6 +166,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({ viewModel: ViewOrganizationTag, @@ -272,6 +280,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 { diff --git a/client/src/app/openslides-main-module/services/app-load.service.ts b/client/src/app/openslides-main-module/services/app-load.service.ts index 36af52d6b5..a428945e78 100644 --- a/client/src/app/openslides-main-module/services/app-load.service.ts +++ b/client/src/app/openslides-main-module/services/app-load.service.ts @@ -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'; @@ -55,7 +56,8 @@ const appConfigs: AppConfig[] = [ MeetingSettingsAppConfig, ChatAppConfig, ActionWorkerAppConfig, - MeetingUserAppConfig + MeetingUserAppConfig, + GendersAppConfig ]; @Injectable({ diff --git a/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.ts b/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.ts index 8df09e61e3..ab16c596d5 100644 --- a/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.ts +++ b/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.ts @@ -193,10 +193,14 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit { public async saveUserChanges(): Promise { 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; diff --git a/client/src/app/site/modules/user-components/components/user-detail-view/user-detail-view.component.html b/client/src/app/site/modules/user-components/components/user-detail-view/user-detail-view.component.html index 424f6587cc..89bf0ecb00 100644 --- a/client/src/app/site/modules/user-components/components/user-detail-view/user-detail-view.component.html +++ b/client/src/app/site/modules/user-components/components/user-detail-view/user-detail-view.component.html @@ -82,14 +82,12 @@

{{ 'Personal information' | translate }}

{{ 'Gender' | translate }} - - - - @for (gender of genders; track gender) { - - {{ gender | translate }} - - } - + @@ -230,7 +228,7 @@

{{ 'Name' | translate }}

@if (user.gender) {

{{ 'Gender' | translate }}

- {{ user.gender | translate }} + {{ user.gender?.name | translate }}
} diff --git a/client/src/app/site/modules/user-components/components/user-detail-view/user-detail-view.component.ts b/client/src/app/site/modules/user-components/components/user-detail-view/user-detail-view.component.ts index 5062731ff4..d91d12e30a 100644 --- a/client/src/app/site/modules/user-components/components/user-detail-view/user-detail-view.component.ts +++ b/client/src/app/site/modules/user-components/components/user-detail-view/user-detail-view.component.ts @@ -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'; @@ -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); } @@ -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(); @@ -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: [``], diff --git a/client/src/app/site/modules/user-components/user-components.module.ts b/client/src/app/site/modules/user-components/user-components.module.ts index 446fb57b1f..9edd9460b6 100644 --- a/client/src/app/site/modules/user-components/user-components.module.ts +++ b/client/src/app/site/modules/user-components/user-components.module.ts @@ -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'; @@ -51,6 +52,7 @@ const MODULES = [MatInputModule, MatMenuModule]; MatFormFieldModule, MatCardModule, MatSelectModule, + SearchSelectorModule, ...MODULES ] }) diff --git a/client/src/app/site/pages/meetings/pages/agenda/modules/list-of-speakers/view-models/view-speaker.ts b/client/src/app/site/pages/meetings/pages/agenda/modules/list-of-speakers/view-models/view-speaker.ts index 2eaf8f8403..a548875f9d 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/modules/list-of-speakers/view-models/view-speaker.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/modules/list-of-speakers/view-models/view-speaker.ts @@ -117,7 +117,7 @@ export class ViewSpeaker extends BaseHasMeetingUserViewModel { } public get gender(): string { - return this.user ? this.user.gender : ``; + return this.user ? this.user.gender?.name : ``; } public get contentType(): string { diff --git a/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail-view/participant-detail-view.component.ts b/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail-view/participant-detail-view.component.ts index d03d4fedec..15b2c6a817 100644 --- a/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail-view/participant-detail-view.component.ts +++ b/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail-view/participant-detail-view.component.ts @@ -409,6 +409,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?` diff --git a/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/pages/participant-detail-manage/components/participant-create-wizard/participant-create-wizard.component.ts b/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/pages/participant-detail-manage/components/participant-create-wizard/participant-create-wizard.component.ts index 978cbe3add..6b363534c2 100644 --- a/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/pages/participant-detail-manage/components/participant-create-wizard/participant-create-wizard.component.ts +++ b/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/pages/participant-detail-manage/components/participant-create-wizard/participant-create-wizard.component.ts @@ -276,6 +276,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, { diff --git a/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/services/participant-list-filter/participant-list-filter.service.ts b/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/services/participant-list-filter/participant-list-filter.service.ts index 5d999850ed..898bfe0b16 100644 --- a/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/services/participant-list-filter/participant-list-filter.service.ts +++ b/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/services/participant-list-filter/participant-list-filter.service.ts @@ -1,13 +1,13 @@ import { Injectable } from '@angular/core'; import { marker as _ } from '@colsen1991/ngx-translate-extract-marker'; import { Permission } from 'src/app/domain/definitions/permission'; -import { GENDER_FITLERABLE, GENDERS } from 'src/app/domain/models/users/user'; import { OsFilter, OsHideFilterSetting } from 'src/app/site/base/base-filter.service'; import { BaseMeetingFilterListService } from 'src/app/site/pages/meetings/base/base-meeting-filter-list.service'; import { MeetingActiveFiltersService } from 'src/app/site/pages/meetings/services/meeting-active-filters.service'; import { MeetingSettingsService } from 'src/app/site/pages/meetings/services/meeting-settings.service'; import { DelegationType } from 'src/app/site/pages/meetings/view-models/delegation-type'; import { ViewUser } from 'src/app/site/pages/meetings/view-models/view-user'; +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 { GroupControllerService } from '../../../../modules/groups/services/group-controller.service'; @@ -35,6 +35,12 @@ export class ParticipantListFilterService extends BaseMeetingFilterListService = { + property: `gender_id`, + label: _(`Gender`), + options: [] + }; + private _voteWeightEnabled: boolean; private _voteDelegationEnabled: boolean; @@ -42,6 +48,7 @@ export class ParticipantListFilterService extends BaseMeetingFilterListService (this._voteWeightEnabled = value)); this.meetingSettings .get(`users_enable_vote_delegations`) @@ -137,17 +149,7 @@ export class ParticipantListFilterService extends BaseMeetingFilterListService { { property: `is_present_in_meeting_ids`, label: _(`Presence`) }, { property: `is_locked_out`, label: _(`Locked Out`) }, { property: `member_number`, label: _(`Membership number`) }, + { property: `gender_name`, label: _(`Gender`) }, { property: `is_active`, label: _(`Is active`) }, { property: `is_physical_person`, label: _(`Is a natural person`) }, { property: `number`, label: _(`Participant number`), foreignBaseKeys: { meeting_user: [`number`] } }, diff --git a/client/src/app/site/pages/meetings/pages/participants/participants.subscription.ts b/client/src/app/site/pages/meetings/pages/participants/participants.subscription.ts index 5187a92018..66709e2d1d 100644 --- a/client/src/app/site/pages/meetings/pages/participants/participants.subscription.ts +++ b/client/src/app/site/pages/meetings/pages/participants/participants.subscription.ts @@ -62,20 +62,28 @@ export const getParticipantIsPresentSubscriptionConfig: SubscriptionConfigGenera subscriptionName: PARTICIPANT_IS_PRESENT_LIST_SUBSCRIPTION }); -export const getParticipantListSubscriptionConfig: SubscriptionConfigGenerator = (id: Id) => ({ - modelRequest: { - viewModelCtor: ViewMeeting, - ids: [id], - follow: [ - { - idField: `meeting_user_ids`, - fieldset: `participantListMinimal`, - follow: [{ idField: `user_id`, fieldset: `participantList` }] - } - ] - }, - subscriptionName: PARTICIPANT_LIST_SUBSCRIPTION -}); +export const getParticipantListSubscriptionConfig: SubscriptionConfigGenerator = (id: Id) => { + return { + modelRequest: { + viewModelCtor: ViewMeeting, + ids: [id], + follow: [ + { + idField: `meeting_user_ids`, + fieldset: `participantListMinimal`, + follow: [ + { + idField: `user_id`, + fieldset: `participantList`, + follow: [{ idField: `gender_id`, fieldset: [`name`] }] + } + ] + } + ] + }, + subscriptionName: PARTICIPANT_LIST_SUBSCRIPTION + }; +}; export const getParticipantMinimalSubscriptionConfig: SubscriptionConfigGenerator = (id: Id) => ({ modelRequest: { @@ -89,7 +97,13 @@ export const getParticipantMinimalSubscriptionConfig: SubscriptionConfigGenerato { idField: `user_id`, fieldset: `participantListMinimal`, - additionalFields: [`is_present_in_meeting_ids`] + additionalFields: [`is_present_in_meeting_ids`], + follow: [ + { + idField: `gender_id`, + fieldset: [`name`] + } + ] }, { idField: `structure_level_ids`, fieldset: [`name`] } ] @@ -108,6 +122,10 @@ export const getParticipantDetailSubscription: SubscriptionConfigGenerator = (id { idField: `meeting_user_ids`, fieldset: DEFAULT_FIELDSET + }, + { + idField: `gender_id`, + fieldset: [`name`] } ] }, diff --git a/client/src/app/site/pages/meetings/view-models/view-user.ts b/client/src/app/site/pages/meetings/view-models/view-user.ts index f798f1fce1..df379aab02 100644 --- a/client/src/app/site/pages/meetings/view-models/view-user.ts +++ b/client/src/app/site/pages/meetings/view-models/view-user.ts @@ -2,6 +2,7 @@ import { User } from 'src/app/domain/models/users/user'; import { BaseViewModel, ViewModelRelations } from 'src/app/site/base/base-view-model'; import { Id } from '../../../../domain/definitions/key-types'; +import { ViewGender } from '../../organization/pages/accounts/pages/gender/view-models/view-gender'; import { ViewCommittee } from '../../organization/pages/committees'; import { ViewOrganization } from '../../organization/view-models/view-organization'; import { ViewGroup } from '../pages/participants/modules/groups/view-models/view-group'; @@ -112,6 +113,10 @@ export class ViewUser extends BaseViewModel /* implements Searchable */ { return !!this.member_number; } + public get gender_name(): string { + return this.gender?.name ?? ``; + } + // Will be set by the repository public getName!: () => string; public getShortName!: () => string; @@ -364,6 +369,7 @@ interface IUserRelations { options: ViewOption[]; votes: ViewVote[]; poll_candidates: ViewPollCandidate[]; + gender?: ViewGender; } export interface ViewUser extends User, ViewModelRelations {} diff --git a/client/src/app/site/pages/organization/pages/accounts/accounts-routing.module.ts b/client/src/app/site/pages/organization/pages/accounts/accounts-routing.module.ts index 461452d1c9..a6d96dcc3a 100644 --- a/client/src/app/site/pages/organization/pages/accounts/accounts-routing.module.ts +++ b/client/src/app/site/pages/organization/pages/accounts/accounts-routing.module.ts @@ -39,6 +39,11 @@ const routes: Routes = [ loadChildren: () => import(`./pages/account-import/account-import.module`).then(m => m.AccountImportModule) }, + { + path: `genders`, + loadChildren: () => + import(`./pages/gender/pages/gender-list/gender-list.module`).then(m => m.GenderListModule) + }, { path: `:id`, loadChildren: () => diff --git a/client/src/app/site/pages/organization/pages/accounts/accounts.subscription.ts b/client/src/app/site/pages/organization/pages/accounts/accounts.subscription.ts index 6f4a0ae2ee..05a6e96736 100644 --- a/client/src/app/site/pages/organization/pages/accounts/accounts.subscription.ts +++ b/client/src/app/site/pages/organization/pages/accounts/accounts.subscription.ts @@ -18,7 +18,8 @@ export const getAccountDetailSubscriptionConfig: SubscriptionConfigGenerator = ( idField: `meeting_ids`, follow: [{ idField: `group_ids`, fieldset: [`name`], isFullList: false }], fieldset: [`name`, `committee_id`] - } + }, + { idField: `gender_id`, fieldset: [`name`] } ] }, subscriptionName: ACCOUNT_DETAIL_SUBSCRIPTION_NAME diff --git a/client/src/app/site/pages/organization/pages/accounts/components/account-main/account-main.component.ts b/client/src/app/site/pages/organization/pages/accounts/components/account-main/account-main.component.ts index b173d852e4..b228ec9c4a 100644 --- a/client/src/app/site/pages/organization/pages/accounts/components/account-main/account-main.component.ts +++ b/client/src/app/site/pages/organization/pages/accounts/components/account-main/account-main.component.ts @@ -25,6 +25,10 @@ const accountListSubsciptionContent = { follow: [{ idField: `committee_id`, fieldset: [`manager_ids`] }] } ] + }, + { + idField: `gender_id`, + fieldset: [`name`] } ] }; @@ -80,6 +84,10 @@ export class AccountMainComponent extends BaseModelRequestHandlerComponent { fieldset: [`name`] } ] + }, + { + idField: `gender_ids`, + fieldset: [`name`] } ] }, diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/account-detail/components/account-detail/account-detail.component.ts b/client/src/app/site/pages/organization/pages/accounts/pages/account-detail/components/account-detail/account-detail.component.ts index e1d9456dee..dc6c9b7df6 100644 --- a/client/src/app/site/pages/organization/pages/accounts/pages/account-detail/components/account-detail/account-detail.component.ts +++ b/client/src/app/site/pages/organization/pages/accounts/pages/account-detail/components/account-detail/account-detail.component.ts @@ -333,6 +333,13 @@ export class AccountDetailComponent extends BaseComponent implements OnInit { payload.member_number = null; } } + if (payload.gender_id === 0) { + if (isCreate) { + delete payload.gender_id; + } else { + payload.gender_id = null; + } + } return payload; } diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/account-import/components/account-import-list/account-import-list.component.ts b/client/src/app/site/pages/organization/pages/accounts/pages/account-import/components/account-import-list/account-import-list.component.ts index 6246b34338..5e90daee76 100644 --- a/client/src/app/site/pages/organization/pages/accounts/pages/account-import/components/account-import-list/account-import-list.component.ts +++ b/client/src/app/site/pages/organization/pages/accounts/pages/account-import/components/account-import-list/account-import-list.component.ts @@ -1,6 +1,5 @@ import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { map, Observable } from 'rxjs'; import { BaseViaBackendImportListComponent } from 'src/app/site/base/base-via-backend-import-list.component'; import { OrganizationSettingsService } from 'src/app/site/pages/organization/services/organization-settings.service'; import { ImportListHeaderDefinition } from 'src/app/ui/modules/import-list'; @@ -19,8 +18,7 @@ export class AccountImportListComponent extends BaseViaBackendImportListComponen public columns: ImportListHeaderDefinition[] = this.possibleFields.map(header => ({ property: header, label: (accountHeadersAndVerboseNames)[header], - isTableColumn: true, - customInfo: header === `gender` ? this.getTranslatedGenderInfoObservable() : undefined + isTableColumn: true })); public constructor( @@ -30,10 +28,4 @@ export class AccountImportListComponent extends BaseViaBackendImportListComponen ) { super(importer); } - - private getTranslatedGenderInfoObservable(): Observable { - return this.translate - .get(`Possible options`) - .pipe(map(framework => framework + `: ` + this.orgaSettings.instant(`genders`).join(`, `))); - } } diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/account-import/definitions/index.ts b/client/src/app/site/pages/organization/pages/accounts/pages/account-import/definitions/index.ts index 5a12b341ce..a4b9dae215 100644 --- a/client/src/app/site/pages/organization/pages/accounts/pages/account-import/definitions/index.ts +++ b/client/src/app/site/pages/organization/pages/accounts/pages/account-import/definitions/index.ts @@ -2,12 +2,12 @@ import { marker as _ } from '@colsen1991/ngx-translate-extract-marker'; import { User } from 'src/app/domain/models/users/user'; import { userHeadersAndVerboseNames } from 'src/app/domain/models/users/user.constants'; -export const accountHeadersAndVerboseNames: { [key in keyof User]?: any } = { +export const accountHeadersAndVerboseNames: { [key in keyof User | 'gender']?: any } = { ...userHeadersAndVerboseNames, default_vote_weight: _(`Vote weight`) }; -export const accountColumns: (keyof User)[] = [ +export const accountColumns: (keyof User | 'gender')[] = [ `title`, `first_name`, `last_name`, diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/account-list/components/account-list/account-list.component.html b/client/src/app/site/pages/organization/pages/accounts/pages/account-list/components/account-list/account-list.component.html index 5d4cda3ea6..5c16228953 100644 --- a/client/src/app/site/pages/organization/pages/accounts/pages/account-list/components/account-list/account-list.component.html +++ b/client/src/app/site/pages/organization/pages/accounts/pages/account-list/components/account-list/account-list.component.html @@ -175,6 +175,10 @@

cloud_upload {{ 'Import' | translate }} + } @if (isMultiSelect) { diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/account-list/services/account-list-sort.service/account-sort.service.ts b/client/src/app/site/pages/organization/pages/accounts/pages/account-list/services/account-list-sort.service/account-sort.service.ts index 18cd39a820..a4351d3e94 100644 --- a/client/src/app/site/pages/organization/pages/accounts/pages/account-list/services/account-list-sort.service/account-sort.service.ts +++ b/client/src/app/site/pages/organization/pages/accounts/pages/account-list/services/account-list-sort.service/account-sort.service.ts @@ -21,7 +21,7 @@ export class AccountSortService extends BaseSortListService { { property: `member_number`, label: _(`Membership number`) }, { property: `is_active`, label: _(`Is active`) }, { property: `default_vote_weight`, label: _(`Vote weight`) }, - { property: `gender`, label: _(`Gender`) }, + { property: `gender_name`, label: _(`Gender`) }, { property: `id`, label: _(`Sequential number`) }, { property: `numberOfMeetings`, label: _(`Amount of meetings`), baseKeys: [`meeting_ids`] }, { property: `last_email_sent`, label: _(`Last email sent`) }, diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/gender.subscription.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/gender.subscription.ts new file mode 100644 index 0000000000..46095f87e9 --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/gender.subscription.ts @@ -0,0 +1,20 @@ +import { SubscriptionConfigGenerator } from 'src/app/domain/interfaces/subscription-config'; + +import { ORGANIZATION_ID } from '../../../../services/organization.service'; +import { ViewOrganization } from '../../../../view-models/view-organization'; + +export const GENDER_LIST_SUBSCRIPTION = `gender_list`; + +export const getGenderListSubscriptionConfig: SubscriptionConfigGenerator = () => ({ + modelRequest: { + viewModelCtor: ViewOrganization, + ids: [ORGANIZATION_ID], + follow: [ + { + idField: `gender_ids`, + fieldset: [`name`] + } + ] + }, + subscriptionName: GENDER_LIST_SUBSCRIPTION +}); diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/genders.config.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/genders.config.ts new file mode 100644 index 0000000000..d6e066b207 --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/genders.config.ts @@ -0,0 +1,16 @@ +import { Gender } from 'src/app/domain/models/gender/gender'; +import { GenderRepositoryService } from 'src/app/gateways/repositories/gender/gender-repository.service'; +import { AppConfig } from 'src/app/infrastructure/definitions/app-config'; + +import { ViewGender } from './view-models/view-gender'; + +export const GendersAppConfig: AppConfig = { + name: `gender`, + models: [ + { + model: Gender, + viewModel: ViewGender, + repository: GenderRepositoryService + } + ] +}; diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.html b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.html new file mode 100644 index 0000000000..1ac27a692a --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.html @@ -0,0 +1,113 @@ + + +
+

{{ 'Genders' | translate }}

+
+ + + + + + + + + + {{ selectedRows.length }} {{ 'selected' | translate }} + +
+ + +
+
+ {{ gender.name }} +
+
+
+ @if (gender.id > 4 && !isMultiSelect) { + + } +
+
+ @if (gender.id > 4 && !isMultiSelect) { + + } +
+
+ + + @if (!isMultiSelect) { +
+ +
+ } @else { +
+ + + + +
+ } +
+ + + +

+ {{ 'Gender' | translate }} +

+ +
+ + + {{ 'Name' | translate }} + + + {{ 'A name is required' | translate }} + + +
+
+ + + + + +
diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.scss b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.spec.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.spec.ts new file mode 100644 index 0000000000..9466069a2a --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GenderListComponent } from './gender-list.component'; + +xdescribe(`GenderListComponent`, () => { + let component: GenderListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [GenderListComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(GenderListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it(`should create`, () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.ts new file mode 100644 index 0000000000..f9eacb4b43 --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/components/gender-list/gender-list.component.ts @@ -0,0 +1,122 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, TemplateRef, ViewChild } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; +import { infoDialogSettings } from 'src/app/infrastructure/utils/dialog-settings'; +import { BaseListViewComponent } from 'src/app/site/base/base-list-view.component'; +import { PromptService } from 'src/app/ui/modules/prompt-dialog'; + +import { GenderControllerService } from '../../../../services/gender-controller.service'; +import { ViewGender } from '../../../../view-models/view-gender'; + +@Component({ + selector: `os-gender-list`, + templateUrl: `./gender-list.component.html`, + styleUrl: `./gender-list.component.scss`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class GenderListComponent extends BaseListViewComponent { + @ViewChild(`genderDialog`, { static: true }) + private genderDialog: TemplateRef | null = null; + + private dialogRef: MatDialogRef | null = null; + + /** + * Holds the create form + */ + public genderForm: UntypedFormGroup; + + /** + * Check in multiselect if a default gender is selected + */ + public get hasDefaultGenderSelected(): boolean { + return this.selectedRows.filter(view => view.id <= 4).length > 0; + } + + private currentGender: ViewGender; + + public constructor( + protected override translate: TranslateService, + public repo: GenderControllerService, + private dialog: MatDialog, + private formBuilder: UntypedFormBuilder, + private cd: ChangeDetectorRef, + private promptService: PromptService + ) { + super(); + this.setTitle(`Genders`); + this.canMultiSelect = true; + this.genderForm = this.formBuilder.group({ + name: [``, Validators.required] + }); + } + + public openGenderDialog(gender?: ViewGender): void { + this.currentGender = gender; + this.genderForm.reset(); + this.genderForm.get(`name`)!.setValue(gender ? gender.name : ``); + this.dialogRef = this.dialog.open(this.genderDialog!, infoDialogSettings); + this.dialogRef.afterClosed().subscribe(res => { + if (res) { + this.save(); + } + }); + } + + public async deleteGenders(...genders: ViewGender[]): Promise { + const title = + genders.length === 1 + ? this.translate.instant(`Are you sure you want to delete this gender?`) + : this.translate.instant(`Are you sure you want to delete all selected genders?`); + const content = genders.length === 1 ? genders[0].name : ``; + if (await this.promptService.open(title, content)) { + const deleteGenderIds = genders.map(g => g.id).filter(id => id > 4); + if (deleteGenderIds) { + return this.repo.delete(...deleteGenderIds).then(() => this.cd.detectChanges()); + } + } + } + + public deleteSelectedGenders(): void { + this.deleteGenders(...this.selectedRows); + } + + /** + * clicking Enter will save automatically + * clicking Escape will cancel the process + * + * @param event has the code + */ + public onKeyDown(event: KeyboardEvent): void { + if (event.key === `Enter`) { + this.save(); + this.dialogRef!.close(); + } + if (event.key === `Escape`) { + this.dialogRef!.close(); + } + } + + private updateGenderHelper(): void { + if (this.currentGender) { + const data = this.genderForm.value; + this.repo.update(data, this.currentGender.id); + } + } + + private createGenderHelper(): void { + this.repo.create(this.genderForm.value); + } + + private save(): void { + if (!this.genderForm.value || !this.genderForm.valid) { + return; + } + if (this.currentGender) { + this.updateGenderHelper(); + } else { + this.createGenderHelper(); + } + this.genderForm.reset(); + } +} diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/gender-list-routing.module.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/gender-list-routing.module.ts new file mode 100644 index 0000000000..213ae3f90d --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/gender-list-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { GenderListComponent } from './components/gender-list/gender-list.component'; + +const routes: Routes = [ + { + path: ``, + component: GenderListComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class GenderListRoutingModule {} diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/gender-list.module.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/gender-list.module.ts new file mode 100644 index 0000000000..e2497b8d1f --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/pages/gender-list/gender-list.module.ts @@ -0,0 +1,43 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { OpenSlidesTranslationModule } from 'src/app/site/modules/translations'; +import { ChipModule } from 'src/app/ui/modules/chip'; +import { HeadBarModule } from 'src/app/ui/modules/head-bar/head-bar.module'; +import { ListModule } from 'src/app/ui/modules/list'; +import { PromptDialogModule } from 'src/app/ui/modules/prompt-dialog'; + +import { GenderListComponent } from './components/gender-list/gender-list.component'; +import { GenderListRoutingModule } from './gender-list-routing.module'; + +@NgModule({ + declarations: [GenderListComponent], + imports: [ + CommonModule, + GenderListRoutingModule, + PromptDialogModule, + HeadBarModule, + ListModule, + ChipModule, + OpenSlidesTranslationModule.forChild(), + MatIconModule, + MatMenuModule, + MatButtonModule, + MatDividerModule, + MatDialogModule, + MatInputModule, + FormsModule, + ReactiveFormsModule, + MatFormFieldModule, + MatTooltipModule + ] +}) +export class GenderListModule {} diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/services/gender-controller.service.spec.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/services/gender-controller.service.spec.ts new file mode 100644 index 0000000000..96f3ff757b --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/services/gender-controller.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { GenderControllerService } from './gender-controller.service'; + +xdescribe(`GenderControllerService`, () => { + let service: GenderControllerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(GenderControllerService); + }); + + it(`should be created`, () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/services/gender-controller.service.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/services/gender-controller.service.ts new file mode 100644 index 0000000000..9e3e895f97 --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/services/gender-controller.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { Gender } from 'src/app/domain/models/gender/gender'; +import { GenderRepositoryService } from 'src/app/gateways/repositories/gender/gender-repository.service'; +import { BaseController } from 'src/app/site/base/base-controller'; +import { ControllerServiceCollectorService } from 'src/app/site/services/controller-service-collector.service'; + +import { ViewGender } from '../view-models/view-gender'; + +@Injectable({ + providedIn: `root` +}) +export class GenderControllerService extends BaseController { + public constructor( + controllerServiceCollector: ControllerServiceCollectorService, + protected override repo: GenderRepositoryService + ) { + super(controllerServiceCollector, Gender, repo); + } + + public create(...models: Partial[]): void { + this.repo.create(...models).resolve(); + } + + public update(update: Partial, viewGenderId: Id): void { + this.repo.update(update, viewGenderId).resolve(); + } + + public delete(...ids: Id[]): Promise { + return this.repo.delete(...ids).resolve(); + } +} diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/gender/view-models/view-gender.ts b/client/src/app/site/pages/organization/pages/accounts/pages/gender/view-models/view-gender.ts new file mode 100644 index 0000000000..620aa52b40 --- /dev/null +++ b/client/src/app/site/pages/organization/pages/accounts/pages/gender/view-models/view-gender.ts @@ -0,0 +1,19 @@ +import { Gender } from 'src/app/domain/models/gender/gender'; +import { BaseViewModel } from 'src/app/site/base/base-view-model'; +import { ViewUser } from 'src/app/site/pages/meetings/view-models/view-user'; +import { ViewOrganization } from 'src/app/site/pages/organization/view-models/view-organization'; + +export class ViewGender extends BaseViewModel { + public static readonly COLLECTION = Gender.COLLECTION; + protected _collection = Gender.COLLECTION; + + public get gender(): Gender { + return this._model; + } +} + +interface IGenderRelations { + organization: ViewOrganization; + users: ViewUser[]; +} +export interface ViewGender extends Gender, IGenderRelations {} diff --git a/client/src/app/site/pages/organization/pages/accounts/services/common/account-filter.service.ts b/client/src/app/site/pages/organization/pages/accounts/services/common/account-filter.service.ts index 70d6692fc3..4ee1462367 100644 --- a/client/src/app/site/pages/organization/pages/accounts/services/common/account-filter.service.ts +++ b/client/src/app/site/pages/organization/pages/accounts/services/common/account-filter.service.ts @@ -10,6 +10,8 @@ import { ActiveFiltersService } from 'src/app/site/services/active-filters.servi import { OperatorService } from 'src/app/site/services/operator.service'; import { UserControllerService } from 'src/app/site/services/user-controller.service'; +import { GenderControllerService } from '../../pages/gender/services/gender-controller.service'; + type Email = string; type Name = string; @@ -24,6 +26,12 @@ export class AccountFilterService extends BaseFilterListService { private userNameMap = new Map(); private userInMeetingMap = new Map(); + private genderFilterOption: OsFilter = { + property: `gender_id`, + label: _(`Gender`), + options: [] + }; + /** * @return Observable data for the filtered output subject */ @@ -55,13 +63,19 @@ export class AccountFilterService extends BaseFilterListService { store: ActiveFiltersService, private operator: OperatorService, private controller: UserControllerService, - private meetingRepo: MeetingControllerService + private meetingRepo: MeetingControllerService, + private genderRepo: GenderControllerService ) { super(store); this.controller.getViewModelListObservable().subscribe(users => { this.updateUserMaps(users); }); + this.updateFilterForRepo({ + repo: genderRepo, + filter: this.genderFilterOption, + noneOptionLabel: _(`not specified`) + }); } public filterMeeting(id: Id): void { @@ -131,17 +145,7 @@ export class AccountFilterService extends BaseFilterListService { { condition: [false, null], label: _(`Is no natural person`) } ] }, - { - property: `gender`, - label: _(`Gender`), - options: [ - { condition: `female`, label: _(`female`) }, - { condition: `male`, label: _(`male`) }, - { condition: `diverse`, label: _(`diverse`) }, - { condition: `non-binary`, label: _(`non-binary`) }, - { condition: null, label: _(`not specified`) } - ] - }, + this.genderFilterOption, { property: `hasEmail`, label: _(`Email address`), diff --git a/client/src/app/site/pages/organization/view-models/view-organization.ts b/client/src/app/site/pages/organization/view-models/view-organization.ts index b8e782af08..227d7f941b 100644 --- a/client/src/app/site/pages/organization/view-models/view-organization.ts +++ b/client/src/app/site/pages/organization/view-models/view-organization.ts @@ -4,6 +4,7 @@ import { BaseViewModel, ViewModelRelations } from '../../../base/base-view-model import { ViewMediafile } from '../../meetings/pages/mediafiles'; import { ViewMeeting } from '../../meetings/view-models/view-meeting'; import { ViewUser } from '../../meetings/view-models/view-user'; +import { ViewGender } from '../pages/accounts/pages/gender/view-models/view-gender'; import { ViewCommittee } from '../pages/committees'; import { ViewTheme } from '../pages/designs'; import { ViewOrganizationTag } from '../pages/organization-tags'; @@ -27,6 +28,7 @@ interface IOrganizationRelations { theme: ViewTheme; themes: ViewTheme[]; users: ViewUser[]; + genders: ViewGender[]; published_mediafiles: ViewMediafile[]; } export interface ViewOrganization extends Organization, ViewModelRelations {}