feat: ajustar layout abaixo do banner do Hub e ocultar targa no Modo Smart

This commit is contained in:
2026-09-24 19:21:21 +00:00
parent bfb2464c03
commit bdf46437af
7 changed files with 110 additions and 5 deletions
+3
View File
@@ -5,6 +5,7 @@ import { ProtectedRoute } from './ProtectedRoute';
import { ProtectedAdminRoute } from './ProtectedAdminRoute';
import { ProtectedRouteByResource } from './ProtectedRouteByResource';
import { routeConfig, RouteDefinition } from '@/config/routeConfig';
import { useHubBanner } from '@/hooks/useHubBanner';
// ============================================================================
// Fallback de loading para Suspense
@@ -84,6 +85,8 @@ function wrapWithGuard(
// ============================================================================
export function AppRoutes() {
useHubBanner();
const routes = useMemo(
() =>
routeConfig.map((route) => {
+2 -2
View File
@@ -26,9 +26,9 @@ export const Layout = ({ children }: LayoutProps) => {
return (
<SidebarProvider>
<div className="min-h-screen flex w-full">
<div className="min-h-[calc(100vh-var(--hub-banner-height,0px))] flex w-full">
<AppSidebar />
<main className="flex-1 overflow-hidden flex flex-col">
<main className="flex-1 overflow-hidden flex flex-col h-[calc(100vh-var(--hub-banner-height,0px))]">
{/* Header fixo com SidebarTrigger, Modo Smart e ThemeToggle */}
<header className="h-14 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 flex items-center justify-between px-4 shrink-0">
<div className="flex items-center">
+11 -2
View File
@@ -202,6 +202,8 @@ const Sidebar = React.forwardRef<
style={
{
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
top: "var(--hub-banner-height, 0px)",
height: "calc(100vh - var(--hub-banner-height, 0px))",
} as React.CSSProperties
}
side={side}
@@ -224,17 +226,20 @@ const Sidebar = React.forwardRef<
{/* This is what handles the sidebar gap on desktop */}
<div
className={cn(
"duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] ease-linear",
"duration-200 relative w-[--sidebar-width] bg-transparent transition-[width] ease-linear",
"group-data-[collapsible=offcanvas]:w-0",
"group-data-[side=right]:rotate-180",
variant === "floating" || variant === "inset"
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]"
)}
style={{
height: "calc(100svh - var(--hub-banner-height, 0px))",
}}
/>
<div
className={cn(
"duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex",
"duration-200 fixed bottom-0 z-10 hidden w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex",
side === "left"
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
@@ -244,6 +249,10 @@ const Sidebar = React.forwardRef<
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l",
className
)}
style={{
top: "var(--hub-banner-height, 0px)",
height: "calc(100svh - var(--hub-banner-height, 0px))",
}}
{...props}
>
<div
+50
View File
@@ -0,0 +1,50 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
/**
* Hook para gerenciar e compensar a presença do cabeçalho / targa do BrainSteel Hub.
* - Detecta se o banner do SDK (#brainsteel-guard-banner) está ativo.
* - Sincroniza a variável CSS --hub-banner-height para que sidebars e layouts se ajustem.
* - Oculta automaticamente o banner quando no Modo Smart (/smart).
*/
export function useHubBanner() {
const location = useLocation();
const isSmartMode = location.pathname.startsWith('/smart');
useEffect(() => {
function updateBannerState() {
const banner = document.getElementById('brainsteel-guard-banner');
const isBannerPresent = Boolean(banner);
if (isSmartMode) {
document.body.classList.add('modo-smart-active');
document.body.classList.remove('has-hub-banner');
document.documentElement.style.setProperty('--hub-banner-height', '0px');
document.body.style.paddingTop = '0px';
} else if (isBannerPresent) {
document.body.classList.remove('modo-smart-active');
document.body.classList.add('has-hub-banner');
document.documentElement.style.setProperty('--hub-banner-height', '34px');
document.body.style.paddingTop = '34px';
} else {
document.body.classList.remove('modo-smart-active');
document.body.classList.remove('has-hub-banner');
document.documentElement.style.setProperty('--hub-banner-height', '0px');
document.body.style.paddingTop = '0px';
}
}
updateBannerState();
// Observa inserções dinâmicas do banner pelo script SDK do Hub
const observer = new MutationObserver(() => {
updateBannerState();
});
observer.observe(document.body, { childList: true, subtree: false });
return () => {
observer.disconnect();
};
}, [isSmartMode]);
}
+26
View File
@@ -41,6 +41,32 @@
--chart-5: 340 75% 55%;
--radius: 0.5rem;
--hub-banner-height: 0px;
}
body:has(#brainsteel-guard-banner) {
--hub-banner-height: 34px;
}
body.has-hub-banner {
--hub-banner-height: 34px;
}
/* Modo Smart: oculta completamente a targa do BrainSteel Hub */
body.modo-smart-active #brainsteel-guard-banner,
body.hide-hub-banner #brainsteel-guard-banner {
display: none !important;
visibility: hidden !important;
height: 0 !important;
min-height: 0 !important;
opacity: 0 !important;
pointer-events: none !important;
}
body.modo-smart-active,
body.hide-hub-banner {
padding-top: 0px !important;
--hub-banner-height: 0px !important;
}
.light {
+17
View File
@@ -73,6 +73,23 @@ export const ModoSmart: React.FC = () => {
carregarProducaoHoje();
}, [carregarProducaoHoje]);
// Modo Smart: Oculta a targa do BrainSteel Hub para maximizar a área de trabalho
useEffect(() => {
document.body.classList.add('modo-smart-active');
document.documentElement.style.setProperty('--hub-banner-height', '0px');
document.body.style.paddingTop = '0px';
return () => {
document.body.classList.remove('modo-smart-active');
const banner = document.getElementById('brainsteel-guard-banner');
if (banner) {
document.body.classList.add('has-hub-banner');
document.documentElement.style.setProperty('--hub-banner-height', '34px');
document.body.style.paddingTop = '34px';
}
};
}, []);
const handleSelectObra = (of: OFAtiva) => {
setObraAtiva(of);
setTelaAtiva('hub');
+1 -1
View File
@@ -501,7 +501,7 @@ export default function Visualizador3D() {
const hasSavedModel = Boolean(currentOFInfo?.ifc_url);
return (
<div className="flex flex-col h-[calc(100vh-4rem)] p-4 md:p-6 space-y-4 max-w-[1920px] mx-auto overflow-hidden">
<div className="flex flex-col flex-1 min-h-0 p-4 md:p-6 space-y-4 max-w-[1920px] mx-auto overflow-hidden">
{/* Top Header Bar */}
<div className="flex flex-wrap items-center justify-between gap-4 p-4 bg-white dark:bg-slate-900/90 backdrop-blur-md border border-slate-200 dark:border-slate-800 rounded-2xl shadow-lg dark:shadow-xl">
{/* Left: Title, OF Selector & Phase Filter */}