mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-01 00:27:24 +00:00
fix(console): member table updates (#4984)
* fix: members table refresh, delete with single role * trigger update after change * remove event propagation handler Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
parent
f4b920c11c
commit
3be1f51e48
@ -69,27 +69,32 @@ export class MembersTableComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public removeRole(member: Member.AsObject, role: string) {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'GRANTS.DIALOG.DELETE_TITLE',
|
||||
descriptionKey: 'GRANTS.DIALOG.DELETE_DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
if (member.rolesList.length === 1) {
|
||||
this.triggerDeleteMember(member);
|
||||
} else {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'ROLES.DIALOG.DELETE_TITLE',
|
||||
descriptionKey: 'ROLES.DIALOG.DELETE_DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
const newRoles = Object.assign([], member.rolesList);
|
||||
const index = newRoles.findIndex((r) => r === role);
|
||||
if (index > -1) {
|
||||
newRoles.splice(index, 1);
|
||||
member.rolesList = newRoles;
|
||||
this.updateRoles.emit({ member: member, change: newRoles });
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
const newRoles = Object.assign([], member.rolesList);
|
||||
|
||||
const index = newRoles.findIndex((r) => r === role);
|
||||
if (index > -1) {
|
||||
newRoles.splice(index, 1);
|
||||
member.rolesList = newRoles;
|
||||
this.updateRoles.emit({ member: member, change: newRoles });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public addRole(member: Member.AsObject) {
|
||||
|
@ -23,6 +23,7 @@ export class ProjectMembersComponent {
|
||||
public INITIALPAGESIZE: number = 25;
|
||||
public project!: Project.AsObject | GrantedProject.AsObject;
|
||||
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
|
||||
public projectId: string = '';
|
||||
public grantId: string = '';
|
||||
public projectName: string = '';
|
||||
public dataSource!: ProjectMembersDataSource;
|
||||
@ -40,7 +41,7 @@ export class ProjectMembersComponent {
|
||||
private mgmtService: ManagementService,
|
||||
private dialog: MatDialog,
|
||||
private toast: ToastService,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private route: ActivatedRoute,
|
||||
) {
|
||||
this.route.data.pipe(take(1)).subscribe((data) => {
|
||||
@ -49,78 +50,85 @@ export class ProjectMembersComponent {
|
||||
this.getRoleOptions();
|
||||
|
||||
this.route.params.subscribe((params) => {
|
||||
this.projectId = params.projectid;
|
||||
this.grantId = params.grantid;
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.getProjectByID(params.projectid).then((resp) => {
|
||||
if (resp.project) {
|
||||
this.project = resp.project;
|
||||
this.projectName = this.project.name;
|
||||
this.dataSource = new ProjectMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(this.project.id, this.projectType, 0, this.INITIALPAGESIZE);
|
||||
this.loadMembers();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.changePageFactory = (event?: PageEvent) => {
|
||||
return this.dataSource.loadMembers(
|
||||
(this.project as Project.AsObject).id,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? 0,
|
||||
event?.pageSize ?? this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
public loadMembers(): Promise<any> {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
return this.mgmtService.getProjectByID(this.projectId).then((resp) => {
|
||||
if (resp.project) {
|
||||
this.project = resp.project;
|
||||
this.projectName = this.project.name;
|
||||
this.dataSource = new ProjectMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(this.project.id, this.projectType, 0, this.INITIALPAGESIZE);
|
||||
|
||||
this.mgmtService.getIAM().then((iam) => {
|
||||
const isZitadel = iam.iamProjectId === (this.project as Project.AsObject).id;
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.PROJECT,
|
||||
param: { key: 'projectid', value: (this.project as Project.AsObject).id },
|
||||
routerLink: ['/projects', (this.project as Project.AsObject).id],
|
||||
isZitadel: isZitadel,
|
||||
}),
|
||||
];
|
||||
breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.getGrantedProjectByID(params.projectid, params.grantid).then((resp) => {
|
||||
if (resp.grantedProject) {
|
||||
this.project = resp.grantedProject;
|
||||
this.projectName = this.project.projectName;
|
||||
this.dataSource = new ProjectMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(this.project.projectId, this.projectType, 0, this.INITIALPAGESIZE, this.grantId);
|
||||
this.changePageFactory = (event?: PageEvent) => {
|
||||
return this.dataSource.loadMembers(
|
||||
(this.project as Project.AsObject).id,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? 0,
|
||||
event?.pageSize ?? this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
|
||||
this.changePageFactory = (event?: PageEvent) => {
|
||||
return this.dataSource.loadMembers(
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? 0,
|
||||
event?.pageSize ?? this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.GRANTEDPROJECT,
|
||||
param: { key: 'projectid', value: (this.project as GrantedProject.AsObject).projectId },
|
||||
routerLink: ['/projects', (this.project as GrantedProject.AsObject).projectId],
|
||||
}),
|
||||
];
|
||||
breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
}
|
||||
this.mgmtService.getIAM().then((iam) => {
|
||||
const isZitadel = iam.iamProjectId === (this.project as Project.AsObject).id;
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.PROJECT,
|
||||
param: { key: 'projectid', value: (this.project as Project.AsObject).id },
|
||||
routerLink: ['/projects', (this.project as Project.AsObject).id],
|
||||
isZitadel: isZitadel,
|
||||
}),
|
||||
];
|
||||
this.breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
return this.mgmtService.getGrantedProjectByID(this.projectId, this.grantId).then((resp) => {
|
||||
if (resp.grantedProject) {
|
||||
this.project = resp.grantedProject;
|
||||
this.projectName = this.project.projectName;
|
||||
this.dataSource = new ProjectMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(this.project.projectId, this.projectType, 0, this.INITIALPAGESIZE, this.grantId);
|
||||
|
||||
this.changePageFactory = (event?: PageEvent) => {
|
||||
return this.dataSource.loadMembers(
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? 0,
|
||||
event?.pageSize ?? this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.GRANTEDPROJECT,
|
||||
param: { key: 'projectid', value: (this.project as GrantedProject.AsObject).projectId },
|
||||
routerLink: ['/projects', (this.project as GrantedProject.AsObject).projectId],
|
||||
}),
|
||||
];
|
||||
this.breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
|
||||
public getRoleOptions(): void {
|
||||
@ -149,32 +157,29 @@ export class ProjectMembersComponent {
|
||||
Promise.all(
|
||||
this.selection.map((member) => {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
return this.mgmtService
|
||||
.removeProjectMember((this.project as Project.AsObject).id, member.userId)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
return this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
return this.mgmtService
|
||||
.removeProjectGrantMember((this.project as GrantedProject.AsObject).projectId, this.grantId, member.userId)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
}),
|
||||
).then(() => {
|
||||
setTimeout(() => {
|
||||
)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public removeProjectMember(member: Member.AsObject | Member.AsObject): void {
|
||||
@ -189,6 +194,7 @@ export class ProjectMembersComponent {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService
|
||||
@ -201,6 +207,7 @@ export class ProjectMembersComponent {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -255,10 +262,14 @@ export class ProjectMembersComponent {
|
||||
this.mgmtService
|
||||
.updateProjectMember((this.project as Project.AsObject).id, member.userId, selectionChange)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService
|
||||
@ -269,10 +280,14 @@ export class ProjectMembersComponent {
|
||||
selectionChange,
|
||||
)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -66,9 +66,13 @@ export class InstanceMembersComponent {
|
||||
.updateIAMMember(member.userId, selectionChange)
|
||||
.then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERCHANGED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
|
||||
@ -79,10 +83,13 @@ export class InstanceMembersComponent {
|
||||
.removeIAMMember(member.userId)
|
||||
.then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true);
|
||||
this.changePage.emit();
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}),
|
||||
);
|
||||
@ -99,6 +106,7 @@ export class InstanceMembersComponent {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
|
||||
@ -129,6 +137,7 @@ export class InstanceMembersComponent {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ export class OrgMembersComponent {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
|
||||
@ -72,8 +73,12 @@ export class OrgMembersComponent {
|
||||
.updateOrgMember(member.userId, selectionChange)
|
||||
.then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERCHANGED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
@ -85,16 +90,24 @@ export class OrgMembersComponent {
|
||||
.removeOrgMember(member.userId)
|
||||
.then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}),
|
||||
).then(() => {
|
||||
setTimeout(() => {
|
||||
)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public removeOrgMember(member: Member.AsObject): void {
|
||||
@ -109,6 +122,7 @@ export class OrgMembersComponent {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
|
||||
@ -138,6 +152,7 @@ export class OrgMembersComponent {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
@ -112,9 +112,13 @@ export class ProjectGrantDetailComponent {
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true);
|
||||
this.grant.state = newState;
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -141,9 +145,13 @@ export class ProjectGrantDetailComponent {
|
||||
.updateProjectGrant(this.grant.grantId, this.grant.projectId, selectionChange)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.changePage.emit();
|
||||
});
|
||||
}
|
||||
|
||||
@ -159,6 +167,7 @@ export class ProjectGrantDetailComponent {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}),
|
||||
@ -175,6 +184,7 @@ export class ProjectGrantDetailComponent {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
@ -203,9 +213,10 @@ export class ProjectGrantDetailComponent {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 3000);
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
@ -217,9 +228,13 @@ export class ProjectGrantDetailComponent {
|
||||
this.mgmtService
|
||||
.updateProjectGrantMember(this.grant.projectId, this.grant.grantId, member.userId, selectionChange)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERCHANGED', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
@ -232,9 +247,13 @@ export class ProjectGrantDetailComponent {
|
||||
this.mgmtService
|
||||
.updateProjectGrant(this.grant.grantId, this.grant.projectId, this.grant.grantedRoleKeysList)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
@ -257,8 +276,12 @@ export class ProjectGrantDetailComponent {
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true);
|
||||
this.grant.grantedRoleKeysList = resp.roles;
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
@ -1484,6 +1484,12 @@
|
||||
"DELETED": "Projekt gelöscht."
|
||||
}
|
||||
},
|
||||
"ROLES": {
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE": "Rolle löschen",
|
||||
"DELETE_DESCRIPTION": "Sie sind im Begriff eine Rolle zu entfernen. Wollen Sie fortfahren?"
|
||||
}
|
||||
},
|
||||
"NEXTSTEPS": {
|
||||
"TITLE": "Nächste Schritte"
|
||||
},
|
||||
|
@ -1484,6 +1484,12 @@
|
||||
"DELETED": "Project deleted."
|
||||
}
|
||||
},
|
||||
"ROLES": {
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE": "Delete role",
|
||||
"DELETE_DESCRIPTION": "You are about to delete a role. Are you sure?"
|
||||
}
|
||||
},
|
||||
"NEXTSTEPS": {
|
||||
"TITLE": "Next Steps"
|
||||
},
|
||||
|
@ -1484,6 +1484,12 @@
|
||||
"DELETED": "Projet supprimé."
|
||||
}
|
||||
},
|
||||
"ROLES": {
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE": "Supprimer un rôle",
|
||||
"DELETE_DESCRIPTION": "Vous êtes sur le point de supprimer un rôle. Êtes-vous sûr ?"
|
||||
}
|
||||
},
|
||||
"NEXTSTEPS": {
|
||||
"TITLE": "Étapes suivantes"
|
||||
},
|
||||
|
@ -1485,6 +1485,12 @@
|
||||
"DELETED": "Progetto eliminato con successo."
|
||||
}
|
||||
},
|
||||
"ROLES": {
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE": "Elima ruolo",
|
||||
"DELETE_DESCRIPTION": "Stai per eliminare un ruolo. Vuoi davvero continuare?"
|
||||
}
|
||||
},
|
||||
"NEXTSTEPS": {
|
||||
"TITLE": "Passi successivi"
|
||||
},
|
||||
|
@ -1483,6 +1483,12 @@
|
||||
"DELETED": "项目已删除"
|
||||
}
|
||||
},
|
||||
"ROLES": {
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE": "删除角色",
|
||||
"DELETE_DESCRIPTION": "您即将删除一个角色。你确定吗?"
|
||||
}
|
||||
},
|
||||
"NEXTSTEPS": {
|
||||
"TITLE": "下一步"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user