diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a159a6a..56c85b7 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -8,23 +8,25 @@ */ import { - makeStyles, - Subtitle1, - Tab, - TabList, - Text, - tokens, + Button, + makeStyles, + Subtitle1, + Subtitle2, + Text, + tokens, } from '@fluentui/react-components' import { - Bot24Regular, - DataHistogram24Regular, - DocumentEdit24Regular, - Flow24Regular, - Info24Regular, - Pulse24Regular, - Settings24Regular, - Table24Regular, - Wrench24Regular, + Bot24Regular, + ChevronLeft24Regular, + ChevronRight24Regular, + DataHistogram24Regular, + DocumentEdit24Regular, + Flow24Regular, + Info24Regular, + Pulse24Regular, + Settings24Regular, + Table24Regular, + Wrench24Regular, } from '@fluentui/react-icons' import { useEffect, useMemo, useState } from 'react' import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom' @@ -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() { />