Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Update frontend/src/components/Layout.jsx
Browse files
frontend/src/components/Layout.jsx
CHANGED
|
@@ -2,6 +2,40 @@ import React, { useState, useEffect, useRef } from 'react';
|
|
| 2 |
import { Link, NavLink, useNavigate, useLocation } from 'react-router-dom';
|
| 3 |
import { useAuth } from './AuthContext';
|
| 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
export const Layout = ({ children }) => {
|
| 6 |
const { user, logout } = useAuth();
|
| 7 |
const navigate = useNavigate();
|
|
@@ -199,17 +233,20 @@ export const Layout = ({ children }) => {
|
|
| 199 |
{/* Header Branding */}
|
| 200 |
<div className="mb-xl flex flex-col gap-sm">
|
| 201 |
<div className="flex items-center gap-sm">
|
| 202 |
-
<div className="h-
|
| 203 |
<img src="/logo.png" alt="LarShield Logo" className="h-full object-contain" />
|
| 204 |
</div>
|
| 205 |
-
<div>
|
| 206 |
-
<h1 className="font-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
user?.
|
|
|
|
|
|
|
|
|
|
| 213 |
</p>
|
| 214 |
</div>
|
| 215 |
</div>
|
|
@@ -282,7 +319,13 @@ export const Layout = ({ children }) => {
|
|
| 282 |
{/* Mobile Hamburger menu */}
|
| 283 |
<div className="md:hidden flex items-center gap-sm">
|
| 284 |
<span className="material-symbols-outlined text-primary cursor-pointer">menu</span>
|
| 285 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
</div>
|
| 287 |
|
| 288 |
{/* Search Bar Utility */}
|
|
@@ -448,7 +491,7 @@ export const Layout = ({ children }) => {
|
|
| 448 |
<span className="material-symbols-outlined" style={{ fontSize: '18px' }}>calendar_today</span>
|
| 449 |
</button>
|
| 450 |
)}
|
| 451 |
-
|
| 452 |
{user && (
|
| 453 |
<div className="relative ml-sm" ref={profileRef}>
|
| 454 |
<button
|
|
@@ -458,17 +501,26 @@ export const Layout = ({ children }) => {
|
|
| 458 |
}}
|
| 459 |
className="flex items-center gap-xs border-0 bg-transparent cursor-pointer hover:opacity-80 transition-opacity"
|
| 460 |
>
|
| 461 |
-
<
|
|
|
|
|
|
|
| 462 |
<span className="hidden lg:inline text-xs font-semibold text-on-surface-variant dark:text-surface-variant">
|
| 463 |
-
{user.email.split('@')[0]}
|
| 464 |
</span>
|
| 465 |
</button>
|
| 466 |
|
| 467 |
{isProfileOpen && (
|
| 468 |
-
<div className="absolute right-0 mt-xs w-
|
| 469 |
-
<div className="px-md py-sm border-b border-outline-variant bg-surface-container-low">
|
| 470 |
-
<
|
| 471 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 472 |
</div>
|
| 473 |
<div className="p-xs">
|
| 474 |
<button
|
|
|
|
| 2 |
import { Link, NavLink, useNavigate, useLocation } from 'react-router-dom';
|
| 3 |
import { useAuth } from './AuthContext';
|
| 4 |
|
| 5 |
+
export const getInitials = (userData) => {
|
| 6 |
+
if (!userData) return 'DP';
|
| 7 |
+
const firstName = (userData.first_name || '').trim();
|
| 8 |
+
const lastName = (userData.last_name || '').trim();
|
| 9 |
+
if (firstName || lastName) {
|
| 10 |
+
const f = firstName.charAt(0);
|
| 11 |
+
const l = lastName.charAt(0);
|
| 12 |
+
if (f && l) return `${f}${l}`.toUpperCase();
|
| 13 |
+
if (f) return f.toUpperCase();
|
| 14 |
+
if (l) return l.toUpperCase();
|
| 15 |
+
}
|
| 16 |
+
const name = (userData.name || userData.full_name || '').trim();
|
| 17 |
+
if (name) {
|
| 18 |
+
const parts = name.split(/\s+/);
|
| 19 |
+
if (parts.length >= 2) {
|
| 20 |
+
return `${parts[0].charAt(0)}${parts[parts.length - 1].charAt(0)}`.toUpperCase();
|
| 21 |
+
}
|
| 22 |
+
return name.substring(0, 2).toUpperCase();
|
| 23 |
+
}
|
| 24 |
+
const email = (userData.email || '').trim();
|
| 25 |
+
if (email) {
|
| 26 |
+
const username = email.split('@')[0];
|
| 27 |
+
const parts = username.split(/[._-]/);
|
| 28 |
+
if (parts.length >= 2 && parts[0] && parts[1]) {
|
| 29 |
+
return `${parts[0].charAt(0)}${parts[1].charAt(0)}`.toUpperCase();
|
| 30 |
+
}
|
| 31 |
+
if (username.length >= 2) {
|
| 32 |
+
return username.substring(0, 2).toUpperCase();
|
| 33 |
+
}
|
| 34 |
+
return username.charAt(0).toUpperCase();
|
| 35 |
+
}
|
| 36 |
+
return 'DP';
|
| 37 |
+
};
|
| 38 |
+
|
| 39 |
export const Layout = ({ children }) => {
|
| 40 |
const { user, logout } = useAuth();
|
| 41 |
const navigate = useNavigate();
|
|
|
|
| 233 |
{/* Header Branding */}
|
| 234 |
<div className="mb-xl flex flex-col gap-sm">
|
| 235 |
<div className="flex items-center gap-sm">
|
| 236 |
+
<div className="h-12 flex items-center justify-center shrink-0">
|
| 237 |
<img src="/logo.png" alt="LarShield Logo" className="h-full object-contain" />
|
| 238 |
</div>
|
| 239 |
+
<div className="flex flex-col justify-center">
|
| 240 |
+
<h1 className="font-extrabold tracking-tight m-0 text-[18px] leading-none flex items-center">
|
| 241 |
+
<span className="text-[#0b132a] dark:text-white">Lar</span>
|
| 242 |
+
<span className="text-[#5856d6]">Shield</span>
|
| 243 |
+
</h1>
|
| 244 |
+
<p className="text-[12px] text-[#d97706] uppercase tracking-[0.16em] m-0 font-semibold leading-tight mt-1">
|
| 245 |
+
{user?.role === 'super_admin' ? 'SUPER ADMIN' :
|
| 246 |
+
user?.role === 'admin' ? 'ADMIN' :
|
| 247 |
+
user?.role === 'support_engineer' ? 'SUPPORT ENGINEER' :
|
| 248 |
+
user?.role === 'org_admin' ? 'ORGANIZATION ADMIN' :
|
| 249 |
+
(user?.subscription_tier ? `${user.subscription_tier.toUpperCase()} TIER` : 'FREE TIER')}
|
| 250 |
</p>
|
| 251 |
</div>
|
| 252 |
</div>
|
|
|
|
| 319 |
{/* Mobile Hamburger menu */}
|
| 320 |
<div className="md:hidden flex items-center gap-sm">
|
| 321 |
<span className="material-symbols-outlined text-primary cursor-pointer">menu</span>
|
| 322 |
+
<div className="flex items-center gap-xs">
|
| 323 |
+
<img src="/logo.png" alt="LarShield Logo" className="h-7 w-7 object-contain" />
|
| 324 |
+
<span className="font-extrabold tracking-tight text-[16px] leading-none flex items-center">
|
| 325 |
+
<span className="text-[#0b132a] dark:text-white">Lar</span>
|
| 326 |
+
<span className="text-[#5856d6]">Shield</span>
|
| 327 |
+
</span>
|
| 328 |
+
</div>
|
| 329 |
</div>
|
| 330 |
|
| 331 |
{/* Search Bar Utility */}
|
|
|
|
| 491 |
<span className="material-symbols-outlined" style={{ fontSize: '18px' }}>calendar_today</span>
|
| 492 |
</button>
|
| 493 |
)}
|
| 494 |
+
|
| 495 |
{user && (
|
| 496 |
<div className="relative ml-sm" ref={profileRef}>
|
| 497 |
<button
|
|
|
|
| 501 |
}}
|
| 502 |
className="flex items-center gap-xs border-0 bg-transparent cursor-pointer hover:opacity-80 transition-opacity"
|
| 503 |
>
|
| 504 |
+
<div className="w-8 h-8 rounded-full bg-primary/10 text-primary border border-primary/20 flex items-center justify-center font-bold text-xs uppercase shadow-sm">
|
| 505 |
+
{getInitials(user)}
|
| 506 |
+
</div>
|
| 507 |
<span className="hidden lg:inline text-xs font-semibold text-on-surface-variant dark:text-surface-variant">
|
| 508 |
+
{user.first_name || user.last_name ? `${user.first_name || ''} ${user.last_name || ''}`.trim() : user.email.split('@')[0]}
|
| 509 |
</span>
|
| 510 |
</button>
|
| 511 |
|
| 512 |
{isProfileOpen && (
|
| 513 |
+
<div className="absolute right-0 mt-xs w-52 bg-surface border border-outline-variant rounded-lg shadow-lg overflow-hidden z-50">
|
| 514 |
+
<div className="px-md py-sm border-b border-outline-variant bg-surface-container-low flex items-center gap-sm">
|
| 515 |
+
<div className="w-9 h-9 rounded-full bg-primary/10 text-primary border border-primary/20 flex items-center justify-center font-bold text-sm uppercase shrink-0">
|
| 516 |
+
{getInitials(user)}
|
| 517 |
+
</div>
|
| 518 |
+
<div className="flex flex-col min-w-0">
|
| 519 |
+
<p className="font-label-md text-label-md font-bold text-on-surface truncate m-0">
|
| 520 |
+
{user.first_name || user.last_name ? `${user.first_name || ''} ${user.last_name || ''}`.trim() : user.email}
|
| 521 |
+
</p>
|
| 522 |
+
<p className="font-body-sm text-[11px] text-on-surface-variant capitalize m-0 truncate">{(user.role || 'User').replace(/_/g, ' ')}</p>
|
| 523 |
+
</div>
|
| 524 |
</div>
|
| 525 |
<div className="p-xs">
|
| 526 |
<button
|