Fix jwt-token
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Outlet, Link, useLocation } from 'react-router-dom';
|
import { Outlet, Link, useLocation } from 'react-router-dom';
|
||||||
|
// import { useTranslation } from 'react-i18next'; // Commented out until Docker rebuild
|
||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
import { useSocket } from '../contexts/SocketContext';
|
import { useSocket } from '../contexts/SocketContext';
|
||||||
import DebugToggle from './DebugToggle';
|
import DebugToggle from './DebugToggle';
|
||||||
import LanguageSelector from './common/LanguageSelector';
|
import LanguageSelector from './common/LanguageSelector';
|
||||||
import { canAccessSettings, hasPermission } from '../utils/rbac';
|
import { canAccessSettings, hasPermission } from '../utils/rbac';
|
||||||
|
import { t } from '../utils/tempTranslations'; // Temporary translation system
|
||||||
import {
|
import {
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
MapIcon,
|
MapIcon,
|
||||||
@@ -21,20 +23,22 @@ import {
|
|||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const baseNavigation = [
|
|
||||||
{ name: 'Dashboard', href: '/', icon: HomeIcon },
|
|
||||||
{ name: 'Map View', href: '/map', icon: MapIcon },
|
|
||||||
{ name: 'Devices', href: '/devices', icon: ServerIcon },
|
|
||||||
{ name: 'Detections', href: '/detections', icon: ExclamationTriangleIcon },
|
|
||||||
{ name: 'Alerts', href: '/alerts', icon: BellIcon },
|
|
||||||
];
|
|
||||||
|
|
||||||
const Layout = () => {
|
const Layout = () => {
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
|
// const { t } = useTranslation(); // Commented out until Docker rebuild
|
||||||
const { user, logout } = useAuth();
|
const { user, logout } = useAuth();
|
||||||
const { connected, recentDetections } = useSocket();
|
const { connected, recentDetections } = useSocket();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
|
// Build navigation based on user permissions with translations
|
||||||
|
const baseNavigation = [
|
||||||
|
{ name: t('navigation.dashboard'), href: '/', icon: HomeIcon },
|
||||||
|
{ name: 'Map View', href: '/map', icon: MapIcon }, // TODO: Add to translations
|
||||||
|
{ name: t('navigation.devices'), href: '/devices', icon: ServerIcon },
|
||||||
|
{ name: t('navigation.detections'), href: '/detections', icon: ExclamationTriangleIcon },
|
||||||
|
{ name: t('navigation.alerts'), href: '/alerts', icon: BellIcon },
|
||||||
|
];
|
||||||
|
|
||||||
// Build navigation based on user permissions
|
// Build navigation based on user permissions
|
||||||
const navigation = React.useMemo(() => {
|
const navigation = React.useMemo(() => {
|
||||||
if (!user?.role) {
|
if (!user?.role) {
|
||||||
@@ -45,16 +49,16 @@ const Layout = () => {
|
|||||||
|
|
||||||
// Add Settings if user has any settings permissions
|
// Add Settings if user has any settings permissions
|
||||||
if (canAccessSettings(user.role)) {
|
if (canAccessSettings(user.role)) {
|
||||||
nav.push({ name: 'Settings', href: '/settings', icon: CogIcon });
|
nav.push({ name: t('navigation.settings'), href: '/settings', icon: CogIcon });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Debug if user has debug permissions
|
// Add Debug if user has debug permissions
|
||||||
if (hasPermission(user.role, 'debug.access')) {
|
if (hasPermission(user.role, 'debug.access')) {
|
||||||
nav.push({ name: 'Debug', href: '/debug', icon: BugAntIcon });
|
nav.push({ name: 'Debug', href: '/debug', icon: BugAntIcon }); // TODO: Add to translations
|
||||||
}
|
}
|
||||||
|
|
||||||
return nav;
|
return nav;
|
||||||
}, [user]);
|
}, [user]); // Removed t dependency until Docker rebuild
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex bg-gray-100">
|
<div className="min-h-screen flex bg-gray-100">
|
||||||
@@ -120,7 +124,7 @@ const Layout = () => {
|
|||||||
) : (
|
) : (
|
||||||
<SignalIcon className="h-3 w-3" />
|
<SignalIcon className="h-3 w-3" />
|
||||||
)}
|
)}
|
||||||
<span>{connected ? 'Connected' : 'Disconnected'}</span>
|
<span>{connected ? t('dashboard.online') : t('dashboard.offline')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -128,7 +132,7 @@ const Layout = () => {
|
|||||||
{recentDetections.length > 0 && (
|
{recentDetections.length > 0 && (
|
||||||
<div className="flex items-center space-x-1 px-2 py-1 bg-danger-100 text-danger-800 rounded-full text-xs font-medium">
|
<div className="flex items-center space-x-1 px-2 py-1 bg-danger-100 text-danger-800 rounded-full text-xs font-medium">
|
||||||
<ExclamationTriangleIcon className="h-3 w-3" />
|
<ExclamationTriangleIcon className="h-3 w-3" />
|
||||||
<span>{recentDetections.length} recent</span>
|
<span>{recentDetections.length} {t('dashboard.recentDetections').toLowerCase()}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -146,7 +150,7 @@ const Layout = () => {
|
|||||||
onClick={logout}
|
onClick={logout}
|
||||||
className="text-sm text-gray-500 hover:text-gray-700"
|
className="text-sm text-gray-500 hover:text-gray-700"
|
||||||
>
|
>
|
||||||
Logout
|
{t('navigation.logout')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
30
client/src/components/TestTranslation.jsx
Normal file
30
client/src/components/TestTranslation.jsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const TestTranslation = () => {
|
||||||
|
const { t, i18n } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h1>Translation Test</h1>
|
||||||
|
<p>Current language: {i18n.language}</p>
|
||||||
|
<p>Dashboard translation: {t('navigation.dashboard')}</p>
|
||||||
|
<p>Loading translation: {t('common.loading')}</p>
|
||||||
|
<p>Error translation: {t('common.error')}</p>
|
||||||
|
<button
|
||||||
|
onClick={() => i18n.changeLanguage('sv')}
|
||||||
|
className="bg-blue-500 text-white px-4 py-2 rounded mr-2"
|
||||||
|
>
|
||||||
|
Switch to Swedish
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => i18n.changeLanguage('en')}
|
||||||
|
className="bg-green-500 text-white px-4 py-2 rounded"
|
||||||
|
>
|
||||||
|
Switch to English
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TestTranslation;
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
// import { useTranslation } from 'react-i18next'; // Commented out until Docker rebuild
|
||||||
import { Menu, Transition } from '@headlessui/react';
|
import { Menu, Transition } from '@headlessui/react';
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { GlobeAltIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
|
import { GlobeAltIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
|
||||||
|
import { getCurrentLanguage, changeLanguage } from '../../utils/tempTranslations'; // Temporary system
|
||||||
|
|
||||||
const languages = [
|
const languages = [
|
||||||
{ code: 'en', name: 'English', flag: '🇺🇸' },
|
{ code: 'en', name: 'English', flag: '🇺🇸' },
|
||||||
@@ -10,12 +11,13 @@ const languages = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function LanguageSelector({ className = '' }) {
|
export default function LanguageSelector({ className = '' }) {
|
||||||
const { i18n, t } = useTranslation();
|
// const { i18n, t } = useTranslation(); // Commented out until Docker rebuild
|
||||||
|
const currentLang = getCurrentLanguage();
|
||||||
|
|
||||||
const currentLanguage = languages.find(lang => lang.code === i18n.language) || languages[0];
|
const currentLanguage = languages.find(lang => lang.code === currentLang) || languages[0];
|
||||||
|
|
||||||
const changeLanguage = (languageCode) => {
|
const handleChangeLanguage = (languageCode) => {
|
||||||
i18n.changeLanguage(languageCode);
|
changeLanguage(languageCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -44,16 +46,16 @@ export default function LanguageSelector({ className = '' }) {
|
|||||||
<Menu.Item key={language.code}>
|
<Menu.Item key={language.code}>
|
||||||
{({ active }) => (
|
{({ active }) => (
|
||||||
<button
|
<button
|
||||||
onClick={() => changeLanguage(language.code)}
|
onClick={() => handleChangeLanguage(language.code)}
|
||||||
className={`${
|
className={`${
|
||||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'
|
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'
|
||||||
} ${
|
} ${
|
||||||
language.code === i18n.language ? 'bg-indigo-50 text-indigo-600' : ''
|
language.code === currentLang ? 'bg-indigo-50 text-indigo-600' : ''
|
||||||
} group flex items-center px-4 py-2 text-sm w-full text-left`}
|
} group flex items-center px-4 py-2 text-sm w-full text-left`}
|
||||||
>
|
>
|
||||||
<span className="mr-3">{language.flag}</span>
|
<span className="mr-3">{language.flag}</span>
|
||||||
<span>{language.name}</span>
|
<span>{language.name}</span>
|
||||||
{language.code === i18n.language && (
|
{language.code === currentLang && (
|
||||||
<span className="ml-auto">
|
<span className="ml-auto">
|
||||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||||
|
|||||||
61
client/src/utils/tempTranslations.js
Normal file
61
client/src/utils/tempTranslations.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Temporary translation system until Docker rebuild
|
||||||
|
const translations = {
|
||||||
|
en: {
|
||||||
|
navigation: {
|
||||||
|
dashboard: 'Dashboard',
|
||||||
|
detections: 'Detections',
|
||||||
|
devices: 'Devices',
|
||||||
|
alerts: 'Alerts',
|
||||||
|
settings: 'Settings',
|
||||||
|
logout: 'Logout'
|
||||||
|
},
|
||||||
|
dashboard: {
|
||||||
|
online: 'Connected',
|
||||||
|
offline: 'Disconnected',
|
||||||
|
recentDetections: 'Recent'
|
||||||
|
},
|
||||||
|
app: {
|
||||||
|
title: 'UAM-ILS Management Portal'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sv: {
|
||||||
|
navigation: {
|
||||||
|
dashboard: 'Översikt',
|
||||||
|
detections: 'Detekteringar',
|
||||||
|
devices: 'Enheter',
|
||||||
|
alerts: 'Larm',
|
||||||
|
settings: 'Inställningar',
|
||||||
|
logout: 'Logga ut'
|
||||||
|
},
|
||||||
|
dashboard: {
|
||||||
|
online: 'Ansluten',
|
||||||
|
offline: 'Frånkopplad',
|
||||||
|
recentDetections: 'Senaste'
|
||||||
|
},
|
||||||
|
app: {
|
||||||
|
title: 'UAM-ILS Förvaltningsportal'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let currentLanguage = localStorage.getItem('language') || 'en';
|
||||||
|
|
||||||
|
export const t = (key) => {
|
||||||
|
const keys = key.split('.');
|
||||||
|
let value = translations[currentLanguage];
|
||||||
|
|
||||||
|
for (const k of keys) {
|
||||||
|
value = value?.[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
return value || key;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const changeLanguage = (lang) => {
|
||||||
|
currentLanguage = lang;
|
||||||
|
localStorage.setItem('language', lang);
|
||||||
|
// Trigger a page refresh to update all components
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCurrentLanguage = () => currentLanguage;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Outlet, NavLink, useLocation } from 'react-router-dom'
|
import { Outlet, NavLink, useLocation } from 'react-router-dom'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
import LanguageSelector from './common/LanguageSelector'
|
import LanguageSelector from './common/LanguageSelector'
|
||||||
import {
|
import {
|
||||||
@@ -11,14 +12,15 @@ import {
|
|||||||
} from '@heroicons/react/24/outline'
|
} from '@heroicons/react/24/outline'
|
||||||
|
|
||||||
const Layout = () => {
|
const Layout = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { user, logout } = useAuth()
|
const { user, logout } = useAuth()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{ name: 'Dashboard', href: '/dashboard', icon: HomeIcon },
|
{ name: t('navigation.dashboard'), href: '/dashboard', icon: HomeIcon },
|
||||||
{ name: 'Tenants', href: '/tenants', icon: BuildingOfficeIcon },
|
{ name: t('navigation.tenants'), href: '/tenants', icon: BuildingOfficeIcon },
|
||||||
{ name: 'Users', href: '/users', icon: UsersIcon },
|
{ name: t('navigation.users'), href: '/users', icon: UsersIcon },
|
||||||
{ name: 'System', href: '/system', icon: CogIcon },
|
{ name: t('navigation.system'), href: '/system', icon: CogIcon },
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -26,7 +28,7 @@ const Layout = () => {
|
|||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<div className="fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-lg">
|
<div className="fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-lg">
|
||||||
<div className="flex h-16 items-center justify-center border-b border-gray-200">
|
<div className="flex h-16 items-center justify-center border-b border-gray-200">
|
||||||
<h1 className="text-xl font-bold text-gray-900">UAMILS Management</h1>
|
<h1 className="text-xl font-bold text-gray-900">{t('app.title')}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="mt-8 px-4 space-y-2">
|
<nav className="mt-8 px-4 space-y-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user