Files
vexplor/frontend/components/pop/dashboard/DashboardHeader.tsx
SeongHyun Kim 3933f1e966 feat(pop): PC <-> POP 모드 전환 네비게이션 + POP 기본 화면(Landing) 기능
PC 모드에서 프로필 드롭다운을 통해 POP 화면으로 진입하고, POP에서 PC로
돌아오는 양방향 네비게이션을 구현한다. 기존 메뉴 시스템(menu_info)을 활용하여
POP 화면의 권한 제어와 회사별 관리가 가능하도록 한다.
[백엔드: POP 메뉴 조회 API]
- AdminService.getPopMenuList: L1 POP 메뉴(menu_desc [POP] 또는
  menu_name_kor POP 포함) 하위의 active L2 메뉴 조회
- company_code 필터링 적용 (L1 + L2 모두)
- landingMenu 반환: menu_desc에 [POP_LANDING] 태그가 있는 메뉴
- GET /admin/pop-menus 라우트 추가
[프론트: PC -> POP 진입]
- AppLayout: handlePopModeClick 함수 추가
  - landingMenu 있으면 해당 URL로 바로 이동
  - 없으면 childMenus 수에 따라 단일 화면/대시보드/안내 분기
- UserDropdown: onPopModeClick prop + "POP 모드" 메뉴 항목 추가
- 사이드바 하단 + 모바일 헤더 프로필 드롭다운 2곳 모두 적용
[프론트: POP -> PC 복귀]
- DashboardHeader: "PC 모드" 버튼 추가 (router.push "/")
- POP 개별 화면 page.tsx: 상단 네비게이션 바 추가
  (POP 대시보드 / PC 모드 버튼)
[프론트: POP 대시보드 동적 메뉴]
- PopDashboard: 하드코딩 MENU_ITEMS -> menuApi.getPopMenus() API 조회
- API 실패 시 하드코딩 fallback 유지
[프론트: POP 기본 화면 설정 (MenuFormModal)]
- L2 POP 화면 수정 시 "POP 기본 화면으로 설정" 체크박스 추가
- 체크 시 menu_desc에 [POP_LANDING] 태그 자동 추가/제거
- 회사당 1개만 설정 가능 (다른 메뉴에 이미 설정 시 비활성화)
[API 타입]
- PopMenuItem, PopMenuResponse(landingMenu 포함) 인터페이스 추가
- menuApi.getPopMenus() 함수 추가
2026-03-09 12:16:26 +09:00

110 lines
3.3 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import { Moon, Sun, Monitor } from "lucide-react";
import { WeatherInfo, UserInfo, CompanyInfo } from "./types";
interface DashboardHeaderProps {
theme: "dark" | "light";
weather: WeatherInfo;
user: UserInfo;
company: CompanyInfo;
onThemeToggle: () => void;
onUserClick: () => void;
onPcModeClick?: () => void;
}
export function DashboardHeader({
theme,
weather,
user,
company,
onThemeToggle,
onUserClick,
onPcModeClick,
}: DashboardHeaderProps) {
const [mounted, setMounted] = useState(false);
const [currentTime, setCurrentTime] = useState(new Date());
useEffect(() => {
setMounted(true);
const timer = setInterval(() => {
setCurrentTime(new Date());
}, 1000);
return () => clearInterval(timer);
}, []);
const formatTime = (date: Date) => {
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${hours}:${minutes}:${seconds}`;
};
const formatDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
return (
<header className="pop-dashboard-header">
<div className="pop-dashboard-header-left">
<div className="pop-dashboard-time-display">
<div className="pop-dashboard-time-main">
{mounted ? formatTime(currentTime) : "--:--:--"}
</div>
<div className="pop-dashboard-time-date">
{mounted ? formatDate(currentTime) : "----.--.--"}
</div>
</div>
</div>
<div className="pop-dashboard-header-right">
{/* 테마 토글 */}
<button
className="pop-dashboard-theme-toggle"
onClick={onThemeToggle}
title="테마 변경"
>
{theme === "dark" ? <Moon size={16} /> : <Sun size={16} />}
</button>
{/* 날씨 정보 */}
<div className="pop-dashboard-weather">
<span className="pop-dashboard-weather-temp">{weather.temp}</span>
<span className="pop-dashboard-weather-desc">{weather.description}</span>
</div>
{/* 회사 정보 */}
<div className="pop-dashboard-company">
<div className="pop-dashboard-company-name">{company.name}</div>
<div className="pop-dashboard-company-sub">{company.subTitle}</div>
</div>
{/* PC 모드 복귀 */}
{onPcModeClick && (
<button
className="pop-dashboard-theme-toggle"
onClick={onPcModeClick}
title="PC 모드로 돌아가기"
>
<Monitor size={16} />
</button>
)}
{/* 사용자 배지 */}
<button className="pop-dashboard-user-badge" onClick={onUserClick}>
<div className="pop-dashboard-user-avatar">{user.avatar}</div>
<div className="pop-dashboard-user-text">
<div className="pop-dashboard-user-name">{user.name}</div>
<div className="pop-dashboard-user-role">{user.role}</div>
</div>
</button>
</div>
</header>
);
}