Compare commits
1 Commits
af194d1b9c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f33c71240 |
@@ -8,8 +8,8 @@ export const viewport: Viewport = {
|
|||||||
themeColor: "#2563EB",
|
themeColor: "#2563EB",
|
||||||
width: "device-width",
|
width: "device-width",
|
||||||
initialScale: 1,
|
initialScale: 1,
|
||||||
maximumScale: 1,
|
maximumScale: 5,
|
||||||
userScalable: false,
|
userScalable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ const RemindersPage = () => {
|
|||||||
<Label htmlFor="showAdvanceReminder">{t("enableAdvanceReminder")}</Label>
|
<Label htmlFor="showAdvanceReminder">{t("enableAdvanceReminder")}</Label>
|
||||||
</div>
|
</div>
|
||||||
{showAdvanceReminder && (
|
{showAdvanceReminder && (
|
||||||
<div className="ml-6 space-y-2">
|
<div className="ml-4 md:ml-6 space-y-2">
|
||||||
<Label htmlFor="offset">{t("advanceReminder")}</Label>
|
<Label htmlFor="offset">{t("advanceReminder")}</Label>
|
||||||
<Input
|
<Input
|
||||||
id="offset"
|
id="offset"
|
||||||
@@ -311,7 +311,7 @@ const RemindersPage = () => {
|
|||||||
<Label htmlFor="showBarkSettings">{t("barkSettings")}</Label>
|
<Label htmlFor="showBarkSettings">{t("barkSettings")}</Label>
|
||||||
</div>
|
</div>
|
||||||
{showBarkSettings && (
|
{showBarkSettings && (
|
||||||
<div className="ml-6 space-y-4 rounded-xl border border-border/60 bg-muted/30 p-4">
|
<div className="ml-4 md:ml-6 space-y-4 rounded-xl border border-border/60 bg-muted/30 p-4">
|
||||||
<p className="text-sm text-muted-foreground">{t("barkSettingsDesc")}</p>
|
<p className="text-sm text-muted-foreground">{t("barkSettingsDesc")}</p>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -271,8 +271,8 @@ const TodosPage = () => {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{todos.length > 0 ? (
|
{todos.length > 0 ? (
|
||||||
<div className="divide-y divide-border/50">
|
<div className="divide-y divide-border/50">
|
||||||
{/* Table header */}
|
{/* Table header - desktop only */}
|
||||||
<div className="grid grid-cols-[auto_1fr_auto_auto_auto] items-center gap-4 px-3 pb-2 text-xs font-medium uppercase tracking-wider text-muted-foreground">
|
<div className="hidden md:grid grid-cols-[auto_1fr_auto_auto_auto] items-center gap-4 px-3 pb-2 text-xs font-medium uppercase tracking-wider text-muted-foreground">
|
||||||
<div className="w-5" />
|
<div className="w-5" />
|
||||||
<div>{t("title")}</div>
|
<div>{t("title")}</div>
|
||||||
<div>{t("due")}</div>
|
<div>{t("due")}</div>
|
||||||
@@ -284,81 +284,87 @@ const TodosPage = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={todo.id}
|
key={todo.id}
|
||||||
className="group grid grid-cols-[auto_1fr_auto_auto_auto] items-center gap-4 px-3 py-3 transition-colors hover:bg-muted/40 rounded-lg"
|
className="group px-3 py-3 transition-colors hover:bg-muted/40 rounded-lg md:grid md:grid-cols-[auto_1fr_auto_auto_auto] md:items-center md:gap-4"
|
||||||
>
|
>
|
||||||
{/* Checkbox / Check-in */}
|
{/* Mobile: Row 1 - Checkbox + Title */}
|
||||||
<button
|
<div className="flex items-center gap-3 md:contents">
|
||||||
type="button"
|
{/* Checkbox / Check-in */}
|
||||||
className={`flex h-5 w-5 items-center justify-center rounded-full border-2 transition-colors ${
|
<button
|
||||||
todo.isCheckedIn
|
type="button"
|
||||||
? "border-emerald-500 bg-emerald-500 text-white"
|
className={`flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 transition-colors ${
|
||||||
: "border-border hover:border-emerald-400"
|
todo.isCheckedIn
|
||||||
}`}
|
? "border-emerald-500 bg-emerald-500 text-white"
|
||||||
onClick={() => checkInTodo(todo.id)}
|
: "border-border hover:border-emerald-400"
|
||||||
disabled={checkingIn === todo.id || todo.isCheckedIn}
|
}`}
|
||||||
>
|
onClick={() => checkInTodo(todo.id)}
|
||||||
{todo.isCheckedIn && <CheckCircle2 className="h-3 w-3" />}
|
disabled={checkingIn === todo.id || todo.isCheckedIn}
|
||||||
</button>
|
>
|
||||||
|
{todo.isCheckedIn && <CheckCircle2 className="h-3 w-3" />}
|
||||||
|
</button>
|
||||||
|
|
||||||
{/* Title + badges */}
|
{/* Title + badges */}
|
||||||
<div className="min-w-0">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<span className={`truncate text-sm font-medium ${todo.isCheckedIn ? "text-muted-foreground line-through" : "text-foreground"}`}>
|
<span className={`truncate text-sm font-medium ${todo.isCheckedIn ? "text-muted-foreground line-through" : "text-foreground"}`}>
|
||||||
{todo.title}
|
{todo.title}
|
||||||
</span>
|
|
||||||
{todo.recurrenceRule && (
|
|
||||||
<span className="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-700">
|
|
||||||
<Repeat className="h-3 w-3" />
|
|
||||||
{getRecurrenceLabel(todo.recurrenceRule)}
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
{todo.recurrenceRule && (
|
||||||
{todo.checkInCount > 0 && (
|
<span className="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-700">
|
||||||
<span className="rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700">
|
<Repeat className="h-3 w-3" />
|
||||||
{todo.checkInCount}x
|
{getRecurrenceLabel(todo.recurrenceRule)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{todo.checkInCount > 0 && (
|
||||||
|
<span className="rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700">
|
||||||
|
{todo.checkInCount}x
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Due date */}
|
{/* Mobile: Row 2 - Due date + Status + Delete */}
|
||||||
<div className="text-xs text-muted-foreground whitespace-nowrap">
|
<div className="flex items-center gap-2 mt-2 pl-8 md:contents md:mt-0 md:pl-0">
|
||||||
{formatDueDate(todo.dueAt)}
|
{/* Due date */}
|
||||||
</div>
|
<div className="text-xs text-muted-foreground whitespace-nowrap">
|
||||||
|
{formatDueDate(todo.dueAt)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Status pill */}
|
{/* Status pill */}
|
||||||
<span className={`inline-flex rounded-full border px-2 py-0.5 text-xs font-medium whitespace-nowrap ${status.className}`}>
|
<span className={`inline-flex rounded-full border px-2 py-0.5 text-xs font-medium whitespace-nowrap ${status.className}`}>
|
||||||
{status.label}
|
{status.label}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Delete */}
|
{/* Delete */}
|
||||||
<div className="flex justify-end w-16">
|
<div className="flex justify-end ml-auto md:w-16">
|
||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded-lg p-1.5 text-muted-foreground opacity-0 transition-all hover:bg-red-50 hover:text-red-500 group-hover:opacity-100"
|
className="rounded-lg p-1.5 text-muted-foreground opacity-100 md:opacity-0 transition-all hover:bg-red-50 hover:text-red-500 group-hover:opacity-100"
|
||||||
>
|
|
||||||
<Trash2 className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</AlertDialogTrigger>
|
|
||||||
<AlertDialogContent>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle>{t("confirmDelete")}</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
{t("confirmDeleteDesc", { title: todo.title })}
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
|
|
||||||
<AlertDialogAction
|
|
||||||
className="bg-red-500 hover:bg-red-600"
|
|
||||||
onClick={() => deleteTodo(todo.id)}
|
|
||||||
>
|
>
|
||||||
{t("delete")}
|
<Trash2 className="h-4 w-4" />
|
||||||
</AlertDialogAction>
|
</button>
|
||||||
</AlertDialogFooter>
|
</AlertDialogTrigger>
|
||||||
</AlertDialogContent>
|
<AlertDialogContent>
|
||||||
</AlertDialog>
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>{t("confirmDelete")}</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
{t("confirmDeleteDesc", { title: todo.title })}
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
className="bg-red-500 hover:bg-red-600"
|
||||||
|
onClick={() => deleteTodo(todo.id)}
|
||||||
|
>
|
||||||
|
{t("delete")}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -384,37 +390,37 @@ const TodosPage = () => {
|
|||||||
{/* Right column - Stats & Upcoming */}
|
{/* Right column - Stats & Upcoming */}
|
||||||
<div className="flex flex-col gap-5">
|
<div className="flex flex-col gap-5">
|
||||||
{/* Stats cards */}
|
{/* Stats cards */}
|
||||||
<div className="grid grid-cols-1 gap-3">
|
<div className="grid grid-cols-3 gap-2 md:grid-cols-1 md:gap-3">
|
||||||
<Card className="p-4">
|
<Card className="p-3 md:p-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex flex-col items-center gap-1 md:flex-row md:items-center md:gap-3">
|
||||||
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-primary/10 text-primary">
|
<div className="flex h-8 w-8 md:h-9 md:w-9 items-center justify-center rounded-xl bg-primary/10 text-primary">
|
||||||
<ListTodo className="h-4 w-4" />
|
<ListTodo className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="text-center md:text-left">
|
||||||
<div className="text-2xl font-semibold text-foreground">{totalTasks}</div>
|
<div className="text-xl md:text-2xl font-semibold text-foreground">{totalTasks}</div>
|
||||||
<div className="text-xs text-muted-foreground">{t("totalTasks")}</div>
|
<div className="text-[10px] md:text-xs text-muted-foreground">{t("totalTasks")}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="p-4">
|
<Card className="p-3 md:p-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex flex-col items-center gap-1 md:flex-row md:items-center md:gap-3">
|
||||||
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-emerald-500/10 text-emerald-600">
|
<div className="flex h-8 w-8 md:h-9 md:w-9 items-center justify-center rounded-xl bg-emerald-500/10 text-emerald-600">
|
||||||
<CalendarCheck className="h-4 w-4" />
|
<CalendarCheck className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="text-center md:text-left">
|
||||||
<div className="text-2xl font-semibold text-foreground">{checkedInCount}</div>
|
<div className="text-xl md:text-2xl font-semibold text-foreground">{checkedInCount}</div>
|
||||||
<div className="text-xs text-muted-foreground">{t("checkedInToday")}</div>
|
<div className="text-[10px] md:text-xs text-muted-foreground">{t("checkedInToday")}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="p-4">
|
<Card className="p-3 md:p-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex flex-col items-center gap-1 md:flex-row md:items-center md:gap-3">
|
||||||
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-blue-500/10 text-blue-600">
|
<div className="flex h-8 w-8 md:h-9 md:w-9 items-center justify-center rounded-xl bg-blue-500/10 text-blue-600">
|
||||||
<Repeat className="h-4 w-4" />
|
<Repeat className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="text-center md:text-left">
|
||||||
<div className="text-2xl font-semibold text-foreground">{recurringCount}</div>
|
<div className="text-xl md:text-2xl font-semibold text-foreground">{recurringCount}</div>
|
||||||
<div className="text-xs text-muted-foreground">{t("recurringTasks")}</div>
|
<div className="text-[10px] md:text-xs text-muted-foreground">{t("recurringTasks")}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ import { UserProvider } from "@/lib/user-context";
|
|||||||
const AppShellContent = ({ children }: { children: ReactNode }) => {
|
const AppShellContent = ({ children }: { children: ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
<SidebarProvider defaultOpen>
|
<SidebarProvider defaultOpen>
|
||||||
<div className="flex min-h-screen bg-muted/50 p-6">
|
<div className="flex min-h-screen bg-muted/50 p-2 md:p-6">
|
||||||
<AppSidebar />
|
<AppSidebar />
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<div className="w-full max-w-[1200px] py-6 px-6">
|
<div className="w-full max-w-[1200px] py-3 px-3 md:py-6 md:px-6">
|
||||||
<div className="grid gap-5">{children}</div>
|
<div className="grid gap-5">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
SidebarSeparator,
|
SidebarSeparator,
|
||||||
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import Avatar from "@/components/ui/avatar";
|
import Avatar from "@/components/ui/avatar";
|
||||||
import LanguageSwitcher from "@/components/LanguageSwitcher";
|
import LanguageSwitcher from "@/components/LanguageSwitcher";
|
||||||
@@ -37,6 +38,42 @@ const AppSidebar = () => {
|
|||||||
const { unreadCount } = useNotification();
|
const { unreadCount } = useNotification();
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const t = useTranslation();
|
const t = useTranslation();
|
||||||
|
const { isMobile } = useSidebar();
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<Sidebar>
|
||||||
|
<nav className="flex items-center justify-around px-1 py-1.5 safe-bottom">
|
||||||
|
{navItems.map((item) => {
|
||||||
|
const isActive = pathname?.startsWith(item.href);
|
||||||
|
const isNotifications = item.href === "/notifications";
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
className={`relative flex flex-col items-center gap-0.5 rounded-lg px-3 py-1.5 text-[10px] font-medium transition-colors ${
|
||||||
|
isActive
|
||||||
|
? "text-primary"
|
||||||
|
: "text-muted-foreground"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="relative">
|
||||||
|
<item.icon className="h-5 w-5" />
|
||||||
|
{isNotifications && unreadCount > 0 && (
|
||||||
|
<span className="absolute -right-1.5 -top-1 h-2 w-2 rounded-full bg-red-500" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span>{t(item.labelKey)}</span>
|
||||||
|
{isActive && (
|
||||||
|
<span className="absolute -bottom-1 h-0.5 w-4 rounded-full bg-primary" />
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
</Sidebar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sidebar variant="inset" className="h-[calc(100vh-3rem)] self-start bg-white">
|
<Sidebar variant="inset" className="h-[calc(100vh-3rem)] self-start bg-white">
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Slot } from "@radix-ui/react-slot";
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
import { cva, type VariantProps } from "class-variance-authority";
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
@@ -8,6 +10,7 @@ type SidebarContextValue = {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: (value: boolean) => void;
|
setOpen: (value: boolean) => void;
|
||||||
toggle: () => void;
|
toggle: () => void;
|
||||||
|
isMobile: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContextValue | null>(null);
|
const SidebarContext = React.createContext<SidebarContextValue | null>(null);
|
||||||
@@ -20,6 +23,8 @@ const useSidebar = () => {
|
|||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MOBILE_BREAKPOINT = 768;
|
||||||
|
|
||||||
const SidebarProvider = ({
|
const SidebarProvider = ({
|
||||||
children,
|
children,
|
||||||
defaultOpen = true,
|
defaultOpen = true,
|
||||||
@@ -28,9 +33,21 @@ const SidebarProvider = ({
|
|||||||
defaultOpen?: boolean;
|
defaultOpen?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [open, setOpen] = React.useState(defaultOpen);
|
const [open, setOpen] = React.useState(defaultOpen);
|
||||||
|
const [isMobile, setIsMobile] = React.useState(false);
|
||||||
const toggle = React.useCallback(() => setOpen((prev) => !prev), []);
|
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 (
|
return (
|
||||||
<SidebarContext.Provider value={{ open, setOpen, toggle }}>
|
<SidebarContext.Provider value={{ open, setOpen, toggle, isMobile }}>
|
||||||
{children}
|
{children}
|
||||||
</SidebarContext.Provider>
|
</SidebarContext.Provider>
|
||||||
);
|
);
|
||||||
@@ -57,8 +74,24 @@ const sidebarVariants = cva(
|
|||||||
const Sidebar = React.forwardRef<
|
const Sidebar = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof sidebarVariants>
|
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof sidebarVariants>
|
||||||
>(({ className, variant, style, ...props }, ref) => {
|
>(({ className, variant, style, children, ...props }, ref) => {
|
||||||
const { open } = useSidebar();
|
const { open, isMobile } = useSidebar();
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"fixed bottom-0 left-0 right-0 z-50 border-t border-border/40 bg-white",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@@ -78,9 +111,16 @@ const Sidebar = React.forwardRef<
|
|||||||
Sidebar.displayName = "Sidebar";
|
Sidebar.displayName = "Sidebar";
|
||||||
|
|
||||||
const SidebarInset = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
const SidebarInset = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||||
({ className, ...props }, ref) => (
|
({ className, ...props }, ref) => {
|
||||||
<div ref={ref} className={cn("min-h-screen flex-1", className)} {...props} />
|
const { isMobile } = useSidebar();
|
||||||
)
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("min-h-screen flex-1", isMobile && "pb-16", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
SidebarInset.displayName = "SidebarInset";
|
SidebarInset.displayName = "SidebarInset";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user