refactor: 모든 페이지의 title 속성을 Tooltip 컴포넌트로 통일

- Admin.jsx: 버튼들의 title을 Tooltip으로 변경 (새로고침, 삭제, 다운로드, OP, 킥, 밴 등)
- PlayerStatsPage.jsx: 아이템/몹 이름에 Tooltip 적용
- ServerDetail.jsx: 적용된 모드 목록에 Tooltip 적용
This commit is contained in:
caadiq 2025-12-24 16:30:53 +09:00
parent 01aa85f041
commit 3661de82ca
3 changed files with 142 additions and 122 deletions

View file

@ -1239,16 +1239,17 @@ export default function Admin({ isMobile = false }) {
{/* 맨 아래로 스크롤 버튼 */} {/* 맨 아래로 스크롤 버튼 */}
<AnimatePresence> <AnimatePresence>
{!isAtBottom && logs.length > 0 && ( {!isAtBottom && logs.length > 0 && (
<Tooltip content="맨 아래로 이동">
<motion.button <motion.button
initial={{ opacity: 0, scale: 0.8 }} initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }} exit={{ opacity: 0, scale: 0.8 }}
onClick={scrollToBottom} onClick={scrollToBottom}
className="absolute bottom-[88px] right-4 p-3 bg-mc-green hover:bg-mc-green/80 text-white rounded-full shadow-xl shadow-black/50 transition-colors z-10" className="absolute bottom-[88px] right-4 p-3 bg-mc-green hover:bg-mc-green/80 text-white rounded-full shadow-xl shadow-black/50 transition-colors z-10"
title="맨 아래로 이동"
> >
<ArrowDown size={20} /> <ArrowDown size={20} />
</motion.button> </motion.button>
</Tooltip>
)} )}
</AnimatePresence> </AnimatePresence>
@ -1318,13 +1319,14 @@ export default function Admin({ isMobile = false }) {
로그 파일 로그 파일
{logFiles.length > 0 && <span className="text-xs text-zinc-500">({logFiles.length})</span>} {logFiles.length > 0 && <span className="text-xs text-zinc-500">({logFiles.length})</span>}
</h3> </h3>
<Tooltip content="새로고침">
<button <button
onClick={fetchLogFiles} onClick={fetchLogFiles}
className="p-2 text-zinc-400 hover:text-mc-green hover:bg-zinc-800 rounded-lg transition-all hover:rotate-180 duration-300" className="p-2 text-zinc-400 hover:text-mc-green hover:bg-zinc-800 rounded-lg transition-all hover:rotate-180 duration-300"
title="새로고침"
> >
<RefreshCw size={16} /> <RefreshCw size={16} />
</button> </button>
</Tooltip>
</div> </div>
{/* 필터 드롭다운 */} {/* 필터 드롭다운 */}
@ -1416,13 +1418,15 @@ export default function Admin({ isMobile = false }) {
</p> </p>
</div> </div>
<div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity"> <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<Tooltip content="삭제">
<button <button
onClick={(e) => deleteLogFile(file, e)} onClick={(e) => deleteLogFile(file, e)}
className="p-2 text-zinc-400 hover:text-red-500 hover:bg-zinc-700 rounded-lg transition-colors" className="p-2 text-zinc-400 hover:text-red-500 hover:bg-zinc-700 rounded-lg transition-colors"
title="삭제"
> >
<Trash2 size={16} /> <Trash2 size={16} />
</button> </button>
</Tooltip>
<Tooltip content="다운로드">
<button <button
onClick={async (e) => { onClick={async (e) => {
e.stopPropagation(); e.stopPropagation();
@ -1441,10 +1445,10 @@ export default function Admin({ isMobile = false }) {
} }
}} }}
className="p-2 text-zinc-400 hover:text-white hover:bg-zinc-700 rounded-lg transition-colors" className="p-2 text-zinc-400 hover:text-white hover:bg-zinc-700 rounded-lg transition-colors"
title="다운로드"
> >
<Download size={16} /> <Download size={16} />
</button> </button>
</Tooltip>
</div> </div>
</div> </div>
)) ))
@ -1540,13 +1544,14 @@ export default function Admin({ isMobile = false }) {
key={player.uuid} key={player.uuid}
className="bg-zinc-800/50 rounded-xl p-3 text-center group hover:bg-zinc-800 transition-colors relative" className="bg-zinc-800/50 rounded-xl p-3 text-center group hover:bg-zinc-800 transition-colors relative"
> >
<Tooltip content="제거">
<button <button
onClick={() => setWhitelistRemoveTarget(player)} onClick={() => setWhitelistRemoveTarget(player)}
className="absolute top-2 right-2 p-1 text-zinc-500 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-all" className="absolute top-2 right-2 p-1 text-zinc-500 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-all"
title="제거"
> >
<X size={14} /> <X size={14} />
</button> </button>
</Tooltip>
<CachedSkin <CachedSkin
uuid={player.uuid} uuid={player.uuid}
name={player.name} name={player.name}
@ -1599,6 +1604,7 @@ export default function Admin({ isMobile = false }) {
{/* 액션 버튼 - mt-2 추가 */} {/* 액션 버튼 - mt-2 추가 */}
<div className="flex justify-center gap-1 mt-2"> <div className="flex justify-center gap-1 mt-2">
<Tooltip content="OP">
<button <button
onClick={() => { onClick={() => {
setSelectedPlayer(player); setSelectedPlayer(player);
@ -1610,11 +1616,12 @@ export default function Admin({ isMobile = false }) {
? 'bg-yellow-500/20 text-yellow-500 hover:bg-yellow-500/30' ? 'bg-yellow-500/20 text-yellow-500 hover:bg-yellow-500/30'
: 'bg-zinc-800 text-zinc-400 hover:text-yellow-500' : 'bg-zinc-800 text-zinc-400 hover:text-yellow-500'
}`} }`}
title="OP"
> >
<Crown size={16} /> <Crown size={16} />
</button> </button>
</Tooltip>
{player.isOnline && ( {player.isOnline && (
<Tooltip content="킥">
<button <button
onClick={() => { onClick={() => {
setSelectedPlayer(player); setSelectedPlayer(player);
@ -1622,12 +1629,13 @@ export default function Admin({ isMobile = false }) {
setShowPlayerDialog(true); setShowPlayerDialog(true);
}} }}
className="p-2 bg-zinc-800 text-zinc-400 hover:text-orange-500 rounded-lg transition-colors" className="p-2 bg-zinc-800 text-zinc-400 hover:text-orange-500 rounded-lg transition-colors"
title="킥"
> >
<UserX size={16} /> <UserX size={16} />
</button> </button>
</Tooltip>
)} )}
{player.isBanned ? ( {player.isBanned ? (
<Tooltip content="차단 해제">
<button <button
onClick={() => { onClick={() => {
setSelectedPlayer(player); setSelectedPlayer(player);
@ -1635,11 +1643,12 @@ export default function Admin({ isMobile = false }) {
setShowPlayerDialog(true); setShowPlayerDialog(true);
}} }}
className="p-2 bg-green-500/20 text-green-500 hover:bg-green-500/30 rounded-lg transition-colors" className="p-2 bg-green-500/20 text-green-500 hover:bg-green-500/30 rounded-lg transition-colors"
title="차단 해제"
> >
<Check size={16} /> <Check size={16} />
</button> </button>
</Tooltip>
) : ( ) : (
<Tooltip content="밴">
<button <button
onClick={() => { onClick={() => {
setSelectedPlayer(player); setSelectedPlayer(player);
@ -1647,10 +1656,10 @@ export default function Admin({ isMobile = false }) {
setShowPlayerDialog(true); setShowPlayerDialog(true);
}} }}
className="p-2 bg-zinc-800 text-zinc-400 hover:text-red-500 rounded-lg transition-colors" className="p-2 bg-zinc-800 text-zinc-400 hover:text-red-500 rounded-lg transition-colors"
title="밴"
> >
<Ban size={16} /> <Ban size={16} />
</button> </button>
</Tooltip>
)} )}
</div> </div>
</div> </div>
@ -2098,6 +2107,7 @@ export default function Admin({ isMobile = false }) {
</div> </div>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Tooltip content="수정">
<button <button
onClick={() => { onClick={() => {
setModpackDialogMode('edit'); setModpackDialogMode('edit');
@ -2106,17 +2116,18 @@ export default function Admin({ isMobile = false }) {
setShowModpackDialog(true); setShowModpackDialog(true);
}} }}
className="p-2 text-zinc-400 hover:text-blue-400 transition-colors" className="p-2 text-zinc-400 hover:text-blue-400 transition-colors"
title="수정"
> >
<Pencil size={16} /> <Pencil size={16} />
</button> </button>
</Tooltip>
<Tooltip content="삭제">
<button <button
onClick={() => setModpackDeleteTarget(pack)} onClick={() => setModpackDeleteTarget(pack)}
className="p-2 text-zinc-400 hover:text-red-400 transition-colors" className="p-2 text-zinc-400 hover:text-red-400 transition-colors"
title="삭제"
> >
<Trash2 size={16} /> <Trash2 size={16} />
</button> </button>
</Tooltip>
</div> </div>
</div> </div>
) )

View file

@ -4,6 +4,7 @@ import { Activity, Skull, Heart, LocateFixed, Box, Sword, Clock, Calendar, Refre
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { io } from 'socket.io-client'; import { io } from 'socket.io-client';
import { formatDate, formatPlayTimeMs } from '../utils/formatters'; import { formatDate, formatPlayTimeMs } from '../utils/formatters';
import Tooltip from '../components/Tooltip';
// 3D ( / ) // 3D ( / )
@ -405,9 +406,11 @@ const ItemStatRow = ({ item, translate, icons }) => {
</div> </div>
)} )}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="text-white text-sm font-medium truncate" title={translate(item.id)}> <Tooltip content={translate(item.id)}>
<p className="text-white text-sm font-medium truncate">
{translate(item.id)} {translate(item.id)}
</p> </p>
</Tooltip>
<div className="flex flex-wrap gap-x-2 text-xs text-gray-400"> <div className="flex flex-wrap gap-x-2 text-xs text-gray-400">
{item.mined > 0 && <span>채굴 <span className="text-yellow-400">{item.mined}</span></span>} {item.mined > 0 && <span>채굴 <span className="text-yellow-400">{item.mined}</span></span>}
{item.used > 0 && <span>사용 <span className="text-blue-400">{item.used}</span></span>} {item.used > 0 && <span>사용 <span className="text-blue-400">{item.used}</span></span>}
@ -458,9 +461,11 @@ const MobStatRow = ({ mob, translate, icons }) => {
</div> </div>
)} )}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="text-white text-sm font-medium truncate" title={translate(mob.id)}> <Tooltip content={translate(mob.id)}>
<p className="text-white text-sm font-medium truncate">
{translate(mob.id)} {translate(mob.id)}
</p> </p>
</Tooltip>
<div className="flex flex-wrap gap-x-2 text-xs text-gray-400"> <div className="flex flex-wrap gap-x-2 text-xs text-gray-400">
{mob.killed > 0 && <span>처치 <span className="text-red-400">{mob.killed}</span></span>} {mob.killed > 0 && <span>처치 <span className="text-red-400">{mob.killed}</span></span>}
{mob.killedBy > 0 && <span>죽음 <span className="text-gray-300">{mob.killedBy}</span></span>} {mob.killedBy > 0 && <span>죽음 <span className="text-gray-300">{mob.killedBy}</span></span>}

View file

@ -433,7 +433,9 @@ const ServerDetail = ({ isMobile = false }) => {
<ul className="grid grid-cols-1 md:grid-cols-2 gap-2"> <ul className="grid grid-cols-1 md:grid-cols-2 gap-2">
{server.mods.map((mod, index) => ( {server.mods.map((mod, index) => (
<li key={index} className="text-sm flex items-center justify-between p-3 bg-black/20 rounded-lg hover:bg-black/30 transition-colors"> <li key={index} className="text-sm flex items-center justify-between p-3 bg-black/20 rounded-lg hover:bg-black/30 transition-colors">
<span className="truncate font-medium text-gray-300" title={mod.id}>{mod.id}</span> <Tooltip content={mod.id}>
<span className="truncate font-medium text-gray-300">{mod.id}</span>
</Tooltip>
<span className="text-xs text-gray-500 font-mono ml-2 px-2 py-0.5 bg-white/5 rounded">{mod.version}</span> <span className="text-xs text-gray-500 font-mono ml-2 px-2 py-0.5 bg-white/5 rounded">{mod.version}</span>
</li> </li>
))} ))}
@ -457,7 +459,9 @@ const ServerDetail = ({ isMobile = false }) => {
<ul className="grid grid-cols-1 md:grid-cols-2 gap-2"> <ul className="grid grid-cols-1 md:grid-cols-2 gap-2">
{server.mods.map((mod, index) => ( {server.mods.map((mod, index) => (
<li key={index} className="text-sm flex items-center justify-between p-3 bg-black/20 rounded-lg hover:bg-black/30 transition-colors"> <li key={index} className="text-sm flex items-center justify-between p-3 bg-black/20 rounded-lg hover:bg-black/30 transition-colors">
<span className="truncate font-medium text-gray-300" title={mod.id}>{mod.id}</span> <Tooltip content={mod.id}>
<span className="truncate font-medium text-gray-300">{mod.id}</span>
</Tooltip>
<span className="text-xs text-gray-500 font-mono ml-2 px-2 py-0.5 bg-white/5 rounded">{mod.version}</span> <span className="text-xs text-gray-500 font-mono ml-2 px-2 py-0.5 bg-white/5 rounded">{mod.version}</span>
</li> </li>
))} ))}