Fix jwt-token

This commit is contained in:
2025-09-20 06:35:03 +02:00
parent aa886599c6
commit 8ae7b75365
3 changed files with 93 additions and 16 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useState, useEffect } 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 { useTranslation } from 'react-i18next'; // Commented out until Docker rebuild
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
@@ -7,6 +7,7 @@ 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 { t } from '../utils/tempTranslations'; // Temporary translation system
import api from '../services/api';
import { import {
HomeIcon, HomeIcon,
MapIcon, MapIcon,
@@ -25,11 +26,29 @@ import classNames from 'classnames';
const Layout = () => { const Layout = () => {
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
const [tenantInfo, setTenantInfo] = useState(null);
// const { t } = useTranslation(); // Commented out until Docker rebuild // 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();
// Fetch tenant information for branding
useEffect(() => {
const fetchTenantInfo = async () => {
try {
const response = await api.get('/tenant/info');
setTenantInfo(response.data.data);
} catch (error) {
console.error('Failed to fetch tenant info:', error);
// Don't show error toast as this is not critical
}
};
if (user) {
fetchTenantInfo();
}
}, [user]);
// Build navigation based on user permissions with translations // Build navigation based on user permissions with translations
const baseNavigation = [ const baseNavigation = [
{ name: t('navigation.dashboard'), href: '/', icon: HomeIcon }, { name: t('navigation.dashboard'), href: '/', icon: HomeIcon },
@@ -78,7 +97,7 @@ const Layout = () => {
<XMarkIcon className="h-6 w-6 text-white" /> <XMarkIcon className="h-6 w-6 text-white" />
</button> </button>
</div> </div>
<SidebarContent navigation={navigation} /> <SidebarContent navigation={navigation} tenantInfo={tenantInfo} />
</div> </div>
</div> </div>
@@ -86,7 +105,7 @@ const Layout = () => {
<div className="hidden md:flex md:flex-shrink-0"> <div className="hidden md:flex md:flex-shrink-0">
<div className="flex flex-col w-64"> <div className="flex flex-col w-64">
<div className="flex flex-col flex-grow pt-5 pb-4 overflow-y-auto bg-white border-r border-gray-200"> <div className="flex flex-col flex-grow pt-5 pb-4 overflow-y-auto bg-white border-r border-gray-200">
<SidebarContent navigation={navigation} /> <SidebarContent navigation={navigation} tenantInfo={tenantInfo} />
</div> </div>
</div> </div>
</div> </div>
@@ -174,18 +193,32 @@ const Layout = () => {
); );
}; };
const SidebarContent = ({ navigation }) => { const SidebarContent = ({ navigation, tenantInfo }) => {
const location = useLocation(); const location = useLocation();
return ( return (
<> <>
<div className="flex items-center flex-shrink-0 px-4"> <div className="flex items-center flex-shrink-0 px-4">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<div className="w-8 h-8 bg-primary-600 rounded-lg flex items-center justify-center"> {/* Display tenant logo if available, otherwise show default icon */}
{tenantInfo?.branding?.logo_url ? (
<img
src={tenantInfo.branding.logo_url}
alt={`${tenantInfo.name || 'Company'} Logo`}
className="w-8 h-8 object-contain"
onError={(e) => {
// Fallback to default icon if logo fails to load
e.target.style.display = 'none';
e.target.nextSibling.style.display = 'flex';
}}
/>
) : null}
{/* Default icon (shown if no logo or logo fails to load) */}
<div className={`w-8 h-8 bg-primary-600 rounded-lg flex items-center justify-center ${tenantInfo?.branding?.logo_url ? 'hidden' : ''}`}>
<ExclamationTriangleIcon className="h-5 w-5 text-white" /> <ExclamationTriangleIcon className="h-5 w-5 text-white" />
</div> </div>
<h1 className="text-lg font-bold text-gray-900"> <h1 className="text-lg font-bold text-gray-900">
Drone Detector {tenantInfo?.name || 'Drone Detector'}
</h1> </h1>
</div> </div>
</div> </div>

View File

@@ -92,11 +92,33 @@ const Login = () => {
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8"> <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8"> <div className="max-w-md w-full space-y-8">
<div> <div>
{/* Display tenant logo if available, otherwise show default icon */}
{tenantConfig?.branding?.logo_url ? (
<div className="mx-auto h-16 w-auto flex items-center justify-center">
<img
src={tenantConfig.branding.logo_url}
alt={`${tenantConfig.tenant_name || 'Company'} Logo`}
className="h-16 w-auto max-w-48 object-contain"
onError={(e) => {
// Fallback to default icon if logo fails to load
e.target.style.display = 'none';
e.target.nextSibling.style.display = 'flex';
}}
/>
{/* Hidden fallback icon */}
<div className="hidden mx-auto h-12 w-12 bg-primary-600 rounded-lg items-center justify-center">
<svg className="h-8 w-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
</div>
</div>
) : (
<div className="mx-auto h-12 w-12 bg-primary-600 rounded-lg flex items-center justify-center"> <div className="mx-auto h-12 w-12 bg-primary-600 rounded-lg flex items-center justify-center">
<svg className="h-8 w-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="h-8 w-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg> </svg>
</div> </div>
)}
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900"> <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
{tenantConfig?.tenant_name || 'Drone Detection System'} {tenantConfig?.tenant_name || 'Drone Detection System'}
</h2> </h2>

View File

@@ -201,11 +201,33 @@ const Register = () => {
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8"> <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8"> <div className="max-w-md w-full space-y-8">
<div> <div>
{/* Display tenant logo if available, otherwise show default icon */}
{tenantConfig?.branding?.logo_url ? (
<div className="mx-auto h-16 w-auto flex items-center justify-center">
<img
src={tenantConfig.branding.logo_url}
alt={`${tenantConfig.tenant_name || 'Company'} Logo`}
className="h-16 w-auto max-w-48 object-contain"
onError={(e) => {
// Fallback to default icon if logo fails to load
e.target.style.display = 'none';
e.target.nextSibling.style.display = 'flex';
}}
/>
{/* Hidden fallback icon */}
<div className="hidden mx-auto h-12 w-12 bg-primary-600 rounded-lg items-center justify-center">
<svg className="h-8 w-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z" />
</svg>
</div>
</div>
) : (
<div className="mx-auto h-12 w-12 bg-primary-600 rounded-lg flex items-center justify-center"> <div className="mx-auto h-12 w-12 bg-primary-600 rounded-lg flex items-center justify-center">
<svg className="h-8 w-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="h-8 w-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z" />
</svg> </svg>
</div> </div>
)}
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900"> <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Create your account Create your account
</h2> </h2>