From 42031ea3604a3eb70869e02b2db7734017980404 Mon Sep 17 00:00:00 2001 From: danieleckoski Date: Mon, 20 Apr 2026 22:55:11 +0200 Subject: [PATCH 1/2] side navigation bar and responsive design --- frontend/src/App.jsx | 313 +++++++++++++++--- frontend/src/features/agent/AgentChat.jsx | 41 +++ .../features/csvtickets/CSVTicketTable.jsx | 59 +++- .../src/features/settings/SettingsPage.jsx | 18 +- .../src/features/workbench/RunsSidePanel.jsx | 10 + .../src/features/workbench/WorkbenchPage.jsx | 50 ++- .../src/features/workflow/WorkflowPage.jsx | 79 +++-- 7 files changed, 477 insertions(+), 93 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a159a6a..f280c1b 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -8,15 +8,17 @@ */ import { + Button, makeStyles, + Subtitle2, Subtitle1, - Tab, - TabList, Text, tokens, } from '@fluentui/react-components' import { Bot24Regular, + ChevronLeft24Regular, + ChevronRight24Regular, DataHistogram24Regular, DocumentEdit24Regular, Flow24Regular, @@ -43,31 +45,174 @@ import SettingsPage from './features/settings/SettingsPage' import useTabPreferences from './features/settings/useTabPreferences' import { listWorkbenchAgents } from './services/api' +const NAV_COLLAPSED_STORAGE_KEY = 'app-nav-collapsed' + const useStyles = makeStyles({ app: { minHeight: '100vh', backgroundColor: tokens.colorNeutralBackground3, + overflowX: 'hidden', + display: 'flex', + flexDirection: 'column', }, header: { backgroundColor: tokens.colorBrandBackground, color: tokens.colorNeutralForegroundOnBrand, padding: `${tokens.spacingVerticalL} ${tokens.spacingHorizontalXL}`, boxShadow: tokens.shadow4, + '@media (max-width: 768px)': { + padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalL}`, + }, + '@media (max-width: 480px)': { + padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalM}`, + }, + }, + headerInner: { + maxWidth: '1400px', + margin: '0 auto', }, title: { color: tokens.colorNeutralForegroundOnBrand, + overflowWrap: 'anywhere', }, subtitle: { color: tokens.colorNeutralForegroundOnBrand, opacity: 0.9, marginTop: tokens.spacingVerticalXS, + overflowWrap: 'anywhere', }, - nav: { + shell: { + display: 'flex', + flex: 1, + minHeight: 0, + }, + sidebar: { + width: '280px', + flexShrink: 0, + display: 'flex', + flexDirection: 'column', backgroundColor: tokens.colorNeutralBackground1, + borderRight: `1px solid ${tokens.colorNeutralStroke1}`, + transitionDuration: tokens.durationNormal, + transitionProperty: 'width', + transitionTimingFunction: tokens.curveEasyEase, + minHeight: 0, + '@media (max-width: 768px)': { + width: '232px', + }, + }, + sidebarCollapsed: { + width: '88px', + '@media (max-width: 768px)': { + width: '72px', + }, + }, + sidebarHeader: { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + gap: tokens.spacingHorizontalS, + padding: `${tokens.spacingVerticalL} ${tokens.spacingHorizontalM}`, borderBottom: `1px solid ${tokens.colorNeutralStroke1}`, - padding: `0 ${tokens.spacingHorizontalXL}`, + }, + sidebarTitle: { + minWidth: 0, + overflow: 'hidden', + }, + collapseButton: { + minWidth: '36px', + width: '36px', + height: '36px', + padding: 0, + flexShrink: 0, + }, + navList: { + display: 'flex', + flexDirection: 'column', + gap: tokens.spacingVerticalXS, + padding: tokens.spacingHorizontalS, + overflowY: 'auto', + flex: 1, + minHeight: 0, + }, + navButton: { + width: '100%', + display: 'flex', + alignItems: 'center', + gap: tokens.spacingHorizontalM, + border: 'none', + borderRadius: tokens.borderRadiusLarge, + backgroundColor: 'transparent', + color: tokens.colorNeutralForeground2, + cursor: 'pointer', + padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`, + textAlign: 'left', + transitionDuration: tokens.durationNormal, + transitionProperty: 'background-color, color', + transitionTimingFunction: tokens.curveEasyEase, + ':hover': { + backgroundColor: tokens.colorNeutralBackground1Hover, + color: tokens.colorNeutralForeground1, + }, + }, + navButtonCollapsed: { + justifyContent: 'center', + padding: `${tokens.spacingVerticalS} 0`, + }, + navButtonActive: { + backgroundColor: tokens.colorBrandBackground2, + color: tokens.colorBrandForeground1, + boxShadow: `inset 3px 0 0 ${tokens.colorBrandStroke1}`, + ':hover': { + backgroundColor: tokens.colorBrandBackground2Hover, + color: tokens.colorBrandForeground1, + }, + }, + navIcon: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + flexShrink: 0, + }, + navLabelWrap: { + minWidth: 0, + display: 'flex', + flexDirection: 'column', + gap: '2px', + }, + navLabel: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, + navPath: { + color: tokens.colorNeutralForeground4, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, + contentArea: { + flex: 1, + minWidth: 0, + minHeight: 0, + display: 'flex', + flexDirection: 'column', }, content: { + width: '100%', + flex: 1, + minWidth: 0, + minHeight: 0, + padding: `${tokens.spacingVerticalL} ${tokens.spacingHorizontalXL}`, + '@media (max-width: 768px)': { + padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalL}`, + }, + '@media (max-width: 480px)': { + padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalM}`, + }, + }, + contentInner: { + width: '100%', maxWidth: '1400px', margin: '0 auto', }, @@ -77,8 +222,24 @@ export default function App() { const styles = useStyles() const location = useLocation() const navigate = useNavigate() + const [isNavCollapsed, setIsNavCollapsed] = useState(false) const [menuAgents, setMenuAgents] = useState([]) + useEffect(() => { + const storedValue = window.localStorage.getItem(NAV_COLLAPSED_STORAGE_KEY) + if (storedValue !== null) { + setIsNavCollapsed(storedValue === 'true') + return + } + if (window.innerWidth <= 1024) { + setIsNavCollapsed(true) + } + }, []) + + useEffect(() => { + window.localStorage.setItem(NAV_COLLAPSED_STORAGE_KEY, String(isNavCollapsed)) + }, [isNavCollapsed]) + useEffect(() => { listWorkbenchAgents() .then((data) => { @@ -136,63 +297,105 @@ export default function App() { ?? allTabs.find((tab) => location.pathname.startsWith(tab.path))?.value ?? 'csvtickets' + const mainNavTabs = navTabs.filter((tab) => tab.value !== 'settings') + + const renderNavButton = (tab) => { + const isActive = activeTab === tab.value + return ( + + ) + } + return (
- CSV Ticket Viewer - - View and filter ticket data from CSV exports - +
+ CSV Ticket Viewer + + View and filter ticket data from CSV exports + +
- - -
- - } /> - } /> - } /> - {USECASE_DEMO_DEFINITIONS.map((definition) => ( - } - /> - ))} - } /> - } /> - } /> - {menuAgents.map((agent) => ( - } +
+
+
+ + + + +
+
+
+ + } /> + } /> + } /> + {USECASE_DEMO_DEFINITIONS.map((definition) => ( + } + /> + ))} + } /> + } /> + } /> + {menuAgents.map((agent) => ( + } + /> + ))} + } /> + } /> + } /> + } /> + } /> + +
+
+
+ ) } diff --git a/frontend/src/features/agent/AgentChat.jsx b/frontend/src/features/agent/AgentChat.jsx index b17fdb4..ab6158f 100644 --- a/frontend/src/features/agent/AgentChat.jsx +++ b/frontend/src/features/agent/AgentChat.jsx @@ -41,6 +41,12 @@ const useStyles = makeStyles({ height: '100%', display: 'flex', flexDirection: 'column', + '@media (max-width: 768px)': { + padding: tokens.spacingVerticalM, + }, + '@media (max-width: 480px)': { + padding: tokens.spacingVerticalS, + }, }, card: { maxWidth: '1200px', @@ -49,17 +55,27 @@ const useStyles = makeStyles({ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 200px)', + minHeight: '480px', + '@media (max-width: 768px)': { + height: 'calc(100vh - 160px)', + }, + '@media (max-width: 480px)': { + height: 'calc(100vh - 120px)', + minHeight: '420px', + }, }, header: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: tokens.spacingHorizontalM, + flexWrap: 'wrap', }, headerControls: { display: 'flex', gap: tokens.spacingHorizontalS, alignItems: 'center', + flexWrap: 'wrap', }, messagesContainer: { flex: 1, @@ -69,12 +85,19 @@ const useStyles = makeStyles({ flexDirection: 'column', gap: tokens.spacingVerticalM, backgroundColor: tokens.colorNeutralBackground1, + '@media (max-width: 480px)': { + padding: tokens.spacingVerticalM, + }, }, message: { display: 'flex', gap: tokens.spacingHorizontalM, alignItems: 'flex-start', maxWidth: '85%', + '@media (max-width: 640px)': { + maxWidth: '100%', + gap: tokens.spacingHorizontalS, + }, }, userMessage: { alignSelf: 'flex-end', @@ -91,6 +114,10 @@ const useStyles = makeStyles({ height: '40px', borderRadius: tokens.borderRadiusCircular, flexShrink: 0, + '@media (max-width: 480px)': { + width: '32px', + height: '32px', + }, }, userIcon: { backgroundColor: tokens.colorBrandBackground, @@ -165,14 +192,27 @@ const useStyles = makeStyles({ display: 'flex', gap: tokens.spacingHorizontalM, backgroundColor: tokens.colorNeutralBackground1, + '@media (max-width: 640px)': { + flexDirection: 'column', + padding: tokens.spacingVerticalM, + gap: tokens.spacingVerticalS, + }, }, inputField: { flex: 1, }, + sendButton: { + '@media (max-width: 640px)': { + width: '100%', + }, + }, emptyState: { textAlign: 'center', padding: tokens.spacingVerticalXXXL, color: tokens.colorNeutralForeground3, + '@media (max-width: 480px)': { + padding: tokens.spacingVerticalXL, + }, }, errorCard: { backgroundColor: tokens.colorPaletteRedBackground1, @@ -386,6 +426,7 @@ export default function AgentChat() { />