Spaces:
Build error
Build error
| /* | |
| Copyright (C) 2025 QuantumNous | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU Affero General Public License as | |
| published by the Free Software Foundation, either version 3 of the | |
| License, or (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU Affero General Public License for more details. | |
| You should have received a copy of the GNU Affero General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| For commercial licensing, please contact support@quantumnous.com | |
| */ | |
| import HeaderBar from './headerbar'; | |
| import { Layout } from '@douyinfe/semi-ui'; | |
| import SiderBar from './SiderBar'; | |
| import App from '../../App'; | |
| import FooterBar from './Footer'; | |
| import { ToastContainer } from 'react-toastify'; | |
| import React, { useContext, useEffect, useState } from 'react'; | |
| import { useIsMobile } from '../../hooks/common/useIsMobile'; | |
| import { useSidebarCollapsed } from '../../hooks/common/useSidebarCollapsed'; | |
| import { useTranslation } from 'react-i18next'; | |
| import { | |
| API, | |
| getLogo, | |
| getSystemName, | |
| showError, | |
| setStatusData, | |
| } from '../../helpers'; | |
| import { UserContext } from '../../context/User'; | |
| import { StatusContext } from '../../context/Status'; | |
| import { useLocation } from 'react-router-dom'; | |
| const { Sider, Content, Header } = Layout; | |
| const PageLayout = () => { | |
| const [, userDispatch] = useContext(UserContext); | |
| const [, statusDispatch] = useContext(StatusContext); | |
| const isMobile = useIsMobile(); | |
| const [collapsed, , setCollapsed] = useSidebarCollapsed(); | |
| const [drawerOpen, setDrawerOpen] = useState(false); | |
| const { i18n } = useTranslation(); | |
| const location = useLocation(); | |
| const shouldHideFooter = | |
| location.pathname.startsWith('/console') || | |
| location.pathname === '/pricing'; | |
| const shouldInnerPadding = | |
| location.pathname.includes('/console') && | |
| !location.pathname.startsWith('/console/chat') && | |
| location.pathname !== '/console/playground'; | |
| const isConsoleRoute = location.pathname.startsWith('/console'); | |
| const showSider = isConsoleRoute && (!isMobile || drawerOpen); | |
| useEffect(() => { | |
| if (isMobile && drawerOpen && collapsed) { | |
| setCollapsed(false); | |
| } | |
| }, [isMobile, drawerOpen, collapsed, setCollapsed]); | |
| const loadUser = () => { | |
| let user = localStorage.getItem('user'); | |
| if (user) { | |
| let data = JSON.parse(user); | |
| userDispatch({ type: 'login', payload: data }); | |
| } | |
| }; | |
| const loadStatus = async () => { | |
| try { | |
| const res = await API.get('/api/status'); | |
| const { success, data } = res.data; | |
| if (success) { | |
| statusDispatch({ type: 'set', payload: data }); | |
| setStatusData(data); | |
| } else { | |
| showError('Unable to connect to server'); | |
| } | |
| } catch (error) { | |
| showError('Failed to load status'); | |
| } | |
| }; | |
| useEffect(() => { | |
| loadUser(); | |
| loadStatus().catch(console.error); | |
| let systemName = getSystemName(); | |
| if (systemName) { | |
| document.title = systemName; | |
| } | |
| let logo = getLogo(); | |
| if (logo) { | |
| let linkElement = document.querySelector("link[rel~='icon']"); | |
| if (linkElement) { | |
| linkElement.href = logo; | |
| } | |
| } | |
| const savedLang = localStorage.getItem('i18nextLng'); | |
| if (savedLang) { | |
| i18n.changeLanguage(savedLang); | |
| } | |
| }, [i18n]); | |
| return ( | |
| <Layout | |
| style={{ | |
| height: '100vh', | |
| display: 'flex', | |
| flexDirection: 'column', | |
| overflow: isMobile ? 'visible' : 'hidden', | |
| }} | |
| > | |
| <Header | |
| style={{ | |
| padding: 0, | |
| height: 'auto', | |
| lineHeight: 'normal', | |
| position: 'fixed', | |
| width: '100%', | |
| top: 0, | |
| zIndex: 100, | |
| }} | |
| > | |
| <HeaderBar | |
| onMobileMenuToggle={() => setDrawerOpen((prev) => !prev)} | |
| drawerOpen={drawerOpen} | |
| /> | |
| </Header> | |
| <Layout | |
| style={{ | |
| overflow: isMobile ? 'visible' : 'auto', | |
| display: 'flex', | |
| flexDirection: 'column', | |
| }} | |
| > | |
| {showSider && ( | |
| <Sider | |
| style={{ | |
| position: 'fixed', | |
| left: 0, | |
| top: '64px', | |
| zIndex: 99, | |
| border: 'none', | |
| paddingRight: '0', | |
| height: 'calc(100vh - 64px)', | |
| width: 'var(--sidebar-current-width)', | |
| }} | |
| > | |
| <SiderBar | |
| onNavigate={() => { | |
| if (isMobile) setDrawerOpen(false); | |
| }} | |
| /> | |
| </Sider> | |
| )} | |
| <Layout | |
| style={{ | |
| marginLeft: isMobile | |
| ? '0' | |
| : showSider | |
| ? 'var(--sidebar-current-width)' | |
| : '0', | |
| flex: '1 1 auto', | |
| display: 'flex', | |
| flexDirection: 'column', | |
| }} | |
| > | |
| <Content | |
| style={{ | |
| flex: '1 0 auto', | |
| overflowY: isMobile ? 'visible' : 'hidden', | |
| WebkitOverflowScrolling: 'touch', | |
| padding: shouldInnerPadding ? (isMobile ? '5px' : '24px') : '0', | |
| position: 'relative', | |
| }} | |
| > | |
| <App /> | |
| </Content> | |
| {!shouldHideFooter && ( | |
| <Layout.Footer | |
| style={{ | |
| flex: '0 0 auto', | |
| width: '100%', | |
| }} | |
| > | |
| <FooterBar /> | |
| </Layout.Footer> | |
| )} | |
| </Layout> | |
| </Layout> | |
| <ToastContainer /> | |
| </Layout> | |
| ); | |
| }; | |
| export default PageLayout; | |