web: Improve user display in modals by falling back to username (#18243)

* web: Improve user display in modals by falling back to username

Fixes UX issues in user-related modals where users without a display name would show empty text.

Previously:
* "Are you sure you want to update User ""?" (empty display name)
* "Successfully updated User undefined"

Now:
* "Are you sure you want to update User john_doe?" (falls back to username)
* "Successfully updated User john_doe"

Topics of discussion:
* Wether the object label should be lowercased to keep the sentence flowing (In this case, lowercasing User)

* oops it did have quotes actually

* pls
This commit is contained in:
Dominic R
2025-11-24 08:36:35 -05:00
committed by GitHub
parent c18f6d2f21
commit e002243a8a
3 changed files with 52 additions and 15 deletions
+8 -7
View File
@@ -3,18 +3,20 @@ import "#elements/buttons/SpinnerButton/index";
import { parseAPIResponseError, pluckErrorDetail } from "#common/errors/network";
import { MessageLevel } from "#common/messages";
import { DeleteForm } from "#elements/forms/DeleteForm";
import { showMessage } from "#elements/messages/MessageContainer";
import { UserDeleteForm } from "#elements/user/utils";
import { msg, str } from "@lit/localize";
import { html, TemplateResult } from "lit";
import { customElement } from "lit/decorators.js";
@customElement("ak-user-active-form")
export class UserActiveForm extends DeleteForm {
export class UserActiveForm extends UserDeleteForm {
onSuccess(): void {
showMessage({
message: msg(str`Successfully updated ${this.objectLabel} ${this.obj?.name}`),
message: msg(
str`Successfully updated ${this.objectLabel} ${this.getObjectDisplayName()}`,
),
level: MessageLevel.success,
});
}
@@ -30,7 +32,8 @@ export class UserActiveForm extends DeleteForm {
});
}
renderModalInner(): TemplateResult {
override renderModalInner(): TemplateResult {
const objName = this.getFormattedObjectName();
return html`<section class="pf-c-modal-box__header pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1 class="pf-c-title pf-m-2xl">${msg(str`Update ${this.objectLabel}`)}</h1>
@@ -39,9 +42,7 @@ export class UserActiveForm extends DeleteForm {
<section class="pf-c-modal-box__body pf-m-light">
<form class="pf-c-form pf-m-horizontal">
<p>
${msg(
str`Are you sure you want to update ${this.objectLabel} "${this.obj?.name}"?`,
)}
${msg(str`Are you sure you want to update ${this.objectLabel}${objName}?`)}
</p>
</form>
</section>
+21 -8
View File
@@ -32,6 +32,22 @@ export class DeleteForm extends ModalButton {
@property({ attribute: false })
delete!: () => Promise<unknown>;
/**
* Get the display name for the object being deleted/updated.
*/
protected getObjectDisplayName(): string | undefined {
return this.obj?.name as string | undefined;
}
/**
* Get the formatted object name for display in messages.
* Returns ` "displayName"` with quotes if display name exists, empty string otherwise.
*/
protected getFormattedObjectName(): string {
const displayName = this.getObjectDisplayName();
return displayName ? ` "${displayName}"` : "";
}
confirm(): Promise<void> {
return this.delete()
.then(() => {
@@ -54,7 +70,9 @@ export class DeleteForm extends ModalButton {
onSuccess(): void {
showMessage({
message: msg(str`Successfully deleted ${this.objectLabel} ${this.obj?.name}`),
message: msg(
str`Successfully deleted ${this.objectLabel} ${this.getObjectDisplayName()}`,
),
level: MessageLevel.success,
});
}
@@ -71,12 +89,7 @@ export class DeleteForm extends ModalButton {
}
renderModalInner(): TemplateResult {
let objName = this.obj?.name;
if (objName) {
objName = ` "${objName}"`;
} else {
objName = "";
}
const objName = this.getFormattedObjectName();
return html`<section class="pf-c-modal-box__header pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1 class="pf-c-title pf-m-2xl">${msg(str`Delete ${this.objectLabel}`)}</h1>
@@ -85,7 +98,7 @@ export class DeleteForm extends ModalButton {
<section class="pf-c-modal-box__body pf-m-light">
<form class="pf-c-form pf-m-horizontal">
<p>
${msg(str`Are you sure you want to delete ${this.objectLabel} ${objName}?`)}
${msg(str`Are you sure you want to delete ${this.objectLabel}${objName}?`)}
</p>
</form>
</section>
+23
View File
@@ -1,3 +1,5 @@
import { DeleteForm } from "#elements/forms/DeleteForm";
import { User } from "@goauthentik/api";
export function UserOption(user: User): string {
@@ -17,3 +19,24 @@ export function UserOption(user: User): string {
}
return finalString;
}
/**
* Get a display-friendly name for a user object.
* Falls back to username if no display name (name field) is set.
*
* @param user - The user object
* @returns The user's display name or username
*/
export function getUserDisplayName(user: User): string {
return user.name || user.username;
}
/**
* Base class for delete/update forms that work with User objects.
* Automatically uses username as fallback when user has no display name.
*/
export class UserDeleteForm extends DeleteForm {
protected override getObjectDisplayName(): string | undefined {
return this.obj ? getUserDisplayName(this.obj as unknown as User) : undefined;
}
}