반응형 처리
This commit is contained in:
@@ -110,12 +110,12 @@ function DashboardPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="p-2 sm:p-4 md:p-6">
|
||||
<div className="flex flex-col gap-1">
|
||||
{/* 첫번째 라인 */}
|
||||
<div className="grid grid-cols-8 gap-1">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-8 gap-1">
|
||||
{/* 첫번쨰 열 */}
|
||||
<div className="col-span-2 h-250 !p-0">
|
||||
<div className="col-span-1 md:col-span-1 lg:col-span-1 xl:col-span-2 h-250 !p-0">
|
||||
{/* 전국 하수처리시설 개소수 및 가동률 */}
|
||||
<FoFcltCntRt />
|
||||
|
||||
@@ -127,7 +127,7 @@ function DashboardPage() {
|
||||
</div>
|
||||
|
||||
{/* 두번쨰 열 */}
|
||||
<div className="col-span-2 h-250 !p-0">
|
||||
<div className="col-span-1 md:col-span-1 lg:col-span-1 xl:col-span-2 h-250 !p-0">
|
||||
{/* 수계별 유입ㆍ방류수질 분석 */}
|
||||
<FoWqltInflEffl />
|
||||
|
||||
@@ -139,14 +139,16 @@ function DashboardPage() {
|
||||
</div>
|
||||
|
||||
{/* 지도영역 */}
|
||||
<KoreaBasinMap />
|
||||
<div className="col-span-1 md:col-span-2 lg:col-span-1 xl:col-span-3">
|
||||
<KoreaBasinMap />
|
||||
</div>
|
||||
|
||||
{/* 사용자 정보 카드 */}
|
||||
<div className="col-span-1 !p-0">
|
||||
<div className="col-span-1 md:col-span-2 lg:col-span-1 xl:col-span-1 !p-0">
|
||||
{error && <div className="mb-4 text-red-500">{error}</div>}
|
||||
{userInfo ? (
|
||||
<div className="card card_border login_info_box" style={{ height: "150px" }}>
|
||||
<div className="login_header">
|
||||
<div className="card card_border login_info_box" style={{ minHeight: "150px", height: "auto" }}>
|
||||
<div className="login_header" style={{ paddingLeft: "10px", paddingRight: "10px" }}>
|
||||
<div className="login_avatar">
|
||||
<img src="/images/icon_loginID.png" alt="프로필" onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
@@ -156,15 +158,15 @@ function DashboardPage() {
|
||||
<div className="login_user_info">
|
||||
<div className="user_name">{userInfo.userName || "-"}</div>
|
||||
<div className="user_status">온라인</div>
|
||||
<div className="user_status">
|
||||
<div className="user_status text-xs sm:text-sm">
|
||||
<b>2FA 활성화:</b>{" "}
|
||||
{userInfo.twoFactorEnabled ? "예" : "아니오"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="login_actions">
|
||||
<button className="login_btn" onClick={handleLogout}>로그아웃</button>
|
||||
<button className="signup_btn" onClick={handleChangePassword}>마이페이지</button>
|
||||
<div className="login_actions" style={{ paddingLeft: "10px", paddingRight: "10px" }}>
|
||||
<button className="login_btn text-xs sm:text-sm" onClick={handleLogout}>로그아웃</button>
|
||||
<button className="signup_btn text-xs sm:text-sm" onClick={handleChangePassword}>마이페이지</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -172,7 +174,7 @@ function DashboardPage() {
|
||||
)}
|
||||
<div className="card card-border" style={{ height: "300px" }}>
|
||||
<div className="card-header text-foreground">
|
||||
<h3 className="text-2xl font-medium">일정 알림</h3>
|
||||
<h3 className="text-lg sm:text-xl md:text-2xl font-medium">일정 알림</h3>
|
||||
</div>
|
||||
<div className="card paddingnone">
|
||||
|
||||
@@ -180,7 +182,7 @@ function DashboardPage() {
|
||||
</div>
|
||||
<div className="card card-border" style={{ height: "300px" }}>
|
||||
<div className="card-header text-foreground">
|
||||
<h3 className="text-2xl font-medium">내 작성글</h3>
|
||||
<h3 className="text-lg sm:text-xl md:text-2xl font-medium">내 작성글</h3>
|
||||
</div>
|
||||
<div className="card paddingnone">
|
||||
|
||||
@@ -190,7 +192,7 @@ function DashboardPage() {
|
||||
</div>
|
||||
|
||||
{/* 두번째 라인 */}
|
||||
<div className="grid grid-cols-4 gap-1">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-1">
|
||||
<div className="card card-border" style={{ height: "250px" }}>
|
||||
<SupportList />
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,7 @@ const isLoggedIn = !!userId;
|
||||
export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [logoWidth, setLogoWidth] = useState(0);
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const logoRef = useRef<HTMLImageElement>(null);
|
||||
|
||||
const { data: userInfo } = useQuery({
|
||||
@@ -146,65 +147,126 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-30 box-border flex h-[6rem] 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 flex-col items-center gap-2">
|
||||
{mounted ? (
|
||||
<img
|
||||
ref={logoRef}
|
||||
src={getLogoImage()}
|
||||
className="h-[3rem]"
|
||||
alt="logo"
|
||||
onLoad={() => {
|
||||
if (logoRef.current) {
|
||||
setLogoWidth(logoRef.current.offsetWidth);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Skeleton className="h-[3rem] w-[15rem]" />
|
||||
)}
|
||||
<header className="sticky top-0 z-30 box-border flex flex-col sm:flex-row h-auto sm:h-[6rem] w-full items-center justify-between border-b border-solid border-border bg-background">
|
||||
<div className="flex items-center px-2 sm:px-4 md:px-6 w-full sm:w-auto justify-between sm:justify-start">
|
||||
<div className="flex items-center gap-2 sm:gap-4">
|
||||
<SidebarTrigger className="size-8 sm:size-10 md:size-12 !bg-transparent p-2 hover:!bg-accent [&>svg]:!size-full [&>svg]:text-foreground" />
|
||||
<h1 className="flex flex-col items-center gap-1 sm:gap-2">
|
||||
{mounted ? (
|
||||
<img
|
||||
ref={logoRef}
|
||||
src={getLogoImage()}
|
||||
className="h-[2rem] sm:h-[2.5rem] md:h-[3rem]"
|
||||
alt="logo"
|
||||
onLoad={() => {
|
||||
if (logoRef.current) {
|
||||
setLogoWidth(logoRef.current.offsetWidth);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Skeleton className="h-[2rem] sm:h-[2.5rem] md:h-[3rem] w-[10rem] sm:w-[12rem] md:w-[15rem]" />
|
||||
)}
|
||||
<span
|
||||
className="text-[1.2rem] font-semibold text-foreground"
|
||||
style={{
|
||||
letterSpacing: logoWidth > 0 ? `${(logoWidth - 120) / (siteInfo?.siteNm?.length || 1)}px` : '0.05em'
|
||||
}}
|
||||
className="text-xs sm:text-sm md:text-[1.2rem] font-semibold text-foreground"
|
||||
style={{
|
||||
letterSpacing: logoWidth > 0 ? `${(logoWidth - 120) / (siteInfo?.siteNm?.length || 1)}px` : '0.05em'
|
||||
}}
|
||||
>
|
||||
{siteInfo?.siteNm}
|
||||
</span>
|
||||
</h1>
|
||||
{siteInfo?.siteNm}
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
{/* 모바일에서 오른쪽 요소들 */}
|
||||
<div className="flex items-center gap-1 sm:hidden">
|
||||
{/* 모바일 메뉴 토글 버튼 */}
|
||||
{mounted && (
|
||||
<Button
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
className="m-0 flex h-[2.5rem] items-center !bg-background px-2 py-0 text-label"
|
||||
>
|
||||
<i className={`diveicon ${isMobileMenuOpen ? 'di-close' : 'di-menu'} static text-xl`} />
|
||||
</Button>
|
||||
)}
|
||||
{mounted && isLoggedIn && (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button className="group m-0 flex h-[2.5rem] items-center !bg-background px-2 py-0 text-label">
|
||||
<i className="diveicon di-account-circle static translate-y-0.5 pr-1 text-xl" />
|
||||
<span className="text-xs hidden xs:inline">{userInfo?.userName}</span>
|
||||
<i className="diveicon di-chevron-down static p-0 transition-transform group-data-[state=open]:rotate-180" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end" className="w-48 border-border p-1">
|
||||
<ul>
|
||||
<li className="cursor-pointer rounded-sm p-3 text-xl hover:bg-accent">
|
||||
<NoticePopover />
|
||||
</li>
|
||||
<li
|
||||
onClick={logout}
|
||||
className="cursor-pointer rounded-sm p-3 text-xl hover:bg-accent"
|
||||
>
|
||||
로그아웃
|
||||
</li>
|
||||
</ul>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
{/* 모바일에서 네비게이션 (토글 시 표시) */}
|
||||
{isMobileMenuOpen && (
|
||||
<div className="w-full sm:hidden border-t border-border bg-background py-2">
|
||||
{mounted && userSiteList?.content && userSiteList.content.length > 0 && (
|
||||
<nav className="flex flex-col items-center gap-2 px-4">
|
||||
{userSiteList.content.map((site) => (
|
||||
<a
|
||||
key={site.siteId}
|
||||
href={site.dmnAddr}
|
||||
className="text-base font-medium text-foreground hover:text-primary transition-colors w-full text-center py-2"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
>
|
||||
{site.siteNm}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* 데스크톱에서 중간 네비게이션 */}
|
||||
<div className="hidden sm:flex items-center flex-1 justify-center px-2">
|
||||
{mounted && userSiteList?.content && userSiteList.content.length > 0 && (
|
||||
<nav className="flex items-center gap-6">
|
||||
<nav className="flex items-center gap-3 md:gap-6 flex-wrap justify-center">
|
||||
{userSiteList.content.map((site, index) => (
|
||||
<div key={site.siteId} className="flex items-center">
|
||||
<a
|
||||
href={site.dmnAddr}
|
||||
className="text-2xl font-medium text-foreground hover:text-primary transition-colors header-nav-link"
|
||||
className="text-sm md:text-lg lg:text-2xl font-medium text-foreground hover:text-primary transition-colors header-nav-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{site.siteNm}
|
||||
</a>
|
||||
{index < userSiteList.content.length - 1 && (
|
||||
<div className="w-px h-6 bg-border ml-6"></div>
|
||||
<div className="w-px h-4 md:h-6 bg-border ml-3 md:ml-6"></div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 데스크톱에서 오른쪽 요소들 */}
|
||||
<div className="hidden sm:flex items-center gap-2 px-2 md:px-4">
|
||||
<SearchForm />
|
||||
{mounted ? (
|
||||
isLoggedIn && (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button className="group m-0 flex h-[3rem] items-center !bg-background px-4 py-0 text-label">
|
||||
<i className="diveicon di-account-circle static translate-y-0.5 pr-2 text-3xl" />
|
||||
{userInfo?.userName}
|
||||
<Button className="group m-0 flex h-[3rem] items-center !bg-background px-2 md:px-4 py-0 text-label">
|
||||
<i className="diveicon di-account-circle static translate-y-0.5 pr-1 md:pr-2 text-2xl md:text-3xl" />
|
||||
<span className="text-sm md:text-base hidden md:inline">{userInfo?.userName}</span>
|
||||
<i className="diveicon di-chevron-down static p-0 transition-transform group-data-[state=open]:rotate-180" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
|
||||
Reference in New Issue
Block a user