Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,21 @@ const BASIC_COLUMNS: ColumnsType<NetworkEvent> = [
// dataIndex: ['Event', 'RuleName'],
// key: 'RuleName',
// },
{
title: 'User',
dataIndex: ['Event', 'User'],
key: 'User',
},
{
title: 'Time stamp',
dataIndex: ['Event', 'EventHeader', 'TimeStamp'],
key: 'TimeStamp',
},
{
title: 'Parent user',
dataIndex: ['Event', 'ParentUser'],
key: 'ParentUser',
},
// {
// title: 'Parent user',
// dataIndex: ['Event', 'ParentUser'],
// key: 'ParentUser',
// },
{
title: 'Thread id',
dataIndex: ['Event', 'EventHeader', 'ThreadId'],
Expand Down
63 changes: 47 additions & 16 deletions src/layouts/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
QuestionOutlined,
SettingOutlined,
UserOutlined,
GlobalOutlined,
} from '@ant-design/icons';
import {
CSSTransition,
Expand All @@ -33,6 +34,7 @@ import HeaderNav from './HeaderNav.tsx';
import FooterNav from './FooterNav.tsx';
import { NProgress } from '../../components';
import { PATH_LANDING } from '../../constants';
import { useTranslation } from './TranslationContext.tsx';

const { Content } = Layout;

Expand All @@ -48,39 +50,40 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
const [collapsed, setCollapsed] = useState(true);
const [navFill, setNavFill] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { setLanguage, t, loading } = useTranslation();
const location = useLocation();
const navigate = useNavigate();
const nodeRef = useRef(null);
const floatBtnRef = useRef(null);

const items: MenuProps['items'] = [
const menuItems: MenuProps['items'] = [
{
key: 'user-profile-link',
label: 'profile',
label: t('Profile'),
icon: <UserOutlined />,
},
{
key: 'user-settings-link',
label: 'settings',
label: t('Settings'),
icon: <SettingOutlined />,
},
{
key: 'user-help-link',
label: 'help center',
label: t('Help Center'),
icon: <QuestionOutlined />,
},
{
type: 'divider',
},
{
key: 'user-logout-link',
label: 'logout',
label: t('Logout'),
icon: <LogoutOutlined />,
danger: true,
onClick: () => {
message.open({
type: 'loading',
content: 'signing you out',
content: t('Signing you out'),
});

setTimeout(() => {
Expand All @@ -106,9 +109,17 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
return () => window.removeEventListener('scroll', handleScroll);
}, []);

const changeLanguage = (lang: 'en' | 'uz' | 'ru') => {
setLanguage(lang);
};

const sidebarTooltip = collapsed
? t('Expand Sidebar')
: t('Collapse Sidebar');

return (
<>
<NProgress isAnimating={isLoading} key={location.key} />
<NProgress isAnimating={isLoading || loading} key={location.key} />
<Layout
style={{
minHeight: '100vh',
Expand Down Expand Up @@ -159,7 +170,7 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
}}
>
<Flex align="center">
<Tooltip title={`${collapsed ? 'Expand' : 'Collapse'} Sidebar`}>
<Tooltip title={sidebarTooltip}>
<Button
type="text"
icon={
Expand All @@ -174,7 +185,7 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
/>
</Tooltip>
<Input.Search
placeholder="search"
placeholder={t('Search')}
style={{
width: isMobile ? '100%' : 400,
marginLeft: isMobile ? 0 : '.5rem',
Expand All @@ -183,17 +194,40 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
/>
</Flex>
<Flex align="center" gap="small">
<Tooltip title="Apps">
<Dropdown
menu={{
items: [
{
key: 'en',
label: 'English',
onClick: () => changeLanguage('en'),
},
{
key: 'uz',
label: 'Oʻzbekcha',
onClick: () => changeLanguage('uz'),
},
{
key: 'ru',
label: 'Русский',
onClick: () => changeLanguage('ru'),
},
],
}}
>
<Button icon={<GlobalOutlined />} type="text" size="large" />
</Dropdown>
<Tooltip title={t('Apps')}>
<Button icon={<AppstoreOutlined />} type="text" size="large" />
</Tooltip>
<Tooltip title="Messages">
<Tooltip title={t('Messages')}>
<Button icon={<MessageOutlined />} type="text" size="large" />
</Tooltip>
<Dropdown menu={{ items }} trigger={['click']}>
<Dropdown menu={{ items: menuItems }} trigger={['click']}>
<Flex>
<img
src="/jujutsu.jpg"
alt="user profile photo"
alt={t('User profile photo')}
height={36}
width={36}
style={{ borderRadius, objectFit: 'cover' }}
Expand Down Expand Up @@ -224,11 +258,8 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
<div
ref={nodeRef}
style={{
// backgroundColor: '#fff',
// borderRadius: borderRadius,
padding: 24,
minHeight: '100%',
// boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
}}
>
{children}
Expand Down
98 changes: 98 additions & 0 deletions src/layouts/app/TranslationContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// contexts/TranslationContext.tsx
import React, { createContext, useContext, useState, useEffect } from 'react';
import { translateText } from '../../service/translation';

type Language = 'en' | 'uz' | 'ru';

interface TranslationContextType {
language: Language;
setLanguage: (lang: Language) => void;
t: (text: string) => string;
loading: boolean;
}

const TranslationContext = createContext<TranslationContextType>({
language: 'en',
setLanguage: () => {},
t: (text) => text,
loading: false,
});

export const TranslationProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [language, setLanguage] = useState<Language>('en');
const [cache, setCache] = useState<Record<string, Record<Language, string>>>(
{}
);
const [loading, setLoading] = useState(false);

// Pre-load common translations
useEffect(() => {
const loadCommonTranslations = async () => {
setLoading(true);
const commonPhrases = [
'Profile',
'Settings',
'Help Center',
'Logout',
'Signing you out',
'Search',
'Expand Sidebar',
'Collapse Sidebar',
'Apps',
'Messages',
];

for (const phrase of commonPhrases) {
await t(phrase);
}
setLoading(false);
};

loadCommonTranslations();
}, [language]);

const t = async (text: string): Promise<string> => {
if (!text.trim()) return text;

// Check cache first
if (cache[text]?.[language]) {
return cache[text][language];
}

// If not in cache, translate
const translatedText = await translateText(text, language);

// Update cache
setCache((prev) => ({
...prev,
[text]: {
...prev[text],
[language]: translatedText,
},
}));

return translatedText;
};

// Synchronous version that uses cached translations
const tSync = (text: string): string => {
return cache[text]?.[language] || text;
};

return (
<TranslationContext.Provider
value={{
language,
setLanguage,
t: tSync,
loading,
}}
>
{children}
</TranslationContext.Provider>
);
};

export const useTranslation = () => useContext(TranslationContext);
3 changes: 3 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
// import { TranslationProvider } from './layouts/app/TranslationContext.tsx';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
{/* <TranslationProvider> */}
<App />
{/* </TranslationProvider> */}
</React.StrictMode>
);
Loading
Loading