Files
frontend/packages/common/components/src/ui/header/index.tsx
T

175 lines
5.6 KiB
TypeScript

import "./header.css";
import { useEffect, useCallback, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { images } from "@common/assets";
import { getConfig } from "@common/config";
import { ENDPOINT } from "../../config";
import { setCookie, getCookie } from "../../lib";
import {
getPopupInfo,
getUserPopupList,
getSiteInfo,
getUserInfo,
type Popup,
} from "../../api";
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from "../dropdown-menu";
import { Button } from "../button";
import { SidebarTrigger } from "../sidebar";
import { Skeleton } from "../skeleton";
import { SearchForm } from "./search-form";
const appConfig = getConfig();
const siteId = appConfig.SITE_ID ?? "";
const userId = getCookie(appConfig.AUTH_USER_ID ?? "");
const isLoggedIn = !!userId;
export function Header({ viewOnly }: { viewOnly?: boolean }) {
const [mounted, setMounted] = useState(false);
const { data: userInfo } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS],
queryFn: () => getUserInfo(),
enabled: isLoggedIn && !viewOnly,
});
const { data: siteInfo } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SITES, siteId],
queryFn: () => getSiteInfo(siteId),
select: (data) => {
if (data.siteSkn) {
const siteSkin = data.siteSkn.toLowerCase();
setCookie("site-theme-key", siteSkin);
document.documentElement.setAttribute("data-theme", siteSkin);
}
return data;
},
enabled: !viewOnly,
});
const { data: userPopupList } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.POPUPS],
queryFn: () => getUserPopupList(),
enabled: isLoggedIn && !viewOnly,
});
const logout = async () => {
try {
const response = await fetch("/api/auth/logout", {
method: "POST",
});
if (!response.ok) throw new Error("Failed to logout");
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}`;
const hasCookie = getCookie(cookieName);
if (hasCookie) return;
const response = await getPopupInfo(popup.popupSn);
if (!response) return;
const width = response.popupWdth || 400;
const height = response.popupHgt || 300;
const left = (response.popupHrzntltyPstn || 0) + window.screenX;
const top = (response.popupVtclPstn || 0) + window.screenY;
const popupWindow = window.open(
`/view/popup/${popup.popupSn}`,
"",
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,status=no,location=no,menubar=no`,
);
if (popupWindow) {
popupWindow.focus();
}
} catch (error) {
console.error(`Failed to open popup ${popup.popupSn}:`, error);
}
}, []);
const getLogoImage = () => {
const siteThemeKey = getCookie("site-theme-key");
if (siteThemeKey === "blue" || siteThemeKey === "green")
return images.logoWhite;
return images.logo;
};
useEffect(() => {
if (!userPopupList?.length) return;
const timer = setTimeout(() => {
userPopupList.forEach((popup) => {
openPopup(popup);
});
}, 500);
return () => clearTimeout(timer);
}, [userPopupList, openPopup]);
useEffect(() => {
setMounted(true);
}, []);
return (
<header className="sticky top-0 z-30 box-border flex h-[5rem] w-full items-center justify-between border-b border-solid border-border bg-background">
<div className="flex items-center px-6">
<SidebarTrigger className="size-12 !bg-transparent p-2 hover:!bg-accent [&>svg]:!size-full [&>svg]:text-foreground" />
<h1 className="flex items-center gap-4">
{mounted ? (
<img src={getLogoImage()} className="h-[3rem]" alt="logo" />
) : (
<Skeleton className="h-[3rem] w-[15rem]" />
)}
<span className="text-[1.8rem] font-semibold text-foreground">
{siteInfo?.siteNm}
</span>
</h1>
</div>
<div className="flex items-center gap-2">
<SearchForm />
{mounted ? (
isLoggedIn && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
className="group m-0 flex h-[3rem] items-center !bg-background px-4 py-0 text-label"
data-trigger="dropdown"
>
<i className="diveicon di-account-circle static translate-y-0.5 pr-2 text-3xl" />
{userInfo?.userName}
<i className="diveicon di-chevron-down static p-0 transition-transform group-data-[state=open]:rotate-180" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>프로필 설정</DropdownMenuItem>
<div className="flex flex-col items-start gap-0 px-4 py-3">
<p className="text-base text-label">최종접속일</p>
<p>{userInfo?.lastLoginDate}</p>
</div>
<div className="flex flex-col items-start gap-0 px-4 py-3">
<p className="text-base text-label">접속 IP</p>
<p></p>
</div>
<DropdownMenuItem onClick={logout}>로그아웃</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
) : (
<Skeleton className="mx-4 h-[3rem] w-[15rem]" />
)}
</div>
</header>
);
}