This commit is contained in:
@@ -20,7 +20,12 @@ import {
|
||||
Building2,
|
||||
FileCheck,
|
||||
Monitor,
|
||||
Plus,
|
||||
Edit3,
|
||||
} from "lucide-react";
|
||||
import { useDashboardStore } from "@/stores/dashboardStore";
|
||||
import { insertDashboard } from "@/lib/api/dashMenu";
|
||||
import { CreateDashboardModal } from "@/components/dash/CreateDashboardModal";
|
||||
import { useMenu } from "@/contexts/MenuContext";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
import { useProfile } from "@/hooks/useProfile";
|
||||
@@ -29,13 +34,13 @@ import { menuScreenApi } from "@/lib/api/screen";
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
import { toast } from "sonner";
|
||||
import { ProfileModal } from "./ProfileModal";
|
||||
import { SettingsModal } from "./SettingsModal";
|
||||
import { Logo } from "./Logo";
|
||||
import { SideMenu } from "./SideMenu";
|
||||
import { TabBar } from "./TabBar";
|
||||
import { TabContent } from "./TabContent";
|
||||
import { useTabStore } from "@/stores/tabStore";
|
||||
import { ThemeToggle } from "./ThemeToggle";
|
||||
import { CosmicBackground } from "./CosmicBackground";
|
||||
import { useTheme } from "next-themes";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -246,8 +251,35 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
const [showCompanySwitcher, setShowCompanySwitcher] = useState(false);
|
||||
const [currentCompanyName, setCurrentCompanyName] = useState<string>("");
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const { theme, setTheme: rawSetTheme } = useTheme();
|
||||
|
||||
// 대시보드 생성/편집 모드 / 템플릿 추가 (전역 헤더 버튼)
|
||||
const dashEditMode = useDashboardStore((s) => s.editMode);
|
||||
const toggleDashEditMode = useDashboardStore((s) => s.toggleEditMode);
|
||||
const dashCreateOpen = useDashboardStore((s) => s.createOpen);
|
||||
const openDashCreate = useDashboardStore((s) => s.openCreate);
|
||||
const closeDashCreate = useDashboardStore((s) => s.closeCreate);
|
||||
const dashActiveId = useDashboardStore((s) => s.activeDashboardId);
|
||||
const openDashLib = useDashboardStore((s) => s.openLib);
|
||||
const [dashCreateSubmitting, setDashCreateSubmitting] = useState(false);
|
||||
|
||||
const handleDashCreateSubmit = useCallback(async (payload: { name: string; icon: string; is_personal: boolean }) => {
|
||||
setDashCreateSubmitting(true);
|
||||
try {
|
||||
const result = await insertDashboard(payload);
|
||||
try { await refreshMenus(); } catch { /* ignore */ }
|
||||
const newUrl = result?.menu_url ?? result?.MENU_URL;
|
||||
closeDashCreate();
|
||||
toast.success(`"${payload.name}" 대시보드를 만들었습니다`);
|
||||
if (newUrl) router.push(newUrl);
|
||||
} catch (err) {
|
||||
toast.error("대시보드 생성 실패");
|
||||
} finally {
|
||||
setDashCreateSubmitting(false);
|
||||
}
|
||||
}, [refreshMenus, closeDashCreate, router]);
|
||||
|
||||
// 테마 전환 — 클릭 위치에서 원형으로 새 테마가 reveal (View Transitions API)
|
||||
const setNextTheme = useCallback((t: "light" | "dark", e?: React.MouseEvent) => {
|
||||
if (theme === t) return;
|
||||
@@ -435,10 +467,11 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 대시보드 할당 (/dashboard/xxx) → admin 탭으로 렌더링 (AdminPageRenderer가 처리)
|
||||
if (menu.url && menu.url.startsWith("/dashboard/")) {
|
||||
console.log("[handleMenuClick] → 대시보드 탭:", menu.url);
|
||||
openTab({ type: "admin", title: menuName, admin_url: menu.url });
|
||||
// 3) 대시보드 (사용자 메뉴) → 자동 부여된 /숫자 URL 로 이동
|
||||
// - 회사별 시퀀스가 URL 그 자체. "/{seq}" 형태만 대시보드로 인식
|
||||
if (menu.url && /^\/\d+$/.test(menu.url)) {
|
||||
console.log("[handleMenuClick] → 대시보드 페이지:", menu.url);
|
||||
router.push(menu.url);
|
||||
if (isMobile) setSidebarOpen(false);
|
||||
return;
|
||||
}
|
||||
@@ -777,9 +810,6 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Cosmic background */}
|
||||
<CosmicBackground />
|
||||
|
||||
{/* Theme fade overlay */}
|
||||
<div className="v5-theme-fade" id="v5-theme-fade" />
|
||||
|
||||
@@ -804,6 +834,34 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
{/* mode transition 헤더 glow 라인 — 평소엔 opacity 0, mode change 시에만 flash */}
|
||||
<div className="v5-hdr-glow" />
|
||||
<div className="v5-hdr-r">
|
||||
{/* 대시보드 생성 + 템플릿 추가 + 편집 모드 (Light/Dark 토글 왼쪽) */}
|
||||
<button
|
||||
className="v5-dash-btn"
|
||||
onClick={openDashCreate}
|
||||
title="새 대시보드 만들기"
|
||||
>
|
||||
<Plus size={13} />
|
||||
<span>대시보드</span>
|
||||
</button>
|
||||
<button
|
||||
className="v5-dash-btn"
|
||||
onClick={openDashLib}
|
||||
disabled={!dashActiveId}
|
||||
title={dashActiveId ? "템플릿 라이브러리에서 카드 추가" : "대시보드를 먼저 선택하세요"}
|
||||
>
|
||||
<Plus size={13} />
|
||||
<span>템플릿 추가</span>
|
||||
</button>
|
||||
<button
|
||||
className={`v5-dash-btn${dashEditMode ? " on" : ""}`}
|
||||
onClick={toggleDashEditMode}
|
||||
disabled={!dashActiveId}
|
||||
title={dashActiveId ? (dashEditMode ? "편집 모드 끄기" : "편집 모드 켜기") : "대시보드 화면에서만 사용할 수 있습니다"}
|
||||
>
|
||||
<Edit3 size={13} />
|
||||
<span>{dashEditMode ? "편집 중" : "편집"}</span>
|
||||
</button>
|
||||
|
||||
{/* Theme pill */}
|
||||
<div className="v5-pill">
|
||||
<button className={theme !== "dark" ? "on" : ""} onClick={(e) => setNextTheme("light", e)}>Light</button>
|
||||
@@ -862,6 +920,10 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
<User className="mr-2 h-4 w-4" />
|
||||
<span>내 정보</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setSettingsOpen(true)}>
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
<span>설정</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => router.push("/admin/approvalBox")}>
|
||||
<FileCheck className="mr-2 h-4 w-4" />
|
||||
<span>결재함</span>
|
||||
@@ -938,9 +1000,11 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
{/* ★ INVYONE 신규 페이지는 children 직접 렌더, 기존 VEX 페이지는 탭 시스템 */}
|
||||
<main className="v5-content flex min-w-0 flex-1 flex-col overflow-hidden">
|
||||
{pathname && (
|
||||
pathname.startsWith('/dashboard') ||
|
||||
pathname.startsWith('/dash') ||
|
||||
pathname.startsWith('/admin/builder') ||
|
||||
pathname.startsWith('/test-fc')
|
||||
pathname.startsWith('/test-fc') ||
|
||||
/^\/\d+$/.test(pathname) // /숫자 = 신규 대시보드(메뉴 시퀀스) 페이지
|
||||
) ? (
|
||||
// ★ flex 컨테이너로 만들어서 안쪽 dash-shell이 height:100% 잘 먹도록
|
||||
<div className="relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
|
||||
@@ -996,6 +1060,17 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<SettingsModal open={settingsOpen} onOpenChange={setSettingsOpen} />
|
||||
|
||||
{/* 전역 대시보드 생성 모달 — 헤더 "대시보드" 버튼에서 열림 */}
|
||||
<CreateDashboardModal
|
||||
open={dashCreateOpen}
|
||||
onClose={closeDashCreate}
|
||||
onSubmit={handleDashCreateSubmit}
|
||||
defaultName="새 대시보드"
|
||||
submitting={dashCreateSubmitting}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user