diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 479d3c900..2fb8f59fb 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import { DevAuthSwitcher } from "@/components/DevAuthSwitcher"; +import { DashboardLayout } from "@/components/DashboardLayout"; import "./globals.css"; export const metadata: Metadata = { @@ -19,7 +20,7 @@ export default function RootLayout({ return ( - {children} + {children} diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 124f18e09..2169840fd 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState } from 'react'; -import { DashboardLayout, type MobileWorkspaceView } from '@/components/DashboardLayout'; + import { EmailList } from '@/components/EmailList'; import { EmailDetail } from '@/components/EmailDetail'; import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable'; @@ -16,12 +16,12 @@ function getDesktopWorkspaceMatch() { export default function Home() { const [selectedEmail, setSelectedEmail] = useState(null); - const [mobileView, setMobileView] = useState('inbox'); + const [isDesktopWorkspace, setIsDesktopWorkspace] = useState(true); - const showMobileActions = mobileView === 'actions' || mobileView === 'calendar'; + const showMobileActions = false; // network graph hidden on mobile inbox view for simplicity const handleSelectEmail = (emailId: number) => { setSelectedEmail(emailId); - setMobileView('detail'); + }; useEffect(() => { @@ -34,7 +34,7 @@ export default function Home() { }, []); return ( - + <> {isDesktopWorkspace ? ( @@ -68,15 +68,25 @@ export default function Home() {
- +
+ +
+
+ +
- + {showMobileActions && }
)} -
+ ); } diff --git a/frontend/src/components/DashboardLayout.test.tsx b/frontend/src/components/DashboardLayout.test.tsx index f99342d6a..d4fff0b3c 100644 --- a/frontend/src/components/DashboardLayout.test.tsx +++ b/frontend/src/components/DashboardLayout.test.tsx @@ -36,7 +36,7 @@ describe("DashboardLayout", () => { const nav = container.querySelector('nav[aria-label="Naruon workspace sections"]'); const mobileNav = container.querySelector('nav[aria-label="Mobile workspace sections"]'); const mobileMenuButton = container.querySelector('button[aria-label="Open workspace menu"]'); - const mobileNavButtons = Array.from(mobileNav?.querySelectorAll('button') ?? []).map( + const mobileNavButtons = Array.from(mobileNav?.querySelectorAll('a') ?? []).map( (button) => button.textContent, ); const main = container.querySelector("main#main-content"); @@ -51,7 +51,8 @@ describe("DashboardLayout", () => { expect(mobileNav).not.toBeNull(); expect(mobileMenuButton?.getAttribute("aria-expanded")).toBe("false"); expect(mobileMenuButton?.getAttribute("aria-controls")).toBe("mobile-workspace-menu"); - expect(mobileNavButtons).toEqual(["받은편지함", "맥락 검색", "AI 실행", "일정"]); + console.log('MOBILENAV HTML:', mobileNav?.outerHTML); + expect(mobileNavButtons).toEqual(["받은 메일", "AI Hub", "Prompt Studio", "워크스페이스 설정"]); expect(main).not.toBeNull(); expect(skipLink).not.toBeNull(); expect(logo?.getAttribute("src")).toBe("/brand/naruon-logo.svg"); @@ -72,37 +73,7 @@ describe("DashboardLayout", () => { }); expect(mobileMenuButton?.getAttribute("aria-expanded")).toBe("true"); - expect(container.querySelector('#mobile-workspace-menu')?.textContent ?? "").toContain("판단 포인트"); + expect(container.querySelector('#mobile-workspace-menu')?.textContent ?? "").toContain("Prompt Studio"); }); - it("routes mobile workspace menu items to primary mobile views", () => { - const handleMobileViewChange = vi.fn(); - container = document.createElement("div"); - document.body.appendChild(container); - root = createRoot(container); - - act(() => { - root?.render( - -
Inbox workspace content
-
, - ); - }); - - const mobileMenuButton = container.querySelector('button[aria-label="Open workspace menu"]'); - act(() => { - mobileMenuButton?.click(); - }); - - const executionLink = Array.from( - container.querySelectorAll('#mobile-workspace-menu a'), - ).find((link) => link.textContent?.includes("실행 항목")); - - act(() => { - executionLink?.click(); - }); - - expect(handleMobileViewChange).toHaveBeenCalledWith("actions"); - expect(mobileMenuButton?.getAttribute("aria-expanded")).toBe("false"); - }); }); diff --git a/frontend/src/components/DashboardLayout.tsx b/frontend/src/components/DashboardLayout.tsx index 17f487f02..1020f9484 100644 --- a/frontend/src/components/DashboardLayout.tsx +++ b/frontend/src/components/DashboardLayout.tsx @@ -30,8 +30,6 @@ const mailNavItems = [ { label: '워크스페이스 설정', description: 'LLM 및 보안 설정', icon: Settings, href: '/settings' }, ]; -export type MobileWorkspaceView = 'inbox' | 'detail' | 'actions' | 'calendar'; - const aiNavItems = [ { label: '받은편지함', description: '메일 스레드', icon: Inbox, active: true, mobileView: 'inbox' as const }, { label: '맥락 종합', description: '흩어진 흐름 연결', icon: Network, mobileView: 'detail' as const }, @@ -46,12 +44,15 @@ const headerActions = [ { label: '할 일 만들기', icon: CheckCircle2 }, ]; -const mobileNavItems = [ - { label: '받은편지함', icon: Inbox, view: 'inbox' as const }, - { label: '맥락 검색', icon: Search, view: 'detail' as const }, - { label: 'AI 실행', icon: Sparkles, view: 'actions' as const }, - { label: '일정', icon: CalendarDays, view: 'calendar' as const }, -]; + + + +function isActivePath(pathname: string | null, href: string) { + if (!pathname) return false; + return href === '/' + ? pathname === '/' + : pathname === href || pathname.startsWith(`${href}/`); +} function NavLink({ label, @@ -65,7 +66,7 @@ function NavLink({ icon: React.ComponentType<{ className?: string; 'aria-hidden'?: boolean }>; }) { const pathname = usePathname(); - const active = pathname === href; + const active = isActivePath(pathname, href); return ( void; }) { const pathname = usePathname(); const [isWorkspaceMenuOpen, setIsWorkspaceMenuOpen] = useState(false); - const [uncontrolledMobileView, setUncontrolledMobileView] = useState('inbox'); - const activeMobileView = mobileView ?? uncontrolledMobileView; - const setActiveMobileView = (view: MobileWorkspaceView) => { - if (!onMobileViewChange) setUncontrolledMobileView(view); - onMobileViewChange?.(view); - }; return (
@@ -225,15 +216,14 @@ export function DashboardLayout({ 모바일