diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 803d4dd..62a3c92 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -8,8 +8,8 @@ export const viewport: Viewport = { themeColor: "#2563EB", width: "device-width", initialScale: 1, - maximumScale: 1, - userScalable: false, + maximumScale: 5, + userScalable: true, }; export const metadata = { diff --git a/frontend/src/app/reminders/page.tsx b/frontend/src/app/reminders/page.tsx index bd7e1cb..e7b168a 100644 --- a/frontend/src/app/reminders/page.tsx +++ b/frontend/src/app/reminders/page.tsx @@ -219,7 +219,7 @@ const RemindersPage = () => { {showAdvanceReminder && ( -
+
{
{showBarkSettings && ( -
+

{t("barkSettingsDesc")}

diff --git a/frontend/src/app/todos/page.tsx b/frontend/src/app/todos/page.tsx index 9e34f21..b7b4534 100644 --- a/frontend/src/app/todos/page.tsx +++ b/frontend/src/app/todos/page.tsx @@ -271,8 +271,8 @@ const TodosPage = () => { {todos.length > 0 ? (
- {/* Table header */} -
+ {/* Table header - desktop only */} +
{t("title")}
{t("due")}
@@ -284,81 +284,87 @@ const TodosPage = () => { return (
- {/* Checkbox / Check-in */} - + {/* Mobile: Row 1 - Checkbox + Title */} +
+ {/* Checkbox / Check-in */} + - {/* Title + badges */} -
-
- - {todo.title} - - {todo.recurrenceRule && ( - - - {getRecurrenceLabel(todo.recurrenceRule)} + {/* Title + badges */} +
+
+ + {todo.title} - )} - {todo.checkInCount > 0 && ( - - {todo.checkInCount}x - - )} + {todo.recurrenceRule && ( + + + {getRecurrenceLabel(todo.recurrenceRule)} + + )} + {todo.checkInCount > 0 && ( + + {todo.checkInCount}x + + )} +
- {/* Due date */} -
- {formatDueDate(todo.dueAt)} -
+ {/* Mobile: Row 2 - Due date + Status + Delete */} +
+ {/* Due date */} +
+ {formatDueDate(todo.dueAt)} +
- {/* Status pill */} - - {status.label} - + {/* Status pill */} + + {status.label} + - {/* Delete */} -
- - - - - - - {t("confirmDelete")} - - {t("confirmDeleteDesc", { title: todo.title })} - - - - {t("cancel")} - deleteTodo(todo.id)} + {/* Delete */} +
+ + + + + + + {t("confirmDelete")} + + {t("confirmDeleteDesc", { title: todo.title })} + + + + {t("cancel")} + deleteTodo(todo.id)} + > + {t("delete")} + + + + +
); @@ -384,37 +390,37 @@ const TodosPage = () => { {/* Right column - Stats & Upcoming */}
{/* Stats cards */} -
- -
-
+
+ +
+
-
-
{totalTasks}
-
{t("totalTasks")}
+
+
{totalTasks}
+
{t("totalTasks")}
- -
-
+ +
+
-
-
{checkedInCount}
-
{t("checkedInToday")}
+
+
{checkedInCount}
+
{t("checkedInToday")}
- -
-
+ +
+
-
-
{recurringCount}
-
{t("recurringTasks")}
+
+
{recurringCount}
+
{t("recurringTasks")}
diff --git a/frontend/src/components/AppShell.tsx b/frontend/src/components/AppShell.tsx index b2f1ad0..74ca27e 100644 --- a/frontend/src/components/AppShell.tsx +++ b/frontend/src/components/AppShell.tsx @@ -10,10 +10,10 @@ import { UserProvider } from "@/lib/user-context"; const AppShellContent = ({ children }: { children: ReactNode }) => { return ( -
+
-
+
{children}
diff --git a/frontend/src/components/AppSidebar.tsx b/frontend/src/components/AppSidebar.tsx index 1008ee9..591105f 100644 --- a/frontend/src/components/AppSidebar.tsx +++ b/frontend/src/components/AppSidebar.tsx @@ -16,6 +16,7 @@ import { SidebarMenuButton, SidebarMenuItem, SidebarSeparator, + useSidebar, } from "@/components/ui/sidebar"; import Avatar from "@/components/ui/avatar"; import LanguageSwitcher from "@/components/LanguageSwitcher"; @@ -37,6 +38,42 @@ const AppSidebar = () => { const { unreadCount } = useNotification(); const { user } = useUser(); const t = useTranslation(); + const { isMobile } = useSidebar(); + + if (isMobile) { + return ( + + + + ); + } return ( diff --git a/frontend/src/components/ui/sidebar.tsx b/frontend/src/components/ui/sidebar.tsx index 0d9a987..81a57cf 100644 --- a/frontend/src/components/ui/sidebar.tsx +++ b/frontend/src/components/ui/sidebar.tsx @@ -1,3 +1,5 @@ +"use client"; + import * as React from "react"; import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; @@ -8,6 +10,7 @@ type SidebarContextValue = { open: boolean; setOpen: (value: boolean) => void; toggle: () => void; + isMobile: boolean; }; const SidebarContext = React.createContext(null); @@ -20,6 +23,8 @@ const useSidebar = () => { return context; }; +const MOBILE_BREAKPOINT = 768; + const SidebarProvider = ({ children, defaultOpen = true, @@ -28,9 +33,21 @@ const SidebarProvider = ({ defaultOpen?: boolean; }) => { const [open, setOpen] = React.useState(defaultOpen); + const [isMobile, setIsMobile] = React.useState(false); const toggle = React.useCallback(() => setOpen((prev) => !prev), []); + + React.useEffect(() => { + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); + const onChange = (e: MediaQueryListEvent | MediaQueryList) => { + setIsMobile(e.matches); + }; + onChange(mql); + mql.addEventListener("change", onChange); + return () => mql.removeEventListener("change", onChange); + }, []); + return ( - + {children} ); @@ -57,8 +74,24 @@ const sidebarVariants = cva( const Sidebar = React.forwardRef< HTMLDivElement, React.HTMLAttributes & VariantProps ->(({ className, variant, style, ...props }, ref) => { - const { open } = useSidebar(); +>(({ className, variant, style, children, ...props }, ref) => { + const { open, isMobile } = useSidebar(); + + if (isMobile) { + return ( +
+ {children} +
+ ); + } + return (
>( - ({ className, ...props }, ref) => ( -
- ) + ({ className, ...props }, ref) => { + const { isMobile } = useSidebar(); + return ( +
+ ); + } ); SidebarInset.displayName = "SidebarInset";