- {user.nickname}({user.username})
+ {formatDisplayName(user)}
{user.email}
diff --git a/web/src/components/console/site-header.tsx b/web/src/components/console/site-header.tsx
index e586039..50f5e98 100644
--- a/web/src/components/console/site-header.tsx
+++ b/web/src/components/console/site-header.tsx
@@ -1,6 +1,7 @@
import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"
import { SidebarTrigger } from "@/components/ui/sidebar"
+import Link from "next/link"
export function SiteHeader({ title = "Title" }: { title?: string }) {
return (
@@ -14,14 +15,14 @@ export function SiteHeader({ title = "Title" }: { title?: string }) {
{title}
diff --git a/web/src/components/layout/avatar-with-dropdown-menu.tsx b/web/src/components/layout/avatar-with-dropdown-menu.tsx
index 44d684a..c0ed32d 100644
--- a/web/src/components/layout/avatar-with-dropdown-menu.tsx
+++ b/web/src/components/layout/avatar-with-dropdown-menu.tsx
@@ -15,11 +15,11 @@ import { useToLogin } from "@/hooks/use-route";
import { CircleUser } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { getGravatarFromUser } from "@/utils/common/gravatar";
-import { getFallbackAvatarFromUsername } from "@/utils/common/username";
+import { formatDisplayName, getFallbackAvatarFromUsername } from "@/utils/common/username";
import { useAuth } from "@/contexts/auth-context";
export function AvatarWithDropdownMenu() {
- const {user} = useAuth();
+ const { user } = useAuth();
const toLogin = useToLogin();
const handleLogout = () => {
@@ -39,8 +39,17 @@ export function AvatarWithDropdownMenu() {
: }
-
- My Account
+
+
+ {user &&
+
+ {formatDisplayName(user)}
+
+ {user.email}
+
+
+
}
+
{user &&
Profile
@@ -51,7 +60,7 @@ export function AvatarWithDropdownMenu() {
- {user ? `Logout (${user.username})` : "Login"}
+ {user ? "Logout" : "Login"}
diff --git a/web/src/models/user.ts b/web/src/models/user.ts
index d1b975b..764eade 100644
--- a/web/src/models/user.ts
+++ b/web/src/models/user.ts
@@ -1,12 +1,12 @@
export interface User {
id: number;
username: string;
- nickname: string;
- avatarUrl: string;
+ nickname?: string;
+ avatarUrl?: string;
email: string;
- gender: string;
+ gender?: string;
role: string;
- language: string;
+ language?: string;
}
export enum Role {
diff --git a/web/src/utils/common/username.ts b/web/src/utils/common/username.ts
index 31a5db1..edb796f 100644
--- a/web/src/utils/common/username.ts
+++ b/web/src/utils/common/username.ts
@@ -1,5 +1,21 @@
import { User } from "@/models/user";
+/**
+ * Returns a single-character fallback avatar derived from a username.
+ *
+ * Rules:
+ * - If `username` is falsy (empty string, null, or undefined), returns `"N"`.
+ * - If the first character is an ASCII letter, returns the uppercase form of that character.
+ * - Otherwise returns the first character as-is.
+ *
+ * @param username - The username to derive the avatar character from.
+ * @returns A single character to use as a fallback avatar.
+ *
+ * @example
+ * getFallbackAvatarFromUsername("alice"); // "A"
+ * getFallbackAvatarFromUsername("1user"); // "1"
+ * getFallbackAvatarFromUsername(""); // "N"
+ */
export function getFallbackAvatarFromUsername(username: string): string {
if (!username) {
return "N";
@@ -11,6 +27,24 @@ export function getFallbackAvatarFromUsername(username: string): string {
return firstChar;
}
+/**
+ * Returns the first-character avatar for a User, preferring nickname over username.
+ *
+ * Behavior:
+ * - If `user.nickname` is present, derives the character from the nickname.
+ * - Else if `user.username` is present, derives the character from the username.
+ * - If neither is present, returns `"N"`.
+ *
+ * This function delegates the actual character selection logic to `getFallbackAvatarFromUsername`.
+ *
+ * @param user - The user object to extract the character from.
+ * @returns A single character derived from the user's nickname or username, or `"N"` if neither is present.
+ *
+ * @example
+ * getFirstCharFromUser({ nickname: "Bob", username: "bob123" }); // "B"
+ * getFirstCharFromUser({ username: "1user" }); // "1"
+ * getFirstCharFromUser({}); // "N"
+ */
export function getFirstCharFromUser(user: User): string {
if (user.nickname) {
return getFallbackAvatarFromUsername(user.nickname);
@@ -19,4 +53,21 @@ export function getFirstCharFromUser(user: User): string {
return getFallbackAvatarFromUsername(user.username);
}
return "N";
+}
+
+/**
+ * Formats a user's display name.
+ *
+ * - If the user has a `nickname`, returns `${nickname}(${username})`.
+ * - Otherwise returns the `username`.
+ *
+ * @param user - The user whose display name is to be formatted.
+ * @returns The formatted display name. If `username` is missing, the returned value may be falsy.
+ *
+ * @example
+ * formatDisplayName({ nickname: "Sam", username: "sam42" }); // "Sam(sam42)"
+ * formatDisplayName({ username: "sam42" }); // "sam42"
+ */
+export function formatDisplayName(user: User) :string {
+ return user?.nickname ? `${user.nickname}(${user.username})` : user.username;
}
\ No newline at end of file