fix: adaptive #2
ci-front / build (push) Successful in 2m28s

This commit is contained in:
nikita
2026-04-05 08:04:42 +03:00
parent 5f6c4303db
commit 9f6defd25c
3 changed files with 91 additions and 30 deletions
+36 -4
View File
@@ -4,9 +4,34 @@ import { Navigation } from "@/app/providers/layout/navigation/navigation";
import { useAgentStore } from "@/app/providers/layout/store/agent.store";
export const Layout = ({ children }: { children: ReactNode }) => {
const [isOpen, setOpen] = useState(true);
const [mobileOpen, setMobileOpen] = useState(false);
const [isMobile, setIsMobile] = useState(() =>
typeof window !== "undefined" ? window.innerWidth < 856 : false,
);
const { fetchAgents } = useAgentStore();
const sidebarOpen = isMobile ? mobileOpen : true;
useEffect(() => {
const handleResize = () => {
const mobile = window.innerWidth < 856;
setIsMobile(mobile);
if (!mobile) {
setMobileOpen(false);
}
};
window.addEventListener("resize", handleResize);
handleResize();
return () => window.removeEventListener("resize", handleResize);
}, []);
const toggleSidebar = () => {
if (isMobile) {
setMobileOpen((prev) => !prev);
}
};
useEffect(() => {
fetchAgents();
}, [fetchAgents]);
@@ -20,10 +45,17 @@ export const Layout = ({ children }: { children: ReactNode }) => {
}, [fetchAgents]);
return (
<div className="flex h-screen overflow-hidden" style={{ backgroundColor: "var(--bg-primary)" }}>
<Sidebar isOpen={isOpen} onToggle={() => setOpen(!isOpen)} />
<div
className="flex h-screen overflow-hidden"
style={{ backgroundColor: "var(--bg-primary)" }}
>
<Sidebar
isOpen={sidebarOpen}
onToggle={toggleSidebar}
isMobile={isMobile}
/>
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
<Navigation />
<Navigation onToggleSidebar={toggleSidebar} isMobile={isMobile} />
<div className="flex-1 overflow-auto p-4">{children}</div>
</div>
</div>
@@ -1,6 +1,6 @@
import { useState, useRef, useEffect } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import { FaCode, FaChevronDown } from "react-icons/fa";
import { FaBars, FaCode, FaChevronDown } from "react-icons/fa";
import {
FaHome,
FaServer,
@@ -21,7 +21,15 @@ import {
getCurrentTheme,
} from "@/modules/theme-changer/utils/apply.theme";
export const Navigation = () => {
interface NavigationProps {
onToggleSidebar?: () => void;
isMobile?: boolean;
}
export const Navigation: React.FC<NavigationProps> = ({
onToggleSidebar,
isMobile,
}) => {
const navigate = useNavigate();
const location = useLocation();
const { user, logout } = useAuthStore();
@@ -75,6 +83,22 @@ export const Navigation = () => {
}}
>
<div className="flex items-center justify-between px-4 py-2.5">
{/* Бургер — только на мобильных */}
{isMobile && (
<button
onClick={onToggleSidebar}
className="p-1.5 mr-2 rounded-lg transition-colors flex-shrink-0"
style={{
backgroundColor: "transparent",
color: "var(--text-secondary)",
border: "1px solid var(--border)",
}}
aria-label="Открыть sidebar"
>
<FaBars size={14} />
</button>
)}
{/* Навигация */}
<div className="flex items-center flex-1 mx-4 overflow-x-auto scrollbar-hide">
<div className="flex items-center gap-1 whitespace-nowrap">
@@ -1,4 +1,4 @@
import React, { useMemo, useState } from "react";
import React, { useMemo, useState, useRef, useEffect } from "react";
import {
FaBars,
FaMicrochip,
@@ -21,11 +21,13 @@ import { adminApi } from "@/modules/admin/api/admin.api";
interface SidebarProps {
isOpen?: boolean;
onToggle?: () => void;
isMobile?: boolean;
}
export const Sidebar: React.FC<SidebarProps> = ({
isOpen = true,
onToggle,
isMobile = false,
}) => {
const navigate = useNavigate();
const { agents, isLoading, error, fetchAgents, removeAgent } =
@@ -35,10 +37,26 @@ export const Sidebar: React.FC<SidebarProps> = ({
const [copied, setCopied] = useState(false);
const [showTokenModal, setShowTokenModal] = useState(false);
const [showGraphs, setShowGraphs] = useState(false);
const [sidebarWidth, setSidebarWidth] = useState(288);
const sidebarRef = useRef<HTMLDivElement>(null);
const [expandedAgents, setExpandedAgents] = useState<Set<string>>(
new Set(agents.map((a) => a.label)),
);
// Рассчитываем максимальную ширину при переключении на графы
useEffect(() => {
const updateWidth = () => {
const targetWidth = showGraphs ? 500 : 288;
const maxWidth = window.innerWidth - 200;
const finalWidth = Math.min(targetWidth, maxWidth);
setSidebarWidth(Math.max(finalWidth, 250));
};
updateWidth();
window.addEventListener("resize", updateWidth);
return () => window.removeEventListener("resize", updateWidth);
}, [showGraphs]);
// Token generation state
const [tokenLabel, setTokenLabel] = useState("");
const [generatedToken, setGeneratedToken] = useState<string | null>(null);
@@ -132,35 +150,21 @@ export const Sidebar: React.FC<SidebarProps> = ({
};
if (!isOpen) {
return (
<button
onClick={onToggle}
className="fixed top-4 left-4 z-50 p-2.5 rounded-lg shadow-lg transition-colors md:hidden"
style={{
backgroundColor: "var(--accent)",
color: "var(--accent-text)",
}}
aria-label="Открыть sidebar"
>
<FaBars size={18} />
</button>
);
return null;
}
return (
<>
{/* Overlay для мобильных */}
<div
className="fixed inset-0 bg-black/50 z-40 md:hidden"
onClick={onToggle}
/>
{/* Overlay — только на мобильных (< 856px) */}
{isMobile && (
<div className="fixed inset-0 bg-black/50 z-40" onClick={onToggle} />
)}
<aside
className={`fixed md:relative z-50 transition-all duration-300 ease-in-out flex flex-col ${
isOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0"
}`}
ref={sidebarRef}
className={`${isMobile ? "fixed" : "relative"} z-50 transition-all duration-300 ease-in-out flex flex-col`}
style={{
width: showGraphs ? "500px" : "288px",
width: `${sidebarWidth}px`,
height: "100vh",
backgroundColor: "var(--card-bg)",
borderRight: "1px solid var(--border)",
@@ -191,8 +195,9 @@ export const Sidebar: React.FC<SidebarProps> = ({
</div>
<button
onClick={onToggle}
className="p-1 rounded transition-colors md:hidden"
className={`p-1 rounded transition-colors ${isMobile ? "" : "hidden"}`}
style={{ color: "var(--text-secondary)" }}
aria-label="Закрыть sidebar"
>
<FaTimes size={14} />
</button>