o2o-castad-frontend/src/components/SettingsPanel.tsx

142 lines
5.1 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { UserMeResponse } from '../types/api';
import LanguageSwitch from './LanguageSwitch';
import { NAV } from './navItems';
interface SettingsPanelProps {
variant: 'sidebar' | 'bottom';
onClose: () => void;
onNavigate: (id: string) => void;
activeItem: string;
userInfo?: UserMeResponse | null;
credits?: number | null;
isGuest: boolean;
isLoggingOut?: boolean;
onLogout?: () => void;
onLoginClick: () => void;
/** 팝업을 여는 설정 버튼 영역. 외부 클릭 닫기에서 제외해 재클릭 시 토글이 정상 동작하게 한다 */
anchorRef?: React.RefObject<HTMLElement | null>;
}
const SettingsPanel: React.FC<SettingsPanelProps> = ({
variant,
onClose,
onNavigate,
activeItem,
userInfo,
credits,
isGuest,
isLoggingOut,
onLogout,
onLoginClick,
anchorRef,
}) => {
const { t } = useTranslation();
const panelRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
const target = e.target as Node;
if (anchorRef?.current?.contains(target)) return;
if (panelRef.current && !panelRef.current.contains(target)) {
onClose();
}
};
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('keydown', handleEscape);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleEscape);
};
}, [onClose, anchorRef]);
return (
<div ref={panelRef} className={`settings-panel settings-panel--${variant}`}>
{/* 1) 프로필 — 패널 헤더 역할 */}
{!isGuest && (
<>
<div className="profile-section settings-panel-profile">
{userInfo?.profile_image_url || userInfo?.thumbnail_image_url ? (
<img
src={userInfo.thumbnail_image_url || userInfo.profile_image_url || ''}
alt="Profile"
className="profile-avatar"
/>
) : (
<div className="profile-avatar profile-avatar-default">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>
</div>
)}
<div className="flex-1 min-w-0">
<p className="profile-name">{userInfo?.nickname || t('sidebar.defaultUser')}</p>
{credits !== null && credits !== undefined && (
<p className="profile-credits">{t('sidebar.credits', { count: credits })}</p>
)}
</div>
</div>
</>
)}
{/* 2) 메뉴 그룹 — 대시보드 */}
<button
className={`settings-panel-item ${activeItem === NAV.DASHBOARD ? 'active' : ''}`}
onClick={() => {
onNavigate(NAV.DASHBOARD);
onClose();
}}
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<rect x="3" y="3" width="7" height="9" /><rect x="14" y="3" width="7" height="5" />
<rect x="14" y="12" width="7" height="9" /><rect x="3" y="16" width="7" height="5" />
</svg>
<span>{t('sidebar.dashboard')}</span>
</button>
<div className="settings-panel-divider" />
{/* 3) 하단 그룹 — 언어 전환 / 로그아웃(로그인) / 고객의견 */}
<div className="settings-panel-language">
<LanguageSwitch isCollapsed={false} />
</div>
{isGuest ? (
<button className="settings-panel-item" onClick={onLoginClick}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" /><polyline points="10 17 15 12 10 7" /><line x1="15" y1="12" x2="3" y2="12" />
</svg>
<span>{t('sidebar.login')}</span>
</button>
) : (
<button className="settings-panel-item" onClick={onLogout} disabled={isLoggingOut}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /><polyline points="16 17 21 12 16 7" /><line x1="21" y1="12" x2="9" y2="12" />
</svg>
<span>{isLoggingOut ? t('sidebar.loggingOut') : t('sidebar.logout')}</span>
</button>
)}
<a
href="https://forms.gle/4a8mGebBYtdesvby9"
target="_blank"
rel="noopener noreferrer"
className="settings-panel-item"
title={t('sidebar.inquiry')}
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
</svg>
<span>{t('sidebar.inquiry')}</span>
</a>
</div>
);
};
export default SettingsPanel;