mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-06 14:22:15 +00:00
feat(console): update deps, alternative hash function with fixed colors, use preferrenLoginName for hashing, fix iam write permissions, user img upload (#1846)
* chore(deps-dev): bump @types/jasmine from 3.6.9 to 3.7.7 in /console (#1824) Bumps [@types/jasmine](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jasmine) from 3.6.9 to 3.7.7. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jasmine) --- updated-dependencies: - dependency-name: "@types/jasmine" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump google-protobuf from 3.15.8 to 3.17.2 in /console (#1823) Bumps [google-protobuf](https://github.com/protocolbuffers/protobuf) from 3.15.8 to 3.17.2. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.15.8...v3.17.2) --- updated-dependencies: - dependency-name: google-protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @angular/animations from 12.0.0 to 12.0.3 in /console (#1821) Bumps [@angular/animations](https://github.com/angular/angular/tree/HEAD/packages/animations) from 12.0.0 to 12.0.3. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/12.0.3/packages/animations) --- updated-dependencies: - dependency-name: "@angular/animations" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @angular/material from 12.0.0 to 12.0.3 in /console (#1819) Bumps [@angular/material](https://github.com/angular/components) from 12.0.0 to 12.0.3. - [Release notes](https://github.com/angular/components/releases) - [Changelog](https://github.com/angular/components/blob/12.0.3/CHANGELOG.md) - [Commits](https://github.com/angular/components/compare/12.0.0...12.0.3) --- updated-dependencies: - dependency-name: "@angular/material" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump prettier from 2.2.1 to 2.3.1 in /console (#1818) Bumps [prettier](https://github.com/prettier/prettier) from 2.2.1 to 2.3.1. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.2.1...2.3.1) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @angular/platform-browser-dynamic in /console (#1817) Bumps [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) from 12.0.0 to 12.0.3. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/12.0.3/packages/platform-browser-dynamic) --- updated-dependencies: - dependency-name: "@angular/platform-browser-dynamic" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/node from 14.14.37 to 15.12.1 in /console (#1815) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.37 to 15.12.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @angular/common from 12.0.0 to 12.0.3 in /console (#1814) Bumps [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) from 12.0.0 to 12.0.3. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/12.0.3/packages/common) --- updated-dependencies: - dependency-name: "@angular/common" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @angular/material-moment-adapter from 12.0.0 to 12.0.3 in /console (#1816) * chore(deps): bump @angular/material-moment-adapter in /console Bumps [@angular/material-moment-adapter](https://github.com/angular/components) from 12.0.0 to 12.0.3. - [Release notes](https://github.com/angular/components/releases) - [Changelog](https://github.com/angular/components/blob/12.0.3/CHANGELOG.md) - [Commits](https://github.com/angular/components/compare/12.0.0...12.0.3) --- updated-dependencies: - dependency-name: "@angular/material-moment-adapter" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> * sort Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Peintner <[email protected]> * chore(deps-dev): bump @angular/language-service in /console (#1822) Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 12.0.0 to 12.0.3. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/12.0.3/packages/language-service) --- updated-dependencies: - dependency-name: "@angular/language-service" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Peintner <[email protected]> * image cropper * fix: avatar bg colors, preferred username, login script * lint * membership color * rem logs * profile picture component * pic comp * dialog tirgger btn * trigger dialog, styles * lock * interceptor for org, upload, remove * tooltip * lint * stylelint * generate same credentials of username as in login * deletepic * fix disable privatelabeling on missing feature, i18n * lint * stylelint * block loading images if no feature * lint * optimize feature check Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
parent
2502f379d9
commit
1e77b8aeae
@@ -3,6 +3,7 @@
|
||||
<app-avatar [routerLink]="['/users/me']"
|
||||
*ngIf="user && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
|
||||
class="avatar"
|
||||
[forColor]="user?.preferredLoginName"
|
||||
[name]="user.human?.profile?.displayName ? user.human?.profile?.displayName : (user.human?.profile?.firstName + ' '+ user.human?.profile?.lastName)"
|
||||
[size]="100">
|
||||
</app-avatar>
|
||||
|
||||
-1
@@ -103,7 +103,6 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
|
||||
this.grid = false;
|
||||
}
|
||||
this.dataSource.data = this.grantedProjectList;
|
||||
console.log(resp.resultList);
|
||||
|
||||
this.loadingSubject.next(false);
|
||||
}).catch(error => {
|
||||
|
||||
@@ -13,190 +13,189 @@ import { StorageKey, StorageService } from 'src/app/services/storage.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-grant-create',
|
||||
templateUrl: './user-grant-create.component.html',
|
||||
styleUrls: ['./user-grant-create.component.scss'],
|
||||
selector: 'app-user-grant-create',
|
||||
templateUrl: './user-grant-create.component.html',
|
||||
styleUrls: ['./user-grant-create.component.scss'],
|
||||
})
|
||||
export class UserGrantCreateComponent implements OnDestroy {
|
||||
public context!: UserGrantContext;
|
||||
public context!: UserGrantContext;
|
||||
|
||||
public org!: Org.AsObject;
|
||||
public userId: string = '';
|
||||
public org!: Org.AsObject;
|
||||
public userId: string = '';
|
||||
|
||||
public projectId: string = '';
|
||||
public project!: GrantedProject.AsObject | Project.AsObject;
|
||||
public projectId: string = '';
|
||||
public project!: GrantedProject.AsObject | Project.AsObject;
|
||||
|
||||
public grantId: string = '';
|
||||
public rolesList: string[] = [];
|
||||
public grantId: string = '';
|
||||
public rolesList: string[] = [];
|
||||
|
||||
public STEPS: number = 2; // project, roles
|
||||
public currentCreateStep: number = 1;
|
||||
public STEPS: number = 2; // project, roles
|
||||
public currentCreateStep: number = 1;
|
||||
|
||||
public filterValue: string = '';
|
||||
public filterValue: string = '';
|
||||
|
||||
private subscription: Subscription = new Subscription();
|
||||
private subscription: Subscription = new Subscription();
|
||||
|
||||
public UserGrantContext: any = UserGrantContext;
|
||||
public UserGrantContext: any = UserGrantContext;
|
||||
|
||||
public grantedRoleKeysList: string[] = [];
|
||||
public grantedRoleKeysList: string[] = [];
|
||||
|
||||
public user!: User.AsObject;
|
||||
public UserTarget: any = UserTarget;
|
||||
public user!: User.AsObject;
|
||||
public UserTarget: any = UserTarget;
|
||||
|
||||
constructor(
|
||||
private userService: ManagementService,
|
||||
private toast: ToastService,
|
||||
private _location: Location,
|
||||
private route: ActivatedRoute,
|
||||
private authService: GrpcAuthService,
|
||||
private mgmtService: ManagementService,
|
||||
private storage: StorageService,
|
||||
) {
|
||||
this.subscription = this.route.params.subscribe((params: Params) => {
|
||||
const { projectid, grantid, userid } = params;
|
||||
this.context = UserGrantContext.NONE;
|
||||
constructor(
|
||||
private userService: ManagementService,
|
||||
private toast: ToastService,
|
||||
private _location: Location,
|
||||
private route: ActivatedRoute,
|
||||
private authService: GrpcAuthService,
|
||||
private mgmtService: ManagementService,
|
||||
private storage: StorageService,
|
||||
) {
|
||||
this.subscription = this.route.params.subscribe((params: Params) => {
|
||||
const { projectid, grantid, userid } = params;
|
||||
this.context = UserGrantContext.NONE;
|
||||
|
||||
this.projectId = projectid;
|
||||
this.grantId = grantid;
|
||||
this.userId = userid;
|
||||
this.projectId = projectid;
|
||||
this.grantId = grantid;
|
||||
this.userId = userid;
|
||||
|
||||
if (this.projectId && !this.grantId) {
|
||||
this.context = UserGrantContext.OWNED_PROJECT;
|
||||
if (this.projectId && !this.grantId) {
|
||||
this.context = UserGrantContext.OWNED_PROJECT;
|
||||
|
||||
this.mgmtService.getProjectByID(this.projectId).then(resp => {
|
||||
if (resp.project) {
|
||||
this.project = resp.project;
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.projectId && this.grantId) {
|
||||
this.context = UserGrantContext.GRANTED_PROJECT;
|
||||
this.mgmtService.getGrantedProjectByID(this.projectId, this.grantId).then(resp => {
|
||||
if (resp.grantedProject) {
|
||||
this.project = resp.grantedProject;
|
||||
}
|
||||
if (resp.grantedProject?.grantedRoleKeysList) {
|
||||
this.grantedRoleKeysList = resp.grantedProject?.grantedRoleKeysList;
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.userId) {
|
||||
this.context = UserGrantContext.USER;
|
||||
this.mgmtService.getUserByID(this.userId).then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
this.mgmtService.getProjectByID(this.projectId).then(resp => {
|
||||
if (resp.project) {
|
||||
this.project = resp.project;
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.projectId && this.grantId) {
|
||||
this.context = UserGrantContext.GRANTED_PROJECT;
|
||||
this.mgmtService.getGrantedProjectByID(this.projectId, this.grantId).then(resp => {
|
||||
if (resp.grantedProject) {
|
||||
this.project = resp.grantedProject;
|
||||
}
|
||||
if (resp.grantedProject?.grantedRoleKeysList) {
|
||||
this.grantedRoleKeysList = resp.grantedProject?.grantedRoleKeysList;
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.userId) {
|
||||
this.context = UserGrantContext.USER;
|
||||
this.mgmtService.getUserByID(this.userId).then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const temporg = this.storage.getItem<Org.AsObject>(StorageKey.organization);
|
||||
if (temporg) {
|
||||
this.org = temporg;
|
||||
const temporg = this.storage.getItem<Org.AsObject>(StorageKey.organization);
|
||||
if (temporg) {
|
||||
this.org = temporg;
|
||||
}
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this._location.back();
|
||||
}
|
||||
|
||||
public addGrant(): void {
|
||||
switch (this.context) {
|
||||
case UserGrantContext.OWNED_PROJECT:
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
case UserGrantContext.GRANTED_PROJECT:
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
this.grantId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
case UserGrantContext.USER:
|
||||
let grantId;
|
||||
|
||||
if ((this.project as GrantedProject.AsObject)?.grantId) {
|
||||
grantId = (this.project as GrantedProject.AsObject).grantId;
|
||||
}
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this._location.back();
|
||||
}
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
grantId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
case UserGrantContext.NONE:
|
||||
let tempGrantId;
|
||||
|
||||
public addGrant(): void {
|
||||
switch (this.context) {
|
||||
case UserGrantContext.OWNED_PROJECT:
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
case UserGrantContext.GRANTED_PROJECT:
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
this.grantId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
case UserGrantContext.USER:
|
||||
let grantId;
|
||||
|
||||
if ((this.project as GrantedProject.AsObject)?.grantId) {
|
||||
grantId = (this.project as GrantedProject.AsObject).grantId;
|
||||
}
|
||||
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
grantId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
case UserGrantContext.NONE:
|
||||
console.log('none');
|
||||
let tempGrantId;
|
||||
|
||||
if ((this.project as GrantedProject.AsObject)?.grantId) {
|
||||
tempGrantId = (this.project as GrantedProject.AsObject).grantId;
|
||||
}
|
||||
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
tempGrantId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
if ((this.project as GrantedProject.AsObject)?.grantId) {
|
||||
tempGrantId = (this.project as GrantedProject.AsObject).grantId;
|
||||
}
|
||||
}
|
||||
|
||||
public selectProject(project: Project.AsObject | GrantedProject.AsObject | any): void {
|
||||
this.project = project;
|
||||
this.projectId = project.id || project.projectId;
|
||||
|
||||
this.grantedRoleKeysList = project.grantedRoleKeysList ?? [];
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
tempGrantId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public selectUser(user: User.AsObject): void {
|
||||
this.userId = user.id;
|
||||
}
|
||||
public selectProject(project: Project.AsObject | GrantedProject.AsObject | any): void {
|
||||
this.project = project;
|
||||
this.projectId = project.id || project.projectId;
|
||||
|
||||
public selectRoles(roles: Role.AsObject[]): void {
|
||||
this.rolesList = roles.map(role => role.key);
|
||||
}
|
||||
this.grantedRoleKeysList = project.grantedRoleKeysList ?? [];
|
||||
}
|
||||
|
||||
public next(): void {
|
||||
this.currentCreateStep++;
|
||||
}
|
||||
public selectUser(user: User.AsObject): void {
|
||||
this.userId = user.id;
|
||||
}
|
||||
|
||||
public previous(): void {
|
||||
this.currentCreateStep--;
|
||||
}
|
||||
public selectRoles(roles: Role.AsObject[]): void {
|
||||
this.rolesList = roles.map(role => role.key);
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
public next(): void {
|
||||
this.currentCreateStep++;
|
||||
}
|
||||
|
||||
public previous(): void {
|
||||
this.currentCreateStep--;
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
</app-card>
|
||||
|
||||
<app-card *ngIf="user && user.human?.profile" class=" app-card" title="{{ 'USER.PROFILE.TITLE' | translate }}">
|
||||
<app-detail-form [genders]="genders" [languages]="languages" [username]="user.userName" [user]="user.human"
|
||||
<app-detail-form [showEditImage]="true" [preferredLoginName]="user.preferredLoginName" [genders]="genders" [languages]="languages" [username]="user.userName" [user]="user.human"
|
||||
(changedLanguage)="changedLanguage($event)" (submitData)="saveProfile($event)">
|
||||
</app-detail-form>
|
||||
</app-card>
|
||||
|
||||
+173
-172
@@ -11,196 +11,197 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { EditDialogComponent, EditDialogType } from './edit-dialog/edit-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-auth-user-detail',
|
||||
templateUrl: './auth-user-detail.component.html',
|
||||
styleUrls: ['./auth-user-detail.component.scss'],
|
||||
selector: 'app-auth-user-detail',
|
||||
templateUrl: './auth-user-detail.component.html',
|
||||
styleUrls: ['./auth-user-detail.component.scss'],
|
||||
})
|
||||
export class AuthUserDetailComponent implements OnDestroy {
|
||||
public user!: User.AsObject;
|
||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
public user!: User.AsObject;
|
||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
|
||||
private subscription: Subscription = new Subscription();
|
||||
private subscription: Subscription = new Subscription();
|
||||
|
||||
public loading: boolean = false;
|
||||
public loading: boolean = false;
|
||||
|
||||
public copied: string = '';
|
||||
public copied: string = '';
|
||||
|
||||
public ChangeType: any = ChangeType;
|
||||
public userLoginMustBeDomain: boolean = false;
|
||||
public UserState: any = UserState;
|
||||
public ChangeType: any = ChangeType;
|
||||
public userLoginMustBeDomain: boolean = false;
|
||||
public UserState: any = UserState;
|
||||
|
||||
public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER;
|
||||
public refreshChanges$: EventEmitter<void> = new EventEmitter();
|
||||
public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER;
|
||||
public refreshChanges$: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
private toast: ToastService,
|
||||
public userService: GrpcAuthService,
|
||||
private dialog: MatDialog,
|
||||
) {
|
||||
this.loading = true;
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
private toast: ToastService,
|
||||
public userService: GrpcAuthService,
|
||||
private dialog: MatDialog,
|
||||
) {
|
||||
this.loading = true;
|
||||
this.refreshUser();
|
||||
}
|
||||
|
||||
refreshUser(): void {
|
||||
this.refreshChanges$.emit();
|
||||
this.userService.getMyUser().then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
console.log(resp.user);
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
public saveProfile(profileData: Profile.AsObject): void {
|
||||
if (this.user.human) {
|
||||
this.user.human.profile = profileData;
|
||||
|
||||
this.userService
|
||||
.updateMyProfile(
|
||||
this.user.human.profile?.firstName,
|
||||
this.user.human.profile?.lastName,
|
||||
this.user.human.profile?.nickName,
|
||||
this.user.human.profile?.displayName,
|
||||
this.user.human.profile?.preferredLanguage,
|
||||
this.user.human.profile?.gender,
|
||||
)
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public saveEmail(email: string): void {
|
||||
this.userService
|
||||
.setMyEmail(email).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
|
||||
if (this.user.human) {
|
||||
const mailToSet = new Email();
|
||||
mailToSet.setEmail(email);
|
||||
this.user.human.email = mailToSet.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public enteredPhoneCode(code: string): void {
|
||||
this.userService.verifyMyPhone(code).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
this.refreshUser();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public changedLanguage(language: string): void {
|
||||
this.translate.use(language);
|
||||
}
|
||||
|
||||
public resendPhoneVerification(): void {
|
||||
this.userService.resendMyPhoneVerification().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public resendEmailVerification(): void {
|
||||
this.userService.resendMyEmailVerification().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public deletePhone(): void {
|
||||
this.userService.removeMyPhone().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEREMOVED', true);
|
||||
if (this.user.human?.phone) {
|
||||
const phone = new Phone();
|
||||
this.user.human.phone = phone.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
refreshUser(): void {
|
||||
this.refreshChanges$.emit();
|
||||
this.userService.getMyUser().then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
public saveProfile(profileData: Profile.AsObject): void {
|
||||
if (this.user.human) {
|
||||
this.user.human.profile = profileData;
|
||||
|
||||
this.userService
|
||||
.updateMyProfile(
|
||||
this.user.human.profile?.firstName,
|
||||
this.user.human.profile?.lastName,
|
||||
this.user.human.profile?.nickName,
|
||||
this.user.human.profile?.displayName,
|
||||
this.user.human.profile?.preferredLanguage,
|
||||
this.user.human.profile?.gender,
|
||||
)
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public saveEmail(email: string): void {
|
||||
this.userService
|
||||
.setMyEmail(email).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
|
||||
if (this.user.human) {
|
||||
const mailToSet = new Email();
|
||||
mailToSet.setEmail(email);
|
||||
this.user.human.email = mailToSet.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public enteredPhoneCode(code: string): void {
|
||||
this.userService.verifyMyPhone(code).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
public savePhone(phone: string): void {
|
||||
if (this.user.human) {
|
||||
this.userService
|
||||
.setMyPhone(phone).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
if (this.user.human) {
|
||||
const phoneToSet = new Phone();
|
||||
phoneToSet.setPhone(phone);
|
||||
this.user.human.phone = phoneToSet.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public changedLanguage(language: string): void {
|
||||
this.translate.use(language);
|
||||
}
|
||||
|
||||
public resendPhoneVerification(): void {
|
||||
this.userService.resendMyPhoneVerification().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
public openEditDialog(type: EditDialogType): void {
|
||||
switch (type) {
|
||||
case EditDialogType.PHONE:
|
||||
const dialogRefPhone = this.dialog.open(EditDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.SAVE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
labelKey: 'ACTIONS.NEWVALUE',
|
||||
titleKey: 'USER.LOGINMETHODS.PHONE.EDITTITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC',
|
||||
value: this.user.human?.phone?.phone,
|
||||
type: type,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
|
||||
public resendEmailVerification(): void {
|
||||
this.userService.resendMyEmailVerification().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
dialogRefPhone.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.savePhone(resp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public deletePhone(): void {
|
||||
this.userService.removeMyPhone().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEREMOVED', true);
|
||||
if (this.user.human?.phone) {
|
||||
const phone = new Phone();
|
||||
this.user.human.phone = phone.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
break;
|
||||
case EditDialogType.EMAIL:
|
||||
const dialogRefEmail = this.dialog.open(EditDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.SAVE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
labelKey: 'ACTIONS.NEWVALUE',
|
||||
titleKey: 'USER.LOGINMETHODS.EMAIL.EDITTITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.EMAIL.EDITDESC',
|
||||
value: this.user.human?.email?.email,
|
||||
type: type,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRefEmail.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.saveEmail(resp);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
public savePhone(phone: string): void {
|
||||
if (this.user.human) {
|
||||
this.userService
|
||||
.setMyPhone(phone).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
if (this.user.human) {
|
||||
const phoneToSet = new Phone();
|
||||
phoneToSet.setPhone(phone);
|
||||
this.user.human.phone = phoneToSet.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public openEditDialog(type: EditDialogType): void {
|
||||
switch (type) {
|
||||
case EditDialogType.PHONE:
|
||||
const dialogRefPhone = this.dialog.open(EditDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.SAVE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
labelKey: 'ACTIONS.NEWVALUE',
|
||||
titleKey: 'USER.LOGINMETHODS.PHONE.EDITTITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC',
|
||||
value: this.user.human?.phone?.phone,
|
||||
type: type,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRefPhone.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.savePhone(resp);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case EditDialogType.EMAIL:
|
||||
const dialogRefEmail = this.dialog.open(EditDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.SAVE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
labelKey: 'ACTIONS.NEWVALUE',
|
||||
titleKey: 'USER.LOGINMETHODS.EMAIL.EDITTITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.EMAIL.EDITDESC',
|
||||
value: this.user.human?.email?.email,
|
||||
type: type,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRefEmail.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.saveEmail(resp);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
<form [formGroup]="profileForm" *ngIf="profileForm" (ngSubmit)="submitForm()">
|
||||
<div class="content">
|
||||
<div class="user-form-content">
|
||||
<div class="user-form-content inner">
|
||||
<button class="camera-wrapper" type="button" (click)="showEditImage ? openUploadDialog() : null">
|
||||
<div class="i-wrapper" *ngIf="showEditImage">
|
||||
<i class="las la-camera"></i>
|
||||
</div>
|
||||
<img class="pic" [src]="profilePic" *ngIf="profilePic"/>
|
||||
<app-avatar
|
||||
*ngIf="!profilePic && user && user.profile?.displayName && user.profile?.firstName && user?.profile.lastName"
|
||||
class="avatar" [name]="user.profile?.displayName" [forColor]="preferredLoginName" [size]="80">
|
||||
</app-avatar>
|
||||
</button>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'USER.PROFILE.USERNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="userName" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="firstName" />
|
||||
|
||||
@@ -1,10 +1,60 @@
|
||||
|
||||
.content {
|
||||
.user-form-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -.5rem;
|
||||
|
||||
&.inner {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.camera-wrapper {
|
||||
margin: 0 .5rem;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
padding: 0;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
transition: all .3s ease;
|
||||
|
||||
.i-wrapper {
|
||||
border-radius: 50%;
|
||||
background-color: #00000050;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
i {
|
||||
font-size: 3rem;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.i-wrapper {
|
||||
background-color: #00000080;
|
||||
}
|
||||
}
|
||||
|
||||
.pic {
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
object-fit: contain;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.formfield {
|
||||
flex: 1 1 33%;
|
||||
margin: 0 .5rem;
|
||||
|
||||
@@ -1,92 +1,131 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Gender, Human, User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { AssetService } from 'src/app/services/asset.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { ProfilePictureComponent } from './profile-picture/profile-picture.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-detail-form',
|
||||
templateUrl: './detail-form.component.html',
|
||||
styleUrls: ['./detail-form.component.scss'],
|
||||
selector: 'app-detail-form',
|
||||
templateUrl: './detail-form.component.html',
|
||||
styleUrls: ['./detail-form.component.scss'],
|
||||
})
|
||||
export class DetailFormComponent implements OnDestroy, OnChanges {
|
||||
@Input() public username!: string;
|
||||
@Input() public user!: Human.AsObject;
|
||||
@Input() public disabled: boolean = false;
|
||||
@Input() public genders: Gender[] = [];
|
||||
@Input() public languages: string[] = ['de', 'en'];
|
||||
@Output() public submitData: EventEmitter<User> = new EventEmitter<User>();
|
||||
@Output() public changedLanguage: EventEmitter<string> = new EventEmitter<string>();
|
||||
@Input() public showEditImage: boolean = false;
|
||||
@Input() public preferredLoginName: string = '';
|
||||
@Input() public username!: string;
|
||||
@Input() public user!: Human.AsObject;
|
||||
@Input() public disabled: boolean = false;
|
||||
@Input() public genders: Gender[] = [];
|
||||
@Input() public languages: string[] = ['de', 'en'];
|
||||
@Output() public submitData: EventEmitter<User> = new EventEmitter<User>();
|
||||
@Output() public changedLanguage: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
public profileForm!: FormGroup;
|
||||
public profilePic: any = null;
|
||||
public profileForm!: FormGroup;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
private sub: Subscription = new Subscription();
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.profileForm = this.fb.group({
|
||||
userName: [{ value: '', disabled: true }, [
|
||||
Validators.required,
|
||||
]],
|
||||
firstName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
lastName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
nickName: [{ value: '', disabled: this.disabled }],
|
||||
displayName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
gender: [{ value: 0, disabled: this.disabled }],
|
||||
preferredLanguage: [{ value: '', disabled: this.disabled }],
|
||||
});
|
||||
}
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private dialog: MatDialog,
|
||||
private assetService: AssetService,
|
||||
private toast: ToastService,
|
||||
private sanitizer: DomSanitizer,
|
||||
) {
|
||||
this.profileForm = this.fb.group({
|
||||
userName: [{ value: '', disabled: true }, [
|
||||
Validators.required,
|
||||
]],
|
||||
firstName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
lastName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
nickName: [{ value: '', disabled: this.disabled }],
|
||||
displayName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
gender: [{ value: 0, disabled: this.disabled }],
|
||||
preferredLanguage: [{ value: '', disabled: this.disabled }],
|
||||
});
|
||||
|
||||
public ngOnChanges(): void {
|
||||
this.profileForm = this.fb.group({
|
||||
userName: [{ value: '', disabled: true }, [
|
||||
Validators.required,
|
||||
]],
|
||||
firstName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
lastName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
nickName: [{ value: '', disabled: this.disabled }],
|
||||
displayName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
gender: [{ value: 0, disabled: this.disabled }],
|
||||
preferredLanguage: [{ value: '', disabled: this.disabled }],
|
||||
});
|
||||
this.loadAvatar();
|
||||
}
|
||||
|
||||
this.profileForm.patchValue({ userName: this.username, ...this.user.profile });
|
||||
public ngOnChanges(): void {
|
||||
this.profileForm = this.fb.group({
|
||||
userName: [{ value: '', disabled: true }, [
|
||||
Validators.required,
|
||||
]],
|
||||
firstName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
lastName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
nickName: [{ value: '', disabled: this.disabled }],
|
||||
displayName: [{ value: '', disabled: this.disabled }, Validators.required],
|
||||
gender: [{ value: 0, disabled: this.disabled }],
|
||||
preferredLanguage: [{ value: '', disabled: this.disabled }],
|
||||
});
|
||||
|
||||
if (this.preferredLanguage) {
|
||||
this.sub = this.preferredLanguage.valueChanges.subscribe(value => {
|
||||
this.changedLanguage.emit(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
this.profileForm.patchValue({ userName: this.username, ...this.user.profile });
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
if (this.preferredLanguage) {
|
||||
this.sub = this.preferredLanguage.valueChanges.subscribe(value => {
|
||||
this.changedLanguage.emit(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.submitData.emit(this.profileForm.value);
|
||||
}
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
public get userName(): AbstractControl | null {
|
||||
return this.profileForm.get('userName');
|
||||
}
|
||||
public submitForm(): void {
|
||||
this.submitData.emit(this.profileForm.value);
|
||||
}
|
||||
|
||||
public get firstName(): AbstractControl | null {
|
||||
return this.profileForm.get('firstName');
|
||||
}
|
||||
public get lastName(): AbstractControl | null {
|
||||
return this.profileForm.get('lastName');
|
||||
}
|
||||
public get nickName(): AbstractControl | null {
|
||||
return this.profileForm.get('nickName');
|
||||
}
|
||||
public get displayName(): AbstractControl | null {
|
||||
return this.profileForm.get('displayName');
|
||||
}
|
||||
public get gender(): AbstractControl | null {
|
||||
return this.profileForm.get('gender');
|
||||
}
|
||||
public get preferredLanguage(): AbstractControl | null {
|
||||
return this.profileForm.get('preferredLanguage');
|
||||
}
|
||||
public openUploadDialog(): void {
|
||||
const dialogRef = this.dialog.open(ProfilePictureComponent, {
|
||||
data: {
|
||||
profilePic: this.profilePic,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public loadAvatar(): Promise<any> {
|
||||
return this.assetService.load(`users/me/avatar`).then(data => {
|
||||
const objectURL = URL.createObjectURL(data);
|
||||
this.profilePic = this.sanitizer.bypassSecurityTrustUrl(objectURL);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public get userName(): AbstractControl | null {
|
||||
return this.profileForm.get('userName');
|
||||
}
|
||||
|
||||
public get firstName(): AbstractControl | null {
|
||||
return this.profileForm.get('firstName');
|
||||
}
|
||||
public get lastName(): AbstractControl | null {
|
||||
return this.profileForm.get('lastName');
|
||||
}
|
||||
public get nickName(): AbstractControl | null {
|
||||
return this.profileForm.get('nickName');
|
||||
}
|
||||
public get displayName(): AbstractControl | null {
|
||||
return this.profileForm.get('displayName');
|
||||
}
|
||||
public get gender(): AbstractControl | null {
|
||||
return this.profileForm.get('gender');
|
||||
}
|
||||
public get preferredLanguage(): AbstractControl | null {
|
||||
return this.profileForm.get('preferredLanguage');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,28 +4,38 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ImageCropperModule } from 'ngx-image-cropper';
|
||||
import { DropzoneModule } from 'src/app/directives/dropzone/dropzone.module';
|
||||
import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
|
||||
import { DetailFormComponent } from './detail-form.component';
|
||||
import { ProfilePictureComponent } from './profile-picture/profile-picture.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
DetailFormComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
TranslateModule,
|
||||
MatSelectModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
TranslateModule,
|
||||
InputModule,
|
||||
],
|
||||
exports: [
|
||||
DetailFormComponent,
|
||||
],
|
||||
declarations: [
|
||||
DetailFormComponent,
|
||||
ProfilePictureComponent,
|
||||
],
|
||||
imports: [
|
||||
DropzoneModule,
|
||||
AvatarModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
ImageCropperModule,
|
||||
TranslateModule,
|
||||
MatSelectModule,
|
||||
MatButtonModule,
|
||||
MatTooltipModule,
|
||||
MatIconModule,
|
||||
TranslateModule,
|
||||
InputModule,
|
||||
],
|
||||
exports: [
|
||||
DetailFormComponent,
|
||||
],
|
||||
})
|
||||
export class DetailFormModule { }
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<span class="title" mat-dialog-title>{{'USER.PROFILE.AVATAR.UPLOADTITLE' | translate}}</span>
|
||||
<div mat-dialog-content>
|
||||
<p class="desc">{{'USER.PROFILE.AVATAR.CURRENT' | translate}}</p>
|
||||
<div class="current-pic-wrapper">
|
||||
<img class="pic" [src]="data.profilePic" *ngIf="data.profilePic"/>
|
||||
<span class="fill-space"></span>
|
||||
<input #selectedFile style="display: none;" class="file-input" type="file" (change)="onDrop($event)">
|
||||
<button class="btn" mat-stroked-button type="button" (click)="selectedFile.click();">{{'USER.PROFILE.AVATAR.UPLOADBTN' | translate}}</button>
|
||||
<button *ngIf="data.profilePic" matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn" (click)="deletePic()" mat-icon-button><mat-icon>remove_circle</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="imageChangedEvent">
|
||||
<p class="desc">{{'USER.PROFILE.AVATAR.PREVIEW' | translate}}</p>
|
||||
|
||||
<div class="cropped-preview" *ngIf="croppedImage">
|
||||
<img class="pic" [src]="croppedImage"/>
|
||||
<button color="primary" mat-raised-button (click)="upload()">{{'USER.PROFILE.AVATAR.UPLOAD' | translate}}</button>
|
||||
</div>
|
||||
|
||||
<p class="error" *ngIf="showCropperError">{{'USER.PROFILE.AVATAR.CROPPERERROR' | translate}}</p>
|
||||
|
||||
<image-cropper
|
||||
class="cropper"
|
||||
[imageChangedEvent]="imageChangedEvent"
|
||||
[maintainAspectRatio]="true"
|
||||
[aspectRatio]="4 / 4"
|
||||
[roundCropper]="true"
|
||||
[autoCrop]="true"
|
||||
(imageCropped)="imageCropped($event)"
|
||||
(loadImageFailed)="loadImageFailed()"
|
||||
></image-cropper>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button color="primary" mat-raised-button class="ok-button" (click)="closeDialog()">
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
.title {
|
||||
font-size: 1.2rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: var(--grey);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.current-pic-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.pic {
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
object-fit: contain;
|
||||
border-radius: 50%;
|
||||
background-color: #00000030;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #f44336;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.cropped-preview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
|
||||
.pic {
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
object-fit: contain;
|
||||
border-radius: 50%;
|
||||
background-color: #00000030;
|
||||
}
|
||||
}
|
||||
|
||||
.cropper {
|
||||
margin: 1rem 0;
|
||||
width: auto;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.ok-button {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--cropper-outline-color: black !important;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProfilePictureComponent } from './profile-picture.component';
|
||||
|
||||
describe('ProfilePictureComponent', () => {
|
||||
let component: ProfilePictureComponent;
|
||||
let fixture: ComponentFixture<ProfilePictureComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ProfilePictureComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProfilePictureComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ImageCroppedEvent } from 'ngx-image-cropper';
|
||||
import { AssetService } from 'src/app/services/asset.service';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-profile-picture',
|
||||
templateUrl: './profile-picture.component.html',
|
||||
styleUrls: ['./profile-picture.component.scss'],
|
||||
})
|
||||
export class ProfilePictureComponent implements OnInit {
|
||||
public isHovering: boolean = false;
|
||||
|
||||
public imageChangedEvent: any = '';
|
||||
public imageChangedFormat: string = '';
|
||||
public croppedImage: any = '';
|
||||
public showCropperError: boolean = false;
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
public dialogRef: MatDialogRef<ProfilePictureComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
private toast: ToastService,
|
||||
private assetService: AssetService) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
}
|
||||
|
||||
public toggleHover(isHovering: boolean): void {
|
||||
this.isHovering = isHovering;
|
||||
}
|
||||
|
||||
public onDrop(event: any): Promise<any> | void {
|
||||
const filelist: FileList = event.target.files;
|
||||
console.log(event.target.files);
|
||||
this.imageChangedEvent = event;
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
this.imageChangedFormat = file.type;
|
||||
}
|
||||
}
|
||||
|
||||
public deletePic(): void {
|
||||
console.log('delete');
|
||||
this.authService.removeMyAvatar().then(() => {
|
||||
this.toast.showInfo('USER.PROFILE.AVATAR.DELETESUCCESS', true);
|
||||
this.data.profilePic = null;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private handleUploadPromise(task: Promise<any>): Promise<any> {
|
||||
return task.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.UPLOADSUCCESS', true);
|
||||
this.data.profilePic = this.croppedImage;
|
||||
}).catch(error => this.toast.showError(error));
|
||||
}
|
||||
|
||||
public fileChangeEvent(event: any): void {
|
||||
this.imageChangedEvent = event;
|
||||
}
|
||||
|
||||
public imageCropped(event: ImageCroppedEvent): void {
|
||||
this.showCropperError = false;
|
||||
this.croppedImage = event.base64;
|
||||
}
|
||||
|
||||
public upload(): Promise<any> | void {
|
||||
const formData = new FormData();
|
||||
const splitted = this.croppedImage.split(';base64,');
|
||||
if (splitted[1]) {
|
||||
const blob = this.base64toBlob(splitted[1]);
|
||||
formData.append('file', blob);
|
||||
return this.handleUploadPromise(this.assetService.upload('users/me/avatar', formData));
|
||||
}
|
||||
}
|
||||
|
||||
public loadImageFailed(): void {
|
||||
this.showCropperError = true;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
public base64toBlob(b64: string): Blob {
|
||||
const byteCharacters = atob(b64);
|
||||
const byteNumbers = new Array(byteCharacters.length);
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||||
}
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
const blob = new Blob([byteArray], { type: this.imageChangedFormat });
|
||||
return blob;
|
||||
}
|
||||
}
|
||||
@@ -11,195 +11,195 @@ import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-memberships',
|
||||
templateUrl: './memberships.component.html',
|
||||
styleUrls: ['./memberships.component.scss'],
|
||||
animations: [
|
||||
trigger('cardAnimation', [
|
||||
transition('* => *', [
|
||||
query('@animate', stagger('40ms', animateChild()), { optional: true }),
|
||||
]),
|
||||
]),
|
||||
trigger('animate', [
|
||||
transition(':enter', [
|
||||
animate('.2s ease-in', keyframes([
|
||||
style({ opacity: 0, offset: 0 }),
|
||||
style({ opacity: .5, transform: 'scale(1.05)', offset: 0.3 }),
|
||||
style({ opacity: 1, transform: 'scale(1)', offset: 1 }),
|
||||
])),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
selector: 'app-memberships',
|
||||
templateUrl: './memberships.component.html',
|
||||
styleUrls: ['./memberships.component.scss'],
|
||||
animations: [
|
||||
trigger('cardAnimation', [
|
||||
transition('* => *', [
|
||||
query('@animate', stagger('40ms', animateChild()), { optional: true }),
|
||||
]),
|
||||
]),
|
||||
trigger('animate', [
|
||||
transition(':enter', [
|
||||
animate('.2s ease-in', keyframes([
|
||||
style({ opacity: 0, offset: 0 }),
|
||||
style({ opacity: .5, transform: 'scale(1.05)', offset: 0.3 }),
|
||||
style({ opacity: 1, transform: 'scale(1)', offset: 1 }),
|
||||
])),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class MembershipsComponent implements OnInit {
|
||||
public loading: boolean = false;
|
||||
public memberships!: Membership.AsObject[] | UserGrant.AsObject[];
|
||||
public totalResult: number = 0;
|
||||
public loading: boolean = false;
|
||||
public memberships!: Membership.AsObject[] | UserGrant.AsObject[];
|
||||
public totalResult: number = 0;
|
||||
|
||||
@Input() public auth: boolean = false;
|
||||
@Input() public user!: User.AsObject;
|
||||
@Input() public disabled: boolean = false;
|
||||
@Input() public auth: boolean = false;
|
||||
@Input() public user!: User.AsObject;
|
||||
@Input() public disabled: boolean = false;
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private mgmtService: ManagementService,
|
||||
private adminService: AdminService,
|
||||
private dialog: MatDialog,
|
||||
private toast: ToastService,
|
||||
private router: Router,
|
||||
) { }
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private mgmtService: ManagementService,
|
||||
private adminService: AdminService,
|
||||
private dialog: MatDialog,
|
||||
private toast: ToastService,
|
||||
private router: Router,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadManager(this.user.id);
|
||||
ngOnInit(): void {
|
||||
this.loadManager(this.user.id);
|
||||
}
|
||||
|
||||
public async loadManager(userId: string): Promise<void> {
|
||||
if (this.auth) {
|
||||
this.authService.listMyUserGrants(100, 0, []).then(resp => {
|
||||
this.memberships = resp.resultList;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
this.mgmtService.listUserMemberships(userId, 100, 0, []).then(resp => {
|
||||
this.memberships = resp.resultList;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async loadManager(userId: string): Promise<void> {
|
||||
if (this.auth) {
|
||||
this.authService.listMyUserGrants(100, 0, []).then(resp => {
|
||||
this.memberships = resp.resultList;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
this.mgmtService.listUserMemberships(userId, 100, 0, []).then(resp => {
|
||||
this.memberships = resp.resultList;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
this.loading = false;
|
||||
});
|
||||
public navigateToObject(): void {
|
||||
if (!this.disabled) {
|
||||
this.router.navigate(['/users', this.user.id, 'memberships']);
|
||||
}
|
||||
}
|
||||
|
||||
public addMember(): void {
|
||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||
width: '400px',
|
||||
data: {
|
||||
user: this.user,
|
||||
},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp && resp.creationType !== undefined) {
|
||||
switch (resp.creationType) {
|
||||
case CreationType.IAM:
|
||||
this.createIamMember(resp);
|
||||
break;
|
||||
case CreationType.ORG:
|
||||
this.createOrgMember(resp);
|
||||
break;
|
||||
case CreationType.PROJECT_OWNED:
|
||||
this.createOwnedProjectMember(resp);
|
||||
break;
|
||||
case CreationType.PROJECT_GRANTED:
|
||||
this.createGrantedProjectMember(resp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public navigateToObject(): void {
|
||||
if (!this.disabled) {
|
||||
this.router.navigate(['/users', this.user.id, 'memberships']);
|
||||
}
|
||||
}
|
||||
public createIamMember(response: any): void {
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
public addMember(): void {
|
||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||
width: '400px',
|
||||
data: {
|
||||
user: this.user,
|
||||
},
|
||||
});
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.adminService.addIAMMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp && resp.creationType !== undefined) {
|
||||
switch (resp.creationType) {
|
||||
case CreationType.IAM:
|
||||
this.createIamMember(resp);
|
||||
break;
|
||||
case CreationType.ORG:
|
||||
this.createOrgMember(resp);
|
||||
break;
|
||||
case CreationType.PROJECT_OWNED:
|
||||
this.createOwnedProjectMember(resp);
|
||||
break;
|
||||
case CreationType.PROJECT_GRANTED:
|
||||
this.createGrantedProjectMember(resp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
private createOrgMember(response: any): void {
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.mgmtService.addOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private createGrantedProjectMember(response: any): void {
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
response.projectId,
|
||||
response.grantId,
|
||||
user.id,
|
||||
roles,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public createIamMember(response: any): void {
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
private createOwnedProjectMember(response: any): void {
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.adminService.addIAMMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.addProjectMember(response.projectId, user.id, roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private createOrgMember(response: any): void {
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
getColor(type: Membership.AsObject[] | UserGrant.AsObject[]): string {
|
||||
const gen = type.toString();
|
||||
const colors = [
|
||||
'rgb(201, 115, 88)',
|
||||
'rgb(226, 176, 50)',
|
||||
'rgb(112, 89, 152)',
|
||||
];
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.mgmtService.addOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
let hash = 0;
|
||||
if (gen.length === 0) {
|
||||
return colors[hash];
|
||||
}
|
||||
|
||||
private createGrantedProjectMember(response: any): void {
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
response.projectId,
|
||||
response.grantId,
|
||||
user.id,
|
||||
roles,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private createOwnedProjectMember(response: any): void {
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.addProjectMember(response.projectId, user.id, roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getColor(type: Membership.AsObject[] | UserGrant.AsObject[]): string {
|
||||
const gen = type.toString();
|
||||
const colors = [
|
||||
'rgb(201, 115, 88)',
|
||||
'rgb(226, 176, 50)',
|
||||
'rgb(112, 89, 152)',
|
||||
];
|
||||
|
||||
let hash = 0;
|
||||
if (gen.length === 0) {
|
||||
return colors[hash];
|
||||
}
|
||||
for (let i = 0; i < gen.length; i++) {
|
||||
// tslint:disable-next-line: no-bitwise
|
||||
hash = gen.charCodeAt(i) + ((hash << 5) - hash);
|
||||
// tslint:disable-next-line: no-bitwise
|
||||
hash = hash & hash;
|
||||
}
|
||||
hash = ((hash % colors.length) + colors.length) % colors.length;
|
||||
return colors[hash];
|
||||
for (let i = 0; i < gen.length; i++) {
|
||||
// tslint:disable-next-line: no-bitwise
|
||||
hash = gen.charCodeAt(i) + ((hash << 5) - hash);
|
||||
// tslint:disable-next-line: no-bitwise
|
||||
hash = hash & hash;
|
||||
}
|
||||
hash = ((hash % colors.length) + colors.length) % colors.length;
|
||||
return colors[hash];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,59 +51,59 @@ import { UserDetailComponent } from './user-detail/user-detail.component';
|
||||
import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AuthUserDetailComponent,
|
||||
UserDetailComponent,
|
||||
EditDialogComponent,
|
||||
AuthUserMfaComponent,
|
||||
AuthPasswordlessComponent,
|
||||
UserMfaComponent,
|
||||
PasswordlessComponent,
|
||||
ThemeSettingComponent,
|
||||
PasswordComponent,
|
||||
CodeDialogComponent,
|
||||
MembershipsComponent,
|
||||
ExternalIdpsComponent,
|
||||
ContactComponent,
|
||||
ResendEmailDialogComponent,
|
||||
DialogU2FComponent,
|
||||
AuthFactorDialogComponent,
|
||||
],
|
||||
imports: [
|
||||
UserDetailRoutingModule,
|
||||
ChangesModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
DetailFormModule,
|
||||
DetailFormMachineModule,
|
||||
WarnDialogModule,
|
||||
MatDialogModule,
|
||||
QRCodeModule,
|
||||
MetaLayoutModule,
|
||||
MatCheckboxModule,
|
||||
HasRolePipeModule,
|
||||
UserGrantsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
CardModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
MatTooltipModule,
|
||||
HasRoleModule,
|
||||
TranslateModule,
|
||||
MatTableModule,
|
||||
PaginatorModule,
|
||||
SharedModule,
|
||||
RefreshTableModule,
|
||||
CopyToClipboardModule,
|
||||
DetailLayoutModule,
|
||||
PasswordComplexityViewModule,
|
||||
MemberCreateDialogModule,
|
||||
TimestampToDatePipeModule,
|
||||
LocalizedDatePipeModule,
|
||||
InputModule,
|
||||
MachineKeysModule,
|
||||
],
|
||||
declarations: [
|
||||
AuthUserDetailComponent,
|
||||
UserDetailComponent,
|
||||
EditDialogComponent,
|
||||
AuthUserMfaComponent,
|
||||
AuthPasswordlessComponent,
|
||||
UserMfaComponent,
|
||||
PasswordlessComponent,
|
||||
ThemeSettingComponent,
|
||||
PasswordComponent,
|
||||
CodeDialogComponent,
|
||||
MembershipsComponent,
|
||||
ExternalIdpsComponent,
|
||||
ContactComponent,
|
||||
ResendEmailDialogComponent,
|
||||
DialogU2FComponent,
|
||||
AuthFactorDialogComponent,
|
||||
],
|
||||
imports: [
|
||||
UserDetailRoutingModule,
|
||||
ChangesModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
DetailFormModule,
|
||||
DetailFormMachineModule,
|
||||
WarnDialogModule,
|
||||
MatDialogModule,
|
||||
QRCodeModule,
|
||||
MetaLayoutModule,
|
||||
MatCheckboxModule,
|
||||
HasRolePipeModule,
|
||||
UserGrantsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
CardModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
MatTooltipModule,
|
||||
HasRoleModule,
|
||||
TranslateModule,
|
||||
MatTableModule,
|
||||
PaginatorModule,
|
||||
SharedModule,
|
||||
RefreshTableModule,
|
||||
CopyToClipboardModule,
|
||||
DetailLayoutModule,
|
||||
PasswordComplexityViewModule,
|
||||
MemberCreateDialogModule,
|
||||
TimestampToDatePipeModule,
|
||||
LocalizedDatePipeModule,
|
||||
InputModule,
|
||||
MachineKeysModule,
|
||||
],
|
||||
})
|
||||
export class UserDetailModule { }
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.read$', 'user.read:'+user?.id]">
|
||||
<app-card *ngIf="user.human" title="{{ 'USER.PROFILE.TITLE' | translate }}">
|
||||
<app-detail-form [disabled]="(canWrite$ | async) == false" [genders]="genders" [languages]="languages"
|
||||
<app-detail-form [preferredLoginName]="user.preferredLoginName" [disabled]="(canWrite$ | async) == false" [genders]="genders" [languages]="languages"
|
||||
[username]="user.userName" [user]="user.human" (submitData)="saveProfile($event)">
|
||||
</app-detail-form>
|
||||
</app-card>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
<app-avatar
|
||||
*ngIf="user.human && user.human.profile.displayName && user.human?.profile.firstName && user.human?.profile.lastName; else cog"
|
||||
class="avatar" [name]="user.human.profile.displayName" [size]="32">
|
||||
class="avatar" [name]="user.human.profile.displayName" [forColor]="user?.preferredLoginName" [size]="32">
|
||||
</app-avatar>
|
||||
<ng-template #cog>
|
||||
<div class="sa-icon" *ngIf="user.machine">
|
||||
|
||||
Reference in New Issue
Block a user