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"; import { useAgentStore } from "@/app/providers/layout/store/agent.store";
export const Layout = ({ children }: { children: ReactNode }) => { 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 { 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(() => { useEffect(() => {
fetchAgents(); fetchAgents();
}, [fetchAgents]); }, [fetchAgents]);
@@ -20,10 +45,17 @@ export const Layout = ({ children }: { children: ReactNode }) => {
}, [fetchAgents]); }, [fetchAgents]);
return ( return (
<div className="flex h-screen overflow-hidden" style={{ backgroundColor: "var(--bg-primary)" }}> <div
<Sidebar isOpen={isOpen} onToggle={() => setOpen(!isOpen)} /> 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"> <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 className="flex-1 overflow-auto p-4">{children}</div>
</div> </div>
</div> </div>
@@ -1,6 +1,6 @@
import { useState, useRef, useEffect } from "react"; import { useState, useRef, useEffect } from "react";
import { useNavigate, useLocation } from "react-router-dom"; import { useNavigate, useLocation } from "react-router-dom";
import { FaCode, FaChevronDown } from "react-icons/fa"; import { FaBars, FaCode, FaChevronDown } from "react-icons/fa";
import { import {
FaHome, FaHome,
FaServer, FaServer,
@@ -21,7 +21,15 @@ import {
getCurrentTheme, getCurrentTheme,
} from "@/modules/theme-changer/utils/apply.theme"; } 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 navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const { user, logout } = useAuthStore(); const { user, logout } = useAuthStore();
@@ -75,6 +83,22 @@ export const Navigation = () => {
}} }}
> >
<div className="flex items-center justify-between px-4 py-2.5"> <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 flex-1 mx-4 overflow-x-auto scrollbar-hide">
<div className="flex items-center gap-1 whitespace-nowrap"> <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 { import {
FaBars, FaBars,
FaMicrochip, FaMicrochip,
@@ -21,11 +21,13 @@ import { adminApi } from "@/modules/admin/api/admin.api";
interface SidebarProps { interface SidebarProps {
isOpen?: boolean; isOpen?: boolean;
onToggle?: () => void; onToggle?: () => void;
isMobile?: boolean;
} }
export const Sidebar: React.FC<SidebarProps> = ({ export const Sidebar: React.FC<SidebarProps> = ({
isOpen = true, isOpen = true,
onToggle, onToggle,
isMobile = false,
}) => { }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const { agents, isLoading, error, fetchAgents, removeAgent } = const { agents, isLoading, error, fetchAgents, removeAgent } =
@@ -35,10 +37,26 @@ export const Sidebar: React.FC<SidebarProps> = ({
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const [showTokenModal, setShowTokenModal] = useState(false); const [showTokenModal, setShowTokenModal] = useState(false);
const [showGraphs, setShowGraphs] = useState(false); const [showGraphs, setShowGraphs] = useState(false);
const [sidebarWidth, setSidebarWidth] = useState(288);
const sidebarRef = useRef<HTMLDivElement>(null);
const [expandedAgents, setExpandedAgents] = useState<Set<string>>( const [expandedAgents, setExpandedAgents] = useState<Set<string>>(
new Set(agents.map((a) => a.label)), 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 // Token generation state
const [tokenLabel, setTokenLabel] = useState(""); const [tokenLabel, setTokenLabel] = useState("");
const [generatedToken, setGeneratedToken] = useState<string | null>(null); const [generatedToken, setGeneratedToken] = useState<string | null>(null);
@@ -132,35 +150,21 @@ export const Sidebar: React.FC<SidebarProps> = ({
}; };
if (!isOpen) { if (!isOpen) {
return ( return null;
<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 ( return (
<> <>
{/* Overlay для мобильных */} {/* Overlay — только на мобильных (< 856px) */}
<div {isMobile && (
className="fixed inset-0 bg-black/50 z-40 md:hidden" <div className="fixed inset-0 bg-black/50 z-40" onClick={onToggle} />
onClick={onToggle} )}
/>
<aside <aside
className={`fixed md:relative z-50 transition-all duration-300 ease-in-out flex flex-col ${ ref={sidebarRef}
isOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0" className={`${isMobile ? "fixed" : "relative"} z-50 transition-all duration-300 ease-in-out flex flex-col`}
}`}
style={{ style={{
width: showGraphs ? "500px" : "288px", width: `${sidebarWidth}px`,
height: "100vh", height: "100vh",
backgroundColor: "var(--card-bg)", backgroundColor: "var(--card-bg)",
borderRight: "1px solid var(--border)", borderRight: "1px solid var(--border)",
@@ -191,8 +195,9 @@ export const Sidebar: React.FC<SidebarProps> = ({
</div> </div>
<button <button
onClick={onToggle} onClick={onToggle}
className="p-1 rounded transition-colors md:hidden" className={`p-1 rounded transition-colors ${isMobile ? "" : "hidden"}`}
style={{ color: "var(--text-secondary)" }} style={{ color: "var(--text-secondary)" }}
aria-label="Закрыть sidebar"
> >
<FaTimes size={14} /> <FaTimes size={14} />
</button> </button>