feat: ajustar layout abaixo do banner do Hub e ocultar targa no Modo Smart
This commit is contained in:
@@ -5,6 +5,7 @@ import { ProtectedRoute } from './ProtectedRoute';
|
|||||||
import { ProtectedAdminRoute } from './ProtectedAdminRoute';
|
import { ProtectedAdminRoute } from './ProtectedAdminRoute';
|
||||||
import { ProtectedRouteByResource } from './ProtectedRouteByResource';
|
import { ProtectedRouteByResource } from './ProtectedRouteByResource';
|
||||||
import { routeConfig, RouteDefinition } from '@/config/routeConfig';
|
import { routeConfig, RouteDefinition } from '@/config/routeConfig';
|
||||||
|
import { useHubBanner } from '@/hooks/useHubBanner';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Fallback de loading para Suspense
|
// Fallback de loading para Suspense
|
||||||
@@ -84,6 +85,8 @@ function wrapWithGuard(
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
export function AppRoutes() {
|
export function AppRoutes() {
|
||||||
|
useHubBanner();
|
||||||
|
|
||||||
const routes = useMemo(
|
const routes = useMemo(
|
||||||
() =>
|
() =>
|
||||||
routeConfig.map((route) => {
|
routeConfig.map((route) => {
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ export const Layout = ({ children }: LayoutProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<div className="min-h-screen flex w-full">
|
<div className="min-h-[calc(100vh-var(--hub-banner-height,0px))] flex w-full">
|
||||||
<AppSidebar />
|
<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 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">
|
<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">
|
<div className="flex items-center">
|
||||||
|
|||||||
@@ -202,6 +202,8 @@ const Sidebar = React.forwardRef<
|
|||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
|
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
|
||||||
|
top: "var(--hub-banner-height, 0px)",
|
||||||
|
height: "calc(100vh - var(--hub-banner-height, 0px))",
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
side={side}
|
side={side}
|
||||||
@@ -224,17 +226,20 @@ const Sidebar = React.forwardRef<
|
|||||||
{/* This is what handles the sidebar gap on desktop */}
|
{/* This is what handles the sidebar gap on desktop */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
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-[collapsible=offcanvas]:w-0",
|
||||||
"group-data-[side=right]:rotate-180",
|
"group-data-[side=right]:rotate-180",
|
||||||
variant === "floating" || variant === "inset"
|
variant === "floating" || variant === "inset"
|
||||||
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
|
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
|
||||||
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]"
|
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]"
|
||||||
)}
|
)}
|
||||||
|
style={{
|
||||||
|
height: "calc(100svh - var(--hub-banner-height, 0px))",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
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"
|
side === "left"
|
||||||
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
||||||
: "right-0 group-data-[collapsible=offcanvas]:right-[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",
|
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
style={{
|
||||||
|
top: "var(--hub-banner-height, 0px)",
|
||||||
|
height: "calc(100svh - var(--hub-banner-height, 0px))",
|
||||||
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
}
|
||||||
@@ -41,6 +41,32 @@
|
|||||||
--chart-5: 340 75% 55%;
|
--chart-5: 340 75% 55%;
|
||||||
|
|
||||||
--radius: 0.5rem;
|
--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 {
|
.light {
|
||||||
|
|||||||
@@ -73,6 +73,23 @@ export const ModoSmart: React.FC = () => {
|
|||||||
carregarProducaoHoje();
|
carregarProducaoHoje();
|
||||||
}, [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) => {
|
const handleSelectObra = (of: OFAtiva) => {
|
||||||
setObraAtiva(of);
|
setObraAtiva(of);
|
||||||
setTelaAtiva('hub');
|
setTelaAtiva('hub');
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ export default function Visualizador3D() {
|
|||||||
const hasSavedModel = Boolean(currentOFInfo?.ifc_url);
|
const hasSavedModel = Boolean(currentOFInfo?.ifc_url);
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* 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">
|
<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 */}
|
{/* Left: Title, OF Selector & Phase Filter */}
|
||||||
|
|||||||
Reference in New Issue
Block a user