refactor(components) : merge file and change name

This commit is contained in:
2026-08-15 23:13:21 +07:00 Unverified
parent 0d673ccc11
commit b9451e26ab
8 changed files with 80 additions and 81 deletions
@@ -49,7 +49,7 @@ import {
import {
DiscordProfileDropdown,
type DiscordMemberProfile,
} from "@/components/dropdown-menu-avatar";
} from "@/components/avatar";
import { SaveStatusIndicator } from "@/components/save-status-indicator";
interface Question {
+1 -1
View File
@@ -35,7 +35,7 @@ import { ThemeToggle } from "@/components/theme-toggle";
import {
DropdownMenuAvatar,
type DiscordMemberProfile,
} from "@/components/dropdown-menu-avatar";
} from "@/components/avatar";
import { GuardedLink } from "@/components/guarded-link";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
+1 -1
View File
@@ -43,7 +43,7 @@ import {
FormItem,
FormMessage,
} from "@/components/ui/form";
import { DropdownMenuAvatar, DiscordMemberProfile } from "@/components/dropdown-menu-avatar";
import { DropdownMenuAvatar, DiscordMemberProfile } from "@/components/avatar";
import { getErrorMessage } from "@/lib/utils";
import {
Accordion,
+1 -2
View File
@@ -5,8 +5,7 @@ import type { LevelProgress } from "@/lib/leaderboard/level-progress";
import { formatInteger, formatVoiceDuration } from "@/lib/leaderboard/ranking";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { DiscordAvatar } from "@/components/discord-avatar";
import { DiscordServerTag } from "@/components/discord-server-tag";
import { DiscordAvatar, DiscordServerTag } from "@/components/avatar";
import {
Card,
CardContent,
+1 -1
View File
@@ -17,7 +17,7 @@ import {
} from "@/components/ui/sidebar";
import { ClipboardList, KeyRound, Server, LayoutDashboard, Upload, LucideIcon } from "lucide-react";
import { ThemeToggle } from "@/components/theme-toggle";
import { DropdownMenuAvatar, DiscordMemberProfile } from "@/components/dropdown-menu-avatar";
import { DropdownMenuAvatar, DiscordMemberProfile } from "@/components/avatar";
interface NavItem {
href: string;
@@ -3,6 +3,7 @@
import { useState, type ReactNode } from "react";
import { usePathname } from "next/navigation";
import Link from "next/link";
import Image from "next/image";
import { LogOut, UserRound } from "lucide-react";
import { signOut } from "next-auth/react";
import {
@@ -14,8 +15,12 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { DiscordAvatar } from "@/components/discord-avatar";
import { DiscordServerTag } from "@/components/discord-server-tag";
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import type {
DiscordMemberProfile as DiscordMemberProfileData,
DiscordRole,
@@ -23,11 +28,79 @@ import type {
import {
discordColorToHex,
} from "@/lib/discord/profile-appearance";
import { cn } from "@/lib/utils";
export type DiscordMemberProfile = DiscordMemberProfileData;
const DEFAULT_ROLE_COLOR = "#99aab5";
type DiscordAvatarProps = {
avatarUrl: string | null;
decorationUrl?: string | null;
alt: string;
fallback: string;
className?: string;
imageError?: boolean;
onImageError?: () => void;
};
export function DiscordAvatar({
avatarUrl,
decorationUrl,
alt,
fallback,
className,
imageError = false,
onImageError,
}: DiscordAvatarProps) {
return (
<div className={cn("relative shrink-0", className)}>
<Avatar className="size-full">
{!imageError && avatarUrl && (
<AvatarImage src={avatarUrl} alt={alt} onError={onImageError} />
)}
<AvatarFallback>{fallback}</AvatarFallback>
</Avatar>
{decorationUrl && (
<Image
src={decorationUrl}
alt=""
aria-hidden
width={160}
height={160}
className="pointer-events-none absolute left-1/2 top-1/2 size-[125%] max-w-none -translate-x-1/2 -translate-y-1/2 object-contain"
unoptimized
/>
)}
</div>
);
}
export function DiscordServerTag({
primaryGuild,
}: Pick<DiscordMemberProfile, "primaryGuild">) {
if (!primaryGuild) return null;
return (
<Badge
variant="secondary"
className="h-6 shrink-0 rounded-full px-2 text-xs"
aria-label={`Server tag ${primaryGuild.tag}`}
>
{primaryGuild.badgeUrl && (
<Image
src={primaryGuild.badgeUrl}
alt=""
width={16}
height={16}
unoptimized
/>
)}
{primaryGuild.tag}
</Badge>
);
}
function initials(value: string) {
return value.trim().slice(0, 2).toLocaleUpperCase("th-TH") || "?";
}
-45
View File
@@ -1,45 +0,0 @@
import Image from "next/image";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
type DiscordAvatarProps = {
avatarUrl: string | null;
decorationUrl?: string | null;
alt: string;
fallback: string;
className?: string;
imageError?: boolean;
onImageError?: () => void;
};
export function DiscordAvatar({
avatarUrl,
decorationUrl,
alt,
fallback,
className,
imageError = false,
onImageError,
}: DiscordAvatarProps) {
return (
<div className={cn("relative shrink-0", className)}>
<Avatar className="size-full">
{!imageError && avatarUrl && (
<AvatarImage src={avatarUrl} alt={alt} onError={onImageError} />
)}
<AvatarFallback>{fallback}</AvatarFallback>
</Avatar>
{decorationUrl && (
<Image
src={decorationUrl}
alt=""
aria-hidden
width={160}
height={160}
className="pointer-events-none absolute left-1/2 top-1/2 size-[125%] max-w-none -translate-x-1/2 -translate-y-1/2 object-contain"
unoptimized
/>
)}
</div>
);
}
-28
View File
@@ -1,28 +0,0 @@
import Image from "next/image";
import { Badge } from "@/components/ui/badge";
import type { DiscordMemberProfile } from "@/lib/discord/discord";
export function DiscordServerTag({
primaryGuild,
}: Pick<DiscordMemberProfile, "primaryGuild">) {
if (!primaryGuild) return null;
return (
<Badge
variant="secondary"
className="h-6 shrink-0 rounded-full px-2 text-xs"
aria-label={`Server tag ${primaryGuild.tag}`}
>
{primaryGuild.badgeUrl && (
<Image
src={primaryGuild.badgeUrl}
alt=""
width={16}
height={16}
unoptimized
/>
)}
{primaryGuild.tag}
</Badge>
);
}