init
This commit is contained in:
44
worklenz-frontend/src/layouts/AuthLayout.tsx
Normal file
44
worklenz-frontend/src/layouts/AuthLayout.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { ConfigProvider, Flex, Layout } from 'antd';
|
||||
import React from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { useAppSelector } from '../hooks/useAppSelector';
|
||||
import { colors } from '../styles/colors';
|
||||
|
||||
const AuthLayout = () => {
|
||||
const themeMode = useAppSelector(state => state.themeReducer.mode);
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Layout: {
|
||||
colorBgLayout: themeMode === 'dark' ? colors.darkGray : '#fafafa',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Layout
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minHeight: '100vh',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
style={{
|
||||
marginBlockStart: 96,
|
||||
marginBlockEnd: 48,
|
||||
marginInline: 24,
|
||||
width: '90%',
|
||||
maxWidth: 440,
|
||||
}}
|
||||
>
|
||||
<Outlet />
|
||||
</Flex>
|
||||
</Layout>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthLayout;
|
||||
11
worklenz-frontend/src/layouts/AuthenticatedLayout.tsx
Normal file
11
worklenz-frontend/src/layouts/AuthenticatedLayout.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { SocketProvider } from '@/socket/socketContext';
|
||||
|
||||
export const AuthenticatedLayout = () => {
|
||||
return (
|
||||
<SocketProvider>
|
||||
<Outlet />
|
||||
</SocketProvider>
|
||||
);
|
||||
};
|
||||
79
worklenz-frontend/src/layouts/MainLayout.tsx
Normal file
79
worklenz-frontend/src/layouts/MainLayout.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Col, ConfigProvider, Layout } from 'antd';
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
import Navbar from '../features/navbar/navbar';
|
||||
import { useAppSelector } from '../hooks/useAppSelector';
|
||||
import { useMediaQuery } from 'react-responsive';
|
||||
import { colors } from '../styles/colors';
|
||||
import { verifyAuthentication } from '@/features/auth/authSlice';
|
||||
import { useEffect } from 'react';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import TawkTo from '@/components/TawkTo';
|
||||
|
||||
const MainLayout = () => {
|
||||
const themeMode = useAppSelector(state => state.themeReducer.mode);
|
||||
const isDesktop = useMediaQuery({ query: '(min-width: 1024px)' });
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const verifyAuthStatus = async () => {
|
||||
const session = await dispatch(verifyAuthentication()).unwrap();
|
||||
if (!session.user.setup_completed) {
|
||||
navigate('/worklenz/setup');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
void verifyAuthStatus();
|
||||
}, [dispatch, navigate]);
|
||||
|
||||
const headerStyles = {
|
||||
zIndex: 999,
|
||||
position: 'fixed',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: 0,
|
||||
borderBottom: themeMode === 'dark' ? '1px solid #303030' : 'none',
|
||||
} as const;
|
||||
|
||||
const contentStyles = {
|
||||
paddingInline: isDesktop ? 64 : 24,
|
||||
overflowX: 'hidden',
|
||||
} as const;
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Layout: {
|
||||
colorBgLayout: themeMode === 'dark' ? colors.darkGray : colors.white,
|
||||
headerBg: themeMode === 'dark' ? colors.darkGray : colors.white,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Layout style={{ minHeight: '100vh' }}>
|
||||
<Layout.Header
|
||||
className={`shadow-md ${themeMode === 'dark' ? '' : 'shadow-[#18181811]'}`}
|
||||
style={headerStyles}
|
||||
>
|
||||
<Navbar />
|
||||
</Layout.Header>
|
||||
|
||||
<Layout.Content>
|
||||
<Col
|
||||
xxl={{ span: 18, offset: 3, flex: '100%' }}
|
||||
style={contentStyles}
|
||||
>
|
||||
<Outlet />
|
||||
</Col>
|
||||
</Layout.Content>
|
||||
{import.meta.env.VITE_APP_ENV === 'production' && (
|
||||
<TawkTo propertyId="67ecc524f62fbf190db18bde" widgetId="1inqe45sq" />
|
||||
)}
|
||||
</Layout>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainLayout;
|
||||
121
worklenz-frontend/src/layouts/ReportingLayout.tsx
Normal file
121
worklenz-frontend/src/layouts/ReportingLayout.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { Col, ConfigProvider, Layout } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Navbar from '../features/navbar/navbar';
|
||||
import { useAppSelector } from '../hooks/useAppSelector';
|
||||
import { colors } from '../styles/colors';
|
||||
import { themeWiseColor } from '../utils/themeWiseColor';
|
||||
import ReportingSider from '../pages/reporting/sidebar/reporting-sider';
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
import ReportingCollapsedButton from '../pages/reporting/sidebar/reporting-collapsed-button';
|
||||
import { useAuthService } from '@/hooks/useAuth';
|
||||
import { reportingApiService } from '@/api/reporting/reporting.api.service';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { setCurrentOrganization } from '@/features/reporting/reporting.slice';
|
||||
import logger from '@/utils/errorLogger';
|
||||
|
||||
const ReportingLayout = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [isCollapsed, setIsCollapsed] = useState<boolean>(false);
|
||||
|
||||
const themeMode = useAppSelector(state => state.themeReducer.mode);
|
||||
const { getCurrentSession } = useAuthService();
|
||||
const currentSession = getCurrentSession();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (currentSession?.is_expired) {
|
||||
navigate('/worklenz/license-expired');
|
||||
}
|
||||
}, [currentSession, navigate]);
|
||||
|
||||
// function to handle collapse
|
||||
const handleCollapsedToggler = () => {
|
||||
setIsCollapsed(prev => !prev);
|
||||
};
|
||||
|
||||
const fetchCurrentOrganization = async () => {
|
||||
try {
|
||||
const response = await reportingApiService.getInfo();
|
||||
if (response.done) {
|
||||
dispatch(setCurrentOrganization(response.body?.organization_name));
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error fetching current organization', error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchCurrentOrganization();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
wave={{ disabled: true }}
|
||||
theme={{
|
||||
components: {
|
||||
Layout: {
|
||||
siderBg: themeMode === 'dark' ? colors.darkGray : colors.white,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Layout
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<Layout.Header
|
||||
className={`shadow-md ${themeMode === 'dark' ? 'shadow-[#5f5f5f1f]' : 'shadow-[#18181811]'}`}
|
||||
style={{
|
||||
zIndex: 999,
|
||||
position: 'fixed',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<Navbar />
|
||||
</Layout.Header>
|
||||
|
||||
<Layout.Sider
|
||||
collapsed={isCollapsed}
|
||||
collapsedWidth={24}
|
||||
width={160}
|
||||
style={{
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
zIndex: 120,
|
||||
height: '100vh',
|
||||
borderInlineEnd: `1px solid ${themeWiseColor('#f0f0f0', '#303030', themeMode)}`,
|
||||
}}
|
||||
>
|
||||
{/* sidebar collapsed button */}
|
||||
<ReportingCollapsedButton
|
||||
isCollapsed={isCollapsed}
|
||||
handleCollapseToggler={handleCollapsedToggler}
|
||||
/>
|
||||
|
||||
{!isCollapsed && (
|
||||
<div style={{ width: 160 }}>
|
||||
<ReportingSider />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Layout.Sider>
|
||||
|
||||
<Layout.Content style={{ marginBlock: 96 }}>
|
||||
<Col style={{ paddingInline: 32 }}>
|
||||
<Outlet />
|
||||
</Col>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportingLayout;
|
||||
56
worklenz-frontend/src/layouts/SettingsLayout.tsx
Normal file
56
worklenz-frontend/src/layouts/SettingsLayout.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Flex, Typography } from 'antd';
|
||||
import SettingsSidebar from '../pages/settings/sidebar/settings-sidebar';
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
import { useMediaQuery } from 'react-responsive';
|
||||
import { useEffect } from 'react';
|
||||
import { useAuthService } from '@/hooks/useAuth';
|
||||
|
||||
const SettingsLayout = () => {
|
||||
const isTablet = useMediaQuery({ query: '(min-width: 768px)' });
|
||||
const { getCurrentSession } = useAuthService();
|
||||
const currentSession = getCurrentSession();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (currentSession?.is_expired) {
|
||||
navigate('/worklenz/license-expired');
|
||||
}
|
||||
}, [currentSession, navigate]);
|
||||
|
||||
return (
|
||||
<div style={{ marginBlock: 96, minHeight: '90vh' }}>
|
||||
<Typography.Title level={4}>Settings</Typography.Title>
|
||||
|
||||
{isTablet ? (
|
||||
<Flex
|
||||
gap={24}
|
||||
align="flex-start"
|
||||
style={{
|
||||
width: '100%',
|
||||
marginBlockStart: 24,
|
||||
}}
|
||||
>
|
||||
<Flex style={{ width: '100%', maxWidth: 240 }}>
|
||||
<SettingsSidebar />
|
||||
</Flex>
|
||||
<Flex style={{ width: '100%' }}>
|
||||
<Outlet />
|
||||
</Flex>
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex
|
||||
vertical
|
||||
gap={24}
|
||||
style={{
|
||||
marginBlockStart: 24,
|
||||
}}
|
||||
>
|
||||
<SettingsSidebar />
|
||||
<Outlet />
|
||||
</Flex>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsLayout;
|
||||
63
worklenz-frontend/src/layouts/admin-center-layout.tsx
Normal file
63
worklenz-frontend/src/layouts/admin-center-layout.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Flex, Typography } from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { useMediaQuery } from 'react-responsive';
|
||||
import AdminCenterSidebar from '@/pages/admin-center/sidebar/sidebar';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { verifyAuthentication } from '@/features/auth/authSlice';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
|
||||
const AdminCenterLayout: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isTablet = useMediaQuery({ query: '(min-width:768px)' });
|
||||
const isMarginAvailable = useMediaQuery({ query: '(min-width: 1000px)' });
|
||||
const { t } = useTranslation('admin-center/sidebar');
|
||||
|
||||
useEffect(() => {
|
||||
void dispatch(verifyAuthentication())
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
marginBlock: 96,
|
||||
minHeight: '90vh',
|
||||
marginLeft: `${isMarginAvailable ? '5%' : ''}`,
|
||||
marginRight: `${isMarginAvailable ? '5%' : ''}`,
|
||||
}}
|
||||
>
|
||||
<Typography.Title level={4}>{t('adminCenter')}</Typography.Title>
|
||||
|
||||
{isTablet ? (
|
||||
<Flex
|
||||
gap={24}
|
||||
align="flex-start"
|
||||
style={{
|
||||
width: '100%',
|
||||
marginBlockStart: 24,
|
||||
}}
|
||||
>
|
||||
<Flex style={{ width: '100%', maxWidth: 240 }}>
|
||||
<AdminCenterSidebar />
|
||||
</Flex>
|
||||
<Flex style={{ width: '100%' }}>
|
||||
<Outlet />
|
||||
</Flex>
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex
|
||||
vertical
|
||||
gap={24}
|
||||
style={{
|
||||
marginBlockStart: 24,
|
||||
}}
|
||||
>
|
||||
<AdminCenterSidebar />
|
||||
<Outlet />
|
||||
</Flex>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminCenterLayout;
|
||||
Reference in New Issue
Block a user