diff --git a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/_containers/WithTimeSidebarContainer.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/_containers/WithTimeSidebarContainer.tsx new file mode 100644 index 00000000..47d3a993 --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/_containers/WithTimeSidebarContainer.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { cn } from "@repo/timo-design-system/utils"; +import { useState } from "react"; + +import { + TIME_SIDEBAR_COLLAPSED_MARGIN_CLASS_NAME, + TIME_SIDEBAR_MARGIN_CLASS_NAME, + type TimeSidebarSize, +} from "@/components/layout/sidebar/time/time-sidebar-size"; +import { TimeSidebar } from "@/components/layout/sidebar/time/TimeSidebar"; +import { ROUTES } from "@/constants/routes"; +import { usePathname } from "@/i18n/navigation"; + +interface WithTimeSidebarContainerProps { + children: React.ReactNode; +} + +export const WithTimeSidebarContainer = ({ + children, +}: WithTimeSidebarContainerProps) => { + const [isOpen, setIsOpen] = useState(true); + + const pathname = usePathname(); + + const size: TimeSidebarSize = pathname.startsWith(ROUTES.TODAY) ? "lg" : "sm"; + + return ( + <> +
+ {children} +
+ + setIsOpen((prev) => !prev)} + /> + + ); +}; diff --git a/apps/timo-web/app/[locale]/(main)/home/_components/HomeDateInformation.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/HomeDateInformation.tsx similarity index 100% rename from apps/timo-web/app/[locale]/(main)/home/_components/HomeDateInformation.tsx rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/HomeDateInformation.tsx diff --git a/apps/timo-web/app/[locale]/(main)/home/_components/HomeTodoCard.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/HomeTodoCard.tsx similarity index 97% rename from apps/timo-web/app/[locale]/(main)/home/_components/HomeTodoCard.tsx rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/HomeTodoCard.tsx index b820082a..a1c525d0 100644 --- a/apps/timo-web/app/[locale]/(main)/home/_components/HomeTodoCard.tsx +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/HomeTodoCard.tsx @@ -18,9 +18,9 @@ import { cn } from "@repo/timo-design-system/utils"; import type { TodoPriorityTypes, TodoTimerStatusTypes, -} from "@/app/[locale]/(main)/home/_types/todo-type"; +} from "@/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type"; -import { convertDurationToTimeText } from "@/app/[locale]/(main)/home/_utils/todo-time"; +import { convertDurationToTimeText } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_utils/todo-time"; const PRIORITY_MAP: Record< TodoPriorityTypes, diff --git a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeHeaderContainer.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeHeaderContainer.tsx new file mode 100644 index 00000000..f7ddc770 --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeHeaderContainer.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { useState } from "react"; + +import { Header } from "@/components/layout/header/Header"; +import { useNavigationSidebar } from "@/components/layout/sidebar/navigation/NavigationSidebarContext"; + +const VIEW_OPTIONS = ["기본", "7일"] as const; + +type ViewOption = (typeof VIEW_OPTIONS)[number]; + +const isViewOption = (value: string): value is ViewOption => + (VIEW_OPTIONS as readonly string[]).includes(value); + +export const HomeHeaderContainer = () => { + const [view, setView] = useState(VIEW_OPTIONS[0]); + const { isOpen, toggle } = useNavigationSidebar(); + + const handleChangeView = (value: string) => { + if (isViewOption(value)) { + setView(value); + } + }; + + return ( +
+ + + + } + right={ + + } + /> + ); +}; diff --git a/apps/timo-web/app/[locale]/(main)/home/_containers/HomeTodoContainer.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx similarity index 86% rename from apps/timo-web/app/[locale]/(main)/home/_containers/HomeTodoContainer.tsx rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx index 05de031a..57b8ea46 100644 --- a/apps/timo-web/app/[locale]/(main)/home/_containers/HomeTodoContainer.tsx +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx @@ -3,15 +3,15 @@ import { AddTaskButton } from "@repo/timo-design-system/ui"; import { useState } from "react"; -import type { Todo } from "@/app/[locale]/(main)/home/_types/todo-type"; +import type { Todo } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type"; -import { HomeDateInformation } from "@/app/[locale]/(main)/home/_components/HomeDateInformation"; -import { HomeTodoCard } from "@/app/[locale]/(main)/home/_components/HomeTodoCard"; -import { todoMocks } from "@/app/[locale]/(main)/home/_mocks/todo-mock"; +import { HomeDateInformation } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_components/HomeDateInformation"; +import { HomeTodoCard } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_components/HomeTodoCard"; +import { todoMocks } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_mocks/todo-mock"; import { convertDateToDateText, convertDateToDayOfWeek, -} from "@/app/[locale]/(main)/home/_utils/date"; +} from "@/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date"; export const HomeTodoContainer = () => { const [todos, setTodos] = useState(todoMocks); diff --git a/apps/timo-web/app/[locale]/(main)/home/_mocks/todo-mock.ts b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_mocks/todo-mock.ts similarity index 94% rename from apps/timo-web/app/[locale]/(main)/home/_mocks/todo-mock.ts rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_mocks/todo-mock.ts index 247890a5..41926977 100644 --- a/apps/timo-web/app/[locale]/(main)/home/_mocks/todo-mock.ts +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_mocks/todo-mock.ts @@ -1,4 +1,4 @@ -import { Todo } from "@/app/[locale]/(main)/home/_types/todo-type"; +import { Todo } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type"; export const todoMocks: Todo[] = [ { diff --git a/apps/timo-web/app/[locale]/(main)/focus/_containers/.gitkeep b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_queries/.gitkeep similarity index 100% rename from apps/timo-web/app/[locale]/(main)/focus/_containers/.gitkeep rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_queries/.gitkeep diff --git a/apps/timo-web/app/[locale]/(main)/home/_types/todo-type.ts b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts similarity index 100% rename from apps/timo-web/app/[locale]/(main)/home/_types/todo-type.ts rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts diff --git a/apps/timo-web/app/[locale]/(main)/home/_utils/date.ts b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date.ts similarity index 100% rename from apps/timo-web/app/[locale]/(main)/home/_utils/date.ts rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date.ts diff --git a/apps/timo-web/app/[locale]/(main)/home/_utils/todo-time.ts b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/todo-time.ts similarity index 100% rename from apps/timo-web/app/[locale]/(main)/home/_utils/todo-time.ts rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/todo-time.ts diff --git a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/page.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/page.tsx new file mode 100644 index 00000000..0f5bda89 --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/page.tsx @@ -0,0 +1,13 @@ +import { HomeHeaderContainer } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeHeaderContainer"; +import { HomeTodoContainer } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer"; + +export default function HomePage() { + return ( + <> + +
+ +
+ + ); +} diff --git a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/layout.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/layout.tsx new file mode 100644 index 00000000..a66351dd --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/layout.tsx @@ -0,0 +1,11 @@ +import { WithTimeSidebarContainer } from "@/app/[locale]/(main)/(with-time-sidebar)/_containers/WithTimeSidebarContainer"; + +interface WithTimeSidebarLayoutProps { + children: React.ReactNode; +} + +export default function WithTimeSidebarLayout({ + children, +}: Readonly) { + return {children}; +} diff --git a/apps/timo-web/app/[locale]/(main)/focus/_queries/.gitkeep b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_components/.gitkeep similarity index 100% rename from apps/timo-web/app/[locale]/(main)/focus/_queries/.gitkeep rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_components/.gitkeep diff --git a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayHeaderContainer.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayHeaderContainer.tsx new file mode 100644 index 00000000..df1d0a4b --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayHeaderContainer.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { Header } from "@/components/layout/header/Header"; +import { useNavigationSidebar } from "@/components/layout/sidebar/navigation/NavigationSidebarContext"; + +export const TodayHeaderContainer = () => { + const { isOpen, toggle } = useNavigationSidebar(); + + return ( +
} /> + ); +}; diff --git a/apps/timo-web/app/[locale]/(main)/home/_queries/.gitkeep b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_queries/.gitkeep similarity index 100% rename from apps/timo-web/app/[locale]/(main)/home/_queries/.gitkeep rename to apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_queries/.gitkeep diff --git a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsx b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsx new file mode 100644 index 00000000..6cbd83d6 --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsx @@ -0,0 +1,5 @@ +import { TodayHeaderContainer } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayHeaderContainer"; + +export default function TodayPage() { + return ; +} diff --git a/apps/timo-web/app/[locale]/(main)/_containers/MainShellContainer.tsx b/apps/timo-web/app/[locale]/(main)/_containers/MainShellContainer.tsx new file mode 100644 index 00000000..4d362aeb --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/_containers/MainShellContainer.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { cn } from "@repo/timo-design-system/utils"; + +import { NavigationSidebar } from "@/components/layout/sidebar/navigation/NavigationSidebar"; +import { + NavigationSidebarProvider, + useNavigationSidebar, +} from "@/components/layout/sidebar/navigation/NavigationSidebarContext"; + +interface MainShellContainerProps { + children: React.ReactNode; +} + +const MAIN_MARGIN_CLASS_NAME = { + open: "ml-55", + collapsed: "ml-5", +} as const; + +const MainContent = ({ children }: { children: React.ReactNode }) => { + const { isOpen } = useNavigationSidebar(); + + return ( +
+ {children} +
+ ); +}; + +export const MainShellContainer = ({ children }: MainShellContainerProps) => { + return ( + + + {children} + + ); +}; diff --git a/apps/timo-web/app/[locale]/(main)/focus/_containers/FocusHeaderContainer.tsx b/apps/timo-web/app/[locale]/(main)/focus/_containers/FocusHeaderContainer.tsx new file mode 100644 index 00000000..0fed184e --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/focus/_containers/FocusHeaderContainer.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { Header } from "@/components/layout/header/Header"; +import { useNavigationSidebar } from "@/components/layout/sidebar/navigation/NavigationSidebarContext"; + +export const FocusHeaderContainer = () => { + const { isOpen, toggle } = useNavigationSidebar(); + + return ( +
} /> + ); +}; diff --git a/apps/timo-web/app/[locale]/(main)/focus/page.tsx b/apps/timo-web/app/[locale]/(main)/focus/page.tsx index e8b0076e..7937631d 100644 --- a/apps/timo-web/app/[locale]/(main)/focus/page.tsx +++ b/apps/timo-web/app/[locale]/(main)/focus/page.tsx @@ -1,3 +1,5 @@ +import { FocusHeaderContainer } from "@/app/[locale]/(main)/focus/_containers/FocusHeaderContainer"; + export default function FocusPage() { - return <>; + return ; } diff --git a/apps/timo-web/app/[locale]/(main)/home/page.tsx b/apps/timo-web/app/[locale]/(main)/home/page.tsx deleted file mode 100644 index aea7245f..00000000 --- a/apps/timo-web/app/[locale]/(main)/home/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { HomeTodoContainer } from "@/app/[locale]/(main)/home/_containers/HomeTodoContainer"; - -export default function HomePage() { - return ( - <> - - - ); -} diff --git a/apps/timo-web/app/[locale]/(main)/layout.tsx b/apps/timo-web/app/[locale]/(main)/layout.tsx index cbc00547..404f2d97 100644 --- a/apps/timo-web/app/[locale]/(main)/layout.tsx +++ b/apps/timo-web/app/[locale]/(main)/layout.tsx @@ -1,4 +1,4 @@ -import { NavigationSidebar } from "@/components/layout/sidebar/NavigationSidebar"; +import { MainShellContainer } from "@/app/[locale]/(main)/_containers/MainShellContainer"; interface MainLayoutProps { children: React.ReactNode; @@ -6,9 +6,8 @@ interface MainLayoutProps { export default function MainLayout({ children }: Readonly) { return ( -
- -
{children}
+
+ {children}
); } diff --git a/apps/timo-web/app/[locale]/(main)/settings/_containers/.gitkeep b/apps/timo-web/app/[locale]/(main)/settings/_containers/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsHeaderContainer.tsx b/apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsHeaderContainer.tsx new file mode 100644 index 00000000..a5fa5297 --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsHeaderContainer.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { Header } from "@/components/layout/header/Header"; +import { useNavigationSidebar } from "@/components/layout/sidebar/navigation/NavigationSidebarContext"; + +export const SettingsHeaderContainer = () => { + const { isOpen, toggle } = useNavigationSidebar(); + + return ( +
} /> + ); +}; diff --git a/apps/timo-web/app/[locale]/(main)/settings/layout.tsx b/apps/timo-web/app/[locale]/(main)/settings/layout.tsx index 8f656187..be819660 100644 --- a/apps/timo-web/app/[locale]/(main)/settings/layout.tsx +++ b/apps/timo-web/app/[locale]/(main)/settings/layout.tsx @@ -1,7 +1,14 @@ -export default function SettingsLayout({ +import { SettingsHeaderContainer } from "@/app/[locale]/(main)/settings/_containers/SettingsHeaderContainer"; + +export default function SettingsLayout({ children, }: { children: React.ReactNode; }) { - return <>{children}; + return ( + <> + + {children} + + ); } diff --git a/apps/timo-web/app/[locale]/(main)/statistics/_containers/.gitkeep b/apps/timo-web/app/[locale]/(main)/statistics/_containers/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx b/apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx new file mode 100644 index 00000000..136d84a2 --- /dev/null +++ b/apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { useState } from "react"; + +import { Header } from "@/components/layout/header/Header"; +import { useNavigationSidebar } from "@/components/layout/sidebar/navigation/NavigationSidebarContext"; + +const MONTH_LABEL_FORMATTER = new Intl.DateTimeFormat("ko-KR", { + year: "numeric", + month: "long", +}); + +const addMonths = (date: Date, amount: number) => + new Date(date.getFullYear(), date.getMonth() + amount, 1); + +export const StatisticsHeaderContainer = () => { + const [currentMonth, setCurrentMonth] = useState(() => new Date()); + const { isOpen, toggle } = useNavigationSidebar(); + + const handlePrev = () => setCurrentMonth((prev) => addMonths(prev, -1)); + const handleNext = () => setCurrentMonth((prev) => addMonths(prev, 1)); + + return ( +
+ + + + } + /> + ); +}; diff --git a/apps/timo-web/app/[locale]/(main)/statistics/page.tsx b/apps/timo-web/app/[locale]/(main)/statistics/page.tsx index 73073c83..a602413c 100644 --- a/apps/timo-web/app/[locale]/(main)/statistics/page.tsx +++ b/apps/timo-web/app/[locale]/(main)/statistics/page.tsx @@ -1,3 +1,5 @@ +import { StatisticsHeaderContainer } from "@/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer"; + export default function StatisticsPage() { - return <>; + return ; } diff --git a/apps/timo-web/app/[locale]/(main)/today/_components/.gitkeep b/apps/timo-web/app/[locale]/(main)/today/_components/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/timo-web/app/[locale]/(main)/today/_containers/.gitkeep b/apps/timo-web/app/[locale]/(main)/today/_containers/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/timo-web/app/[locale]/(main)/today/_queries/.gitkeep b/apps/timo-web/app/[locale]/(main)/today/_queries/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/timo-web/app/[locale]/(main)/today/page.tsx b/apps/timo-web/app/[locale]/(main)/today/page.tsx deleted file mode 100644 index 38a6b1d1..00000000 --- a/apps/timo-web/app/[locale]/(main)/today/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function TodayPage() { - return <>; -} diff --git a/apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx b/apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx deleted file mode 100644 index 678cbb64..00000000 --- a/apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx +++ /dev/null @@ -1,113 +0,0 @@ -"use client"; - -import timoTextLogo from "@repo/timo-design-system/assets/images/logo/timo-text-logo.svg"; -import { - ChartHoverIcon, - ChartOffIcon, - ChartOnIcon, - HomeHoverIcon, - HomeOffIcon, - HomeOnIcon, - SettingHoverIcon, - SettingOffIcon, - SettingOnIcon, - TimerHoverIcon, - TimerOffIcon, - TimerOnIcon, - TodayHoverIcon, - TodayOffIcon, - TodayOnIcon, -} from "@repo/timo-design-system/icons"; -import { TabButton } from "@repo/timo-design-system/ui"; -import Image from "next/image"; -import { useTranslations } from "next-intl"; - -import { ROUTES } from "@/constants/routes"; -import { Link, usePathname } from "@/i18n/navigation"; - -const NAV_ITEMS = [ - { - href: ROUTES.HOME, - labelKey: "home", - OnIcon: HomeOnIcon, - OffIcon: HomeOffIcon, - HoverIcon: HomeHoverIcon, - className: undefined, - }, - { - href: ROUTES.TODAY, - labelKey: "today", - OnIcon: TodayOnIcon, - OffIcon: TodayOffIcon, - HoverIcon: TodayHoverIcon, - className: undefined, - }, - { - href: ROUTES.FOCUS, - labelKey: "focus", - OnIcon: TimerOnIcon, - OffIcon: TimerOffIcon, - HoverIcon: TimerHoverIcon, - className: undefined, - }, - { - href: ROUTES.STATISTICS, - labelKey: "statistics", - OnIcon: ChartOnIcon, - OffIcon: ChartOffIcon, - HoverIcon: ChartHoverIcon, - className: undefined, - }, - { - href: ROUTES.SETTINGS, - labelKey: "settings", - OnIcon: SettingOnIcon, - OffIcon: SettingOffIcon, - HoverIcon: SettingHoverIcon, - className: "mt-auto", - }, -] as const; - -export const NavigationSidebar = () => { - const t = useTranslations("Navigation"); - const pathname = usePathname(); - const isActivePath = (pathname: string, href: string) => - pathname === href || pathname.startsWith(`${href}/`); - - return ( - - ); -}; diff --git a/apps/timo-web/components/layout/sidebar/navigation/NavigationSidebar.tsx b/apps/timo-web/components/layout/sidebar/navigation/NavigationSidebar.tsx new file mode 100644 index 00000000..d29c399d --- /dev/null +++ b/apps/timo-web/components/layout/sidebar/navigation/NavigationSidebar.tsx @@ -0,0 +1,136 @@ +"use client"; + +import timoTextLogo from "@repo/timo-design-system/assets/images/logo/timo-text-logo.svg"; +import { + ChartHoverIcon, + ChartOffIcon, + ChartOnIcon, + HomeHoverIcon, + HomeOffIcon, + HomeOnIcon, + SettingHoverIcon, + SettingOffIcon, + SettingOnIcon, + TimerHoverIcon, + TimerOffIcon, + TimerOnIcon, + TodayHoverIcon, + TodayOffIcon, + TodayOnIcon, +} from "@repo/timo-design-system/icons"; +import { TabButton } from "@repo/timo-design-system/ui"; +import { cn } from "@repo/timo-design-system/utils"; +import Image from "next/image"; +import { useTranslations } from "next-intl"; + +import { useNavigationSidebar } from "@/components/layout/sidebar/navigation/NavigationSidebarContext"; +import { ROUTES } from "@/constants/routes"; +import { Link, usePathname } from "@/i18n/navigation"; + +const MAIN_NAV_ITEMS = [ + { + href: ROUTES.HOME, + labelKey: "home", + OnIcon: HomeOnIcon, + OffIcon: HomeOffIcon, + HoverIcon: HomeHoverIcon, + }, + { + href: ROUTES.TODAY, + labelKey: "today", + OnIcon: TodayOnIcon, + OffIcon: TodayOffIcon, + HoverIcon: TodayHoverIcon, + }, + { + href: ROUTES.FOCUS, + labelKey: "focus", + OnIcon: TimerOnIcon, + OffIcon: TimerOffIcon, + HoverIcon: TimerHoverIcon, + }, + { + href: ROUTES.STATISTICS, + labelKey: "statistics", + OnIcon: ChartOnIcon, + OffIcon: ChartOffIcon, + HoverIcon: ChartHoverIcon, + }, +] as const; + +const SETTINGS_NAV_ITEM = { + href: ROUTES.SETTINGS, + labelKey: "settings", + OnIcon: SettingOnIcon, + OffIcon: SettingOffIcon, + HoverIcon: SettingHoverIcon, +} as const; + +export const NavigationSidebar = () => { + const t = useTranslations("Navigation"); + + const pathname = usePathname(); + + const { isOpen } = useNavigationSidebar(); + + const isActivePath = (pathname: string, href: string) => + pathname === href || pathname.startsWith(`${href}/`); + + const renderNavItem = ({ + href, + labelKey, + OnIcon, + OffIcon, + HoverIcon, + }: (typeof MAIN_NAV_ITEMS)[number] | typeof SETTINGS_NAV_ITEM) => { + const isSelected = isActivePath(pathname, href); + + return ( + + + ) : ( + + ) + } + hoverIcon={} + isSelected={isSelected} + /> + + ); + }; + + return ( + + ); +}; diff --git a/apps/timo-web/components/layout/sidebar/navigation/NavigationSidebarContext.tsx b/apps/timo-web/components/layout/sidebar/navigation/NavigationSidebarContext.tsx new file mode 100644 index 00000000..fe985d21 --- /dev/null +++ b/apps/timo-web/components/layout/sidebar/navigation/NavigationSidebarContext.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { createContext, useContext, useState } from "react"; + +interface NavigationSidebarContextValue { + isOpen: boolean; + toggle: () => void; +} + +const NavigationSidebarContext = + createContext(null); + +interface NavigationSidebarProviderProps { + children: React.ReactNode; +} + +export const NavigationSidebarProvider = ({ + children, +}: NavigationSidebarProviderProps) => { + const [isOpen, setIsOpen] = useState(true); + + const value: NavigationSidebarContextValue = { + isOpen, + toggle: () => setIsOpen((prev) => !prev), + }; + + return ( + + {children} + + ); +}; + +export const useNavigationSidebar = () => { + const context = useContext(NavigationSidebarContext); + + if (!context) { + throw new Error( + "useNavigationSidebar must be used within a NavigationSidebarProvider", + ); + } + + return context; +}; diff --git a/apps/timo-web/components/layout/sidebar/time/TimeSidebar.tsx b/apps/timo-web/components/layout/sidebar/time/TimeSidebar.tsx new file mode 100644 index 00000000..c77fb616 --- /dev/null +++ b/apps/timo-web/components/layout/sidebar/time/TimeSidebar.tsx @@ -0,0 +1,95 @@ +"use client"; + +import { TogglePanel } from "@repo/timo-design-system/ui"; +import { cn } from "@repo/timo-design-system/utils"; +import { useId, useState } from "react"; + +import { + TIME_SIDEBAR_COLLAPSED_WIDTH_CLASS_NAME, + TIME_SIDEBAR_WIDTH_CLASS_NAME, + type TimeSidebarSize, +} from "@/components/layout/sidebar/time/time-sidebar-size"; +import { TimeboxPanel } from "@/components/layout/sidebar/time/TimeboxPanel"; +import { TimerPanel } from "@/components/layout/sidebar/time/TimerPanel"; +import { TimeSidebarHeader } from "@/components/layout/sidebar/time/TimeSidebarHeader"; + +type TimeSidebarTab = "timebox" | "timer"; + +export interface TimeSidebarProps { + size?: TimeSidebarSize; + isOpen?: boolean; + onToggleCollapse?: () => void; +} + +export const TimeSidebar = ({ + size = "sm", + isOpen = true, + onToggleCollapse, +}: TimeSidebarProps) => { + const id = useId(); + const [activeTab, setActiveTab] = useState("timebox"); + + const timeboxPanelId = `${id}-timebox-panel`; + const timerPanelId = `${id}-timer-panel`; + + const handleChangeTab = (value: string) => { + setActiveTab(value as TimeSidebarTab); + }; + + return ( + + ); +}; diff --git a/apps/timo-web/components/layout/time-sidebar/TimeSidebarHeader.tsx b/apps/timo-web/components/layout/sidebar/time/TimeSidebarHeader.tsx similarity index 55% rename from apps/timo-web/components/layout/time-sidebar/TimeSidebarHeader.tsx rename to apps/timo-web/components/layout/sidebar/time/TimeSidebarHeader.tsx index 039e57af..fbd32ec1 100644 --- a/apps/timo-web/components/layout/time-sidebar/TimeSidebarHeader.tsx +++ b/apps/timo-web/components/layout/sidebar/time/TimeSidebarHeader.tsx @@ -1,4 +1,5 @@ import { SidebarButton } from "@repo/timo-design-system/ui"; +import { cn } from "@repo/timo-design-system/utils"; export interface TimeSidebarHeaderProps { date: Date; @@ -12,18 +13,29 @@ export const TimeSidebarHeader = ({ onToggleCollapse, }: TimeSidebarHeaderProps) => { const day = date.getDate(); + const weekday = new Intl.DateTimeFormat("ko-KR", { weekday: "long" }).format( date, ); return ( -
-

+

+

{day} {weekday}

- +
); }; diff --git a/apps/timo-web/components/layout/time-sidebar/TimeboxPanel.tsx b/apps/timo-web/components/layout/sidebar/time/TimeboxPanel.tsx similarity index 100% rename from apps/timo-web/components/layout/time-sidebar/TimeboxPanel.tsx rename to apps/timo-web/components/layout/sidebar/time/TimeboxPanel.tsx diff --git a/apps/timo-web/components/layout/time-sidebar/TimerPanel.tsx b/apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx similarity index 100% rename from apps/timo-web/components/layout/time-sidebar/TimerPanel.tsx rename to apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx diff --git a/apps/timo-web/components/layout/sidebar/time/time-sidebar-size.ts b/apps/timo-web/components/layout/sidebar/time/time-sidebar-size.ts new file mode 100644 index 00000000..9ff2df03 --- /dev/null +++ b/apps/timo-web/components/layout/sidebar/time/time-sidebar-size.ts @@ -0,0 +1,14 @@ +export type TimeSidebarSize = "sm" | "lg"; + +export const TIME_SIDEBAR_WIDTH_CLASS_NAME: Record = { + sm: "w-76", + lg: "w-110", +}; + +export const TIME_SIDEBAR_MARGIN_CLASS_NAME: Record = { + sm: "mr-76", + lg: "mr-110", +}; + +export const TIME_SIDEBAR_COLLAPSED_WIDTH_CLASS_NAME = "w-17"; +export const TIME_SIDEBAR_COLLAPSED_MARGIN_CLASS_NAME = "mr-17"; diff --git a/apps/timo-web/components/layout/time-sidebar/TimeSidebar.tsx b/apps/timo-web/components/layout/time-sidebar/TimeSidebar.tsx deleted file mode 100644 index 482a66d8..00000000 --- a/apps/timo-web/components/layout/time-sidebar/TimeSidebar.tsx +++ /dev/null @@ -1,72 +0,0 @@ -"use client"; - -import { TogglePanel } from "@repo/timo-design-system/ui"; -import { useId, useState } from "react"; - -import { TimeboxPanel } from "@/components/layout/time-sidebar/TimeboxPanel"; -import { TimerPanel } from "@/components/layout/time-sidebar/TimerPanel"; -import { TimeSidebarHeader } from "@/components/layout/time-sidebar/TimeSidebarHeader"; - -type TimeSidebarTab = "timebox" | "timer"; -type TimeSidebarSize = "sm" | "lg"; - -const TIME_SIDEBAR_SIZE_WIDTH_CLASS_NAME: Record = { - sm: "w-76", - lg: "w-110", -}; - -export interface TimeSidebarProps { - size?: TimeSidebarSize; -} - -export const TimeSidebar = ({ size = "sm" }: TimeSidebarProps) => { - const id = useId(); - const [activeTab, setActiveTab] = useState("timebox"); - - const timeboxPanelId = `${id}-timebox-panel`; - const timerPanelId = `${id}-timer-panel`; - - const handleChangeTab = (value: string) => { - setActiveTab(value as TimeSidebarTab); - }; - - return ( - - ); -}; diff --git a/apps/timo-web/components/timer/Timer.tsx b/apps/timo-web/components/timer/Timer.tsx index 10cdb4d4..1c67b82b 100644 --- a/apps/timo-web/components/timer/Timer.tsx +++ b/apps/timo-web/components/timer/Timer.tsx @@ -1,3 +1,5 @@ +import { cn } from "@repo/timo-design-system/utils"; + import type { ReactNode } from "react"; export type TimerSize = "sm" | "lg"; @@ -68,9 +70,9 @@ export const Timer = ({ cy={diameter / 2} r={radius} fill="none" - className={ - isOvertime ? "stroke-timo-blue-300" : "stroke-timo-blue-50" - } + className={cn( + isOvertime ? "stroke-timo-blue-300" : "stroke-timo-blue-50", + )} strokeWidth={strokeWidth} /> @@ -79,7 +81,10 @@ export const Timer = ({ cy={diameter / 2} r={radius} fill="none" - className={`transition-[stroke-dashoffset] duration-1000 ease-linear ${isOvertime ? "stroke-timo-yellow-300" : "stroke-timo-blue-300"}`} + className={cn( + "transition-[stroke-dashoffset] duration-1000 ease-linear", + isOvertime ? "stroke-timo-yellow-300" : "stroke-timo-blue-300", + )} strokeWidth={strokeWidth} strokeDasharray={circumference} strokeDashoffset={dashOffset} @@ -93,13 +98,21 @@ export const Timer = ({

{time}

{plannedLabel}

diff --git a/apps/timo-web/components/timer/TimerControlButton.tsx b/apps/timo-web/components/timer/TimerControlButton.tsx index 307b897b..776aa2c8 100644 --- a/apps/timo-web/components/timer/TimerControlButton.tsx +++ b/apps/timo-web/components/timer/TimerControlButton.tsx @@ -1,3 +1,5 @@ +import { cn } from "@repo/timo-design-system/utils"; + import type { MouseEventHandler, ReactNode } from "react"; export type TimerControlButtonVariant = "default" | "active"; @@ -27,13 +29,14 @@ export const TimerControlButton = ({ aria-label={label} onClick={onClick} disabled={disabled} - className={`group flex size-14.5 items-center justify-center rounded-full ${ + className={cn( + "group flex size-14.5 items-center justify-center rounded-full", isForcedActive ? "bg-timo-blue-50" : activeIcon ? "bg-timo-gray-300 active:bg-timo-blue-50" - : "bg-timo-gray-300" - }`} + : "bg-timo-gray-300", + )} > {isForcedActive || !activeIcon ? ( icon diff --git a/packages/timo-design-system/src/components/dropdown-view/DropdownView.tsx b/packages/timo-design-system/src/components/dropdown-view/DropdownView.tsx index 17debb7a..76fd20ba 100644 --- a/packages/timo-design-system/src/components/dropdown-view/DropdownView.tsx +++ b/packages/timo-design-system/src/components/dropdown-view/DropdownView.tsx @@ -1,6 +1,6 @@ import { Fragment } from "react"; -import { ChevronDownIcon, ChevronUpIcon } from "../../icons"; +import { ChevronDownIcon } from "../../icons"; import { cn } from "../../lib"; import { Dropdown } from "../layout/dropdown/Dropdown"; @@ -20,7 +20,7 @@ export const DropdownView = ({ className, }: DropdownViewProps) => { return ( - +
- - +
diff --git a/packages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx b/packages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx index 53902191..6c8faa81 100644 --- a/packages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx +++ b/packages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx @@ -1,3 +1,5 @@ +import { cn } from "../../../lib"; + export type TagVariant = "default" | "blue"; export interface TagIconProps { @@ -10,14 +12,16 @@ export const TagIcon = ({ text, variant = "default" }: TagIconProps) => { return (
{text}