mirror of
https://github.com/dpup/meshstream.git
synced 2026-08-06 16:53:09 +02:00
Debugging prod failures
This commit is contained in:
+117
-37
@@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { ConnectionStatus } from "./ConnectionStatus";
|
||||
import { Separator } from "./Separator";
|
||||
import { Button } from "./ui/Button";
|
||||
import { SITE_TITLE } from "../lib/config";
|
||||
import {
|
||||
Info,
|
||||
@@ -11,6 +12,8 @@ import {
|
||||
MessageSquare,
|
||||
Map,
|
||||
LucideIcon,
|
||||
Menu,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
|
||||
// Define navigation item structure
|
||||
@@ -60,47 +63,124 @@ const navigationItems: NavItem[] = [
|
||||
];
|
||||
|
||||
export const Nav: React.FC<NavProps> = ({ connectionStatus }) => {
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const [isMobileView, setIsMobileView] = useState(false);
|
||||
|
||||
// Handle window resize to determine if we're in mobile view
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setIsMobileView(window.innerWidth < 768);
|
||||
if (window.innerWidth >= 768) {
|
||||
setIsMobileMenuOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Set initial state
|
||||
handleResize();
|
||||
|
||||
// Add event listener
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
// Clean up
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
// Navigation links component to avoid duplication
|
||||
const NavLinks = () => (
|
||||
<ul className="space-y-1">
|
||||
{navigationItems.map((item) => (
|
||||
<li key={item.to}>
|
||||
<Link
|
||||
to={item.to}
|
||||
className="flex items-center px-4 py-2.5 transition-colors"
|
||||
onClick={() => isMobileView && setIsMobileMenuOpen(false)}
|
||||
inactiveProps={{
|
||||
className:
|
||||
"text-neutral-400 hover:text-neutral-200 font-thin",
|
||||
}}
|
||||
activeProps={{
|
||||
className: "text-neutral-200 font-normal",
|
||||
}}
|
||||
activeOptions={{
|
||||
exact: item.exact,
|
||||
}}
|
||||
>
|
||||
<item.icon className="h-4 w-4 mr-3" />
|
||||
{item.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
||||
return (
|
||||
<aside className="w-64 text-neutral-100 h-screen fixed left-0 top-0 flex flex-col">
|
||||
{/* Logo and title section */}
|
||||
<div className="p-4 flex items-center">
|
||||
<div className="bg-pink-400 rounded-md mr-3 p-1.5 flex items-center justify-center">
|
||||
<Layers className="h-5 w-5 text-neutral-800" />
|
||||
<>
|
||||
{/* Mobile header (always visible on mobile) */}
|
||||
<div className="md:hidden fixed top-0 left-0 right-0 z-20 border-neutral-700 border-b shadow-inner bg-neutral-800/40">
|
||||
<div className="flex items-center justify-between p-3">
|
||||
<div className="flex items-center">
|
||||
<div className="bg-pink-400 rounded-md mr-3 p-1.5 flex items-center justify-center">
|
||||
<Layers className="h-5 w-5 text-neutral-800" />
|
||||
</div>
|
||||
<h1 className="text-xl font-thin tracking-wide text-neutral-100">{SITE_TITLE}</h1>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
icon={isMobileMenuOpen ? X : Menu}
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
aria-label={isMobileMenuOpen ? "Close menu" : "Open menu"}
|
||||
/>
|
||||
</div>
|
||||
<h1 className="text-xl font-thin tracking-wide">{SITE_TITLE}</h1>
|
||||
</div>
|
||||
|
||||
<Separator className="mt-1" />
|
||||
{/* Mobile menu overlay */}
|
||||
{isMobileMenuOpen && (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 z-30 md:hidden"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<nav className="flex-1">
|
||||
<ul className="space-y-1">
|
||||
{navigationItems.map((item) => (
|
||||
<li key={item.to}>
|
||||
<Link
|
||||
to={item.to}
|
||||
className="flex items-center px-4 py-2.5 transition-colors"
|
||||
inactiveProps={{
|
||||
className:
|
||||
"text-neutral-400 hover:text-neutral-200 font-thin",
|
||||
}}
|
||||
activeProps={{
|
||||
className: "text-neutral-200 font-normal",
|
||||
}}
|
||||
activeOptions={{
|
||||
exact: item.exact,
|
||||
}}
|
||||
>
|
||||
<item.icon className="h-4 w-4 mr-3" />
|
||||
{item.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
{/* Mobile menu */}
|
||||
<aside
|
||||
className={`
|
||||
fixed top-0 right-0 h-screen bg-neutral-800 z-40 w-64 transform transition-transform duration-300 ease-in-out md:hidden
|
||||
${isMobileMenuOpen ? 'translate-x-0' : 'translate-x-full'}
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col h-full pt-16">
|
||||
<nav className="flex-1 overflow-y-auto">
|
||||
<NavLinks />
|
||||
</nav>
|
||||
<div className="px-4 py-2 pb-6">
|
||||
<ConnectionStatus status={connectionStatus} />
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div className="px-4 py-2 pb-6">
|
||||
<ConnectionStatus status={connectionStatus} />
|
||||
</div>
|
||||
</aside>
|
||||
{/* Desktop sidebar (hidden on mobile) */}
|
||||
<aside className="hidden md:flex w-64 text-neutral-100 h-screen fixed left-0 top-0 flex-col">
|
||||
{/* Logo and title section */}
|
||||
<div className="p-4 flex items-center">
|
||||
<div className="bg-pink-400 rounded-md mr-3 p-1.5 flex items-center justify-center">
|
||||
<Layers className="h-5 w-5 text-neutral-800" />
|
||||
</div>
|
||||
<h1 className="text-xl font-thin tracking-wide">{SITE_TITLE}</h1>
|
||||
</div>
|
||||
|
||||
<Separator className="mt-1" />
|
||||
|
||||
<nav className="flex-1 overflow-y-auto">
|
||||
<NavLinks />
|
||||
</nav>
|
||||
|
||||
<div className="px-4 py-2 pb-6">
|
||||
<ConnectionStatus status={connectionStatus} />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Content padding spacer for mobile view - this will be positioned outside the main content area */}
|
||||
<div className="md:hidden h-16"></div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ interface PageWrapperProps {
|
||||
*/
|
||||
export const PageWrapper: React.FC<PageWrapperProps> = ({ children }) => {
|
||||
return (
|
||||
<div className="bg-neutral-50/5 flex-1 h-[calc(100vh-3rem)] overflow-y-auto rounded-tl-3xl rounded-bl-3xl p-4 shadow-md">
|
||||
<div className="flex-1 h-[calc(100vh-6rem)] md:h-[calc(100vh-3rem)] overflow-y-auto md:rounded-tl-3xl md:rounded-bl-3xl p-4 shadow-md">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cn } from "../../lib/cn";
|
||||
|
||||
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
/** Button content */
|
||||
children: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
/** Optional icon to display before the text */
|
||||
icon?: LucideIcon;
|
||||
/** Button variant */
|
||||
|
||||
@@ -82,9 +82,9 @@ function RootLayout() {
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className="flex h-screen overflow-hidden bg-neutral-900">
|
||||
<div className="flex h-screen overflow-hidden bg-neutral-800">
|
||||
<Nav connectionStatus={connectionStatus} />
|
||||
<main className="ml-64 flex-1 py-6 overflow-hidden flex flex-col">
|
||||
<main className="md:ml-64 flex-1 pt-16 md:pt-6 pb-2 md:py-6 overflow-hidden flex flex-col bg-neutral-50/5">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Set default background and text colors and font family */
|
||||
@layer base {
|
||||
html, body {
|
||||
@apply bg-neutral-800;
|
||||
@apply bg-neutral-700;
|
||||
@apply text-neutral-200;
|
||||
@apply font-sans;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user