diff --git a/packages/common/components/src/ui/header/header.css b/packages/common/components/src/ui/header/header.css index a83a7bd38..cd7f1a54e 100644 --- a/packages/common/components/src/ui/header/header.css +++ b/packages/common/components/src/ui/header/header.css @@ -1,27 +1,42 @@ @tailwind components; @layer components { - /* 상단 메뉴 호버 시 하단 라인 - 기본 테마 */ .header-nav-link { position: relative; } - .header-nav-link::after { - content: ''; + /* .header-nav-link::before { + content: ""; position: absolute; bottom: -4px; left: 0; width: 0; height: 2px; - background-color: #063A74; + background-color: #063a74; transition: width 0.3s ease; } - .header-nav-link:hover::after { + .header-nav-link:hover::before { width: 100%; + } */ + + /* 네비게이션 아이템 사이 구분선 */ + .header-nav-link::after { + content: ""; + position: absolute; + left: -0.75rem; + top: 50%; + transform: translateY(-50%); + width: 1px !important; + height: 1.5rem !important; + background-color: var(--border); } - + + .header-nav-link:first-child::after { + display: none; + } + [data-theme="blue"] { header { background: #00afe5; @@ -35,7 +50,7 @@ color: #fff; } header button[role="button"].primary { - background-color: #008E8D !important; + background-color: #008e8d !important; color: #fff !important; } nav a, @@ -45,7 +60,7 @@ .header-nav-link:hover { color: #fff !important; } - .header-nav-link::after { + .header-nav-link::before { background-color: #fff; } } @@ -63,7 +78,7 @@ color: #fff; } header button[role="button"].primary { - background-color: #008E8D !important; + background-color: #008e8d !important; color: #fff !important; } nav a, @@ -73,9 +88,8 @@ .header-nav-link:hover { color: #fff !important; } - .header-nav-link::after { + .header-nav-link::before { background-color: #fff; } } - } diff --git a/packages/common/components/src/ui/header/index.tsx b/packages/common/components/src/ui/header/index.tsx index b9f7fa628..69e2a7196 100644 --- a/packages/common/components/src/ui/header/index.tsx +++ b/packages/common/components/src/ui/header/index.tsx @@ -21,6 +21,7 @@ import { SidebarTrigger } from "../sidebar"; import { Popover, PopoverContent, PopoverTrigger } from "../popover"; import { SearchForm } from "./search-form"; import { NoticePopover } from "./notice-popover"; +import { ScrollArea, ScrollBar } from "../scroll-area"; const appConfig = getConfig(); const userId = getCookie(appConfig.AUTH_USER_ID ?? ""); @@ -71,7 +72,7 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { method: "POST", credentials: "include", // ⭐ 쿠키 포함 중요! }); - + if (!response.ok) { console.error("Logout API failed"); // API 실패해도 계속 진행 @@ -82,42 +83,42 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { } finally { // 2. 클라이언트 쿠키 삭제 (백업) clearClientCookies(); - + // 3. 로컬/세션 스토리지 정리 if (typeof window !== "undefined") { localStorage.clear(); sessionStorage.clear(); } - + // 4. 로그인 페이지로 리다이렉트 window.location.href = "/auth/login"; } }; - + // 쿠키 삭제 헬퍼 함수 const clearClientCookies = () => { const actualHost = window.location.hostname; - + // 루트 도메인 추출 const getRootDomain = (hostname: string): string => { if (hostname === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) { return hostname; } - + if (hostname.includes("hasuon.or.kr")) { return ".hasuon.or.kr"; } - + const parts = hostname.split("."); if (parts.length >= 2) { return `.${parts.slice(-2).join(".")}`; } - + return hostname; }; - + const domain = getRootDomain(actualHost); - + // 삭제할 쿠키 목록 const cookiesToDelete = [ appConfig.AUTH_USER_ID, @@ -127,26 +128,26 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { "site-id-key", "site-theme-key", ].filter(Boolean); // undefined 제거 - + // 여러 방법으로 쿠키 삭제 시도 cookiesToDelete.forEach((cookieName) => { if (!cookieName) return; - + // 방법 1: removeCookie 함수 사용 if (domain === "localhost") { removeCookie(cookieName, {}); } else { removeCookie(cookieName, { domain }); } - + // 방법 2: 직접 document.cookie 설정 (도메인 포함) if (domain !== "localhost") { document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain}`; } - + // 방법 3: 도메인 없이 삭제 (현재 도메인만) document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; - + // 방법 4: Secure 속성 포함 삭제 if (window.location.protocol === "https:") { if (domain !== "localhost") { @@ -156,66 +157,7 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { } }); }; - /* - const logout = async () => { - try { - const response = await fetch("/api/auth/logout", { - method: "POST", - }); - if (!response.ok) throw new Error("Failed to logout"); - const actualHost = window.location.hostname; - - // 서브도메인 쿠키 삭제를 위한 루트 도메인 추출 - const getRootDomain = (hostname: string): string => { - // localhost나 IP 주소인 경우 - if (hostname === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) { - return hostname; - } - - // hasuon.or.kr 도메인인 경우 - if (hostname.includes("hasuon.or.kr")) { - return ".hasuon.or.kr"; - } - - // 일반적인 도메인의 경우 루트 도메인 추출 (예: sub.example.com → .example.com) - const parts = hostname.split("."); - if (parts.length >= 2) { - // 마지막 두 부분을 사용 (예: example.com) - return `.${parts.slice(-2).join(".")}`; - } - - return hostname; - }; - - const domain = getRootDomain(actualHost); - const cookieOptions = domain.startsWith(".") || domain === "localhost" - ? { domain } - : {}; - - // 쿠키 삭제 (서브도메인 포함) - if (appConfig.AUTH_USER_ID) { - removeCookie(appConfig.AUTH_USER_ID, cookieOptions); - } - if (appConfig.REFRESH_TOKEN) { - removeCookie(appConfig.REFRESH_TOKEN, cookieOptions); - } - if (appConfig.ACCESS_TOKEN) { - removeCookie(appConfig.ACCESS_TOKEN, cookieOptions); - } - if (appConfig.CLAIM_NAME) { - removeCookie(appConfig.CLAIM_NAME, cookieOptions); - } - - removeCookie("site-id-key", cookieOptions); - removeCookie("site-theme-key", cookieOptions); - - window.location.href = "/auth/login"; - } catch (error) { - console.error(error); - } - }; -*/ const openPopup = useCallback(async (popup: Popup) => { try { const cookieName = `cs-popup-hide-for-day-${popup.popupSn}`; @@ -268,8 +210,8 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { }, []); return ( -
-
+
+

@@ -291,7 +233,10 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { 0 ? `${(logoWidth - 120) / (siteInfo?.siteNm?.length || 1)}px` : '0.05em' + letterSpacing: + logoWidth > 0 + ? `${(logoWidth - 120) / (siteInfo?.siteNm?.length || 1)}px` + : "0.05em", }} > {siteInfo?.siteNm} @@ -306,7 +251,9 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} className="m-0 flex h-[3rem] items-center !bg-background px-3 py-0 text-label" > - + )} {mounted && isLoggedIn && ( @@ -314,7 +261,9 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { @@ -337,57 +286,60 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {

{/* 모바일에서 네비게이션 (토글 시 표시) */} {isMobileMenuOpen && ( -
- {mounted && userSiteList?.content && userSiteList.content.length > 0 && ( -
- )} - {/* 데스크톱에서 중간 네비게이션 */} -
- {mounted && userSiteList?.content && userSiteList.content.length > 0 && ( - - )} -
+ + {/* 데스크톱에서 오른쪽 요소들 */} -
+
{mounted ? ( isLoggedIn && ( -