ui-nyla/src/components/Navigation/UserSection.tsx
ValueOn AG d579df1c92
Some checks failed
Deploy Nyla Frontend to Integration / deploy (push) Failing after 52s
panel ui
2026-06-11 16:43:53 +02:00

215 lines
7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2026 PowerOn AG
// All rights reserved.
/**
* UserSection Component
*
* Zeigt Benutzerinformationen und Logout-Button in der Sidebar.
*/
import React, { useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useCurrentUser } from '../../hooks/useUsers';
import { NotificationBell } from '../NotificationBell';
import { _isOnboardingHidden, _showOnboarding } from '../OnboardingAssistant';
import { FloatingPortal } from '../UiComponents/FloatingPortal';
import styles from './UserSection.module.css';
import { useLanguage } from '../../providers/language/LanguageContext';
import { useSidebar } from '../../layouts/SidebarContext';
export const UserSection: React.FC = () => {
const { t } = useLanguage();
const { collapsed } = useSidebar();
const { user, logout } = useCurrentUser();
const navigate = useNavigate();
const [isLoggingOut, setIsLoggingOut] = useState(false);
const [showMenu, setShowMenu] = useState(false);
const [showLegalModal, setShowLegalModal] = useState(false);
const [onboardingHidden, setOnboardingHidden] = useState(() => _isOnboardingHidden());
const userButtonRef = useRef<HTMLButtonElement>(null);
const handleLogout = async () => {
setIsLoggingOut(true);
try {
await logout();
} catch (error) {
console.error('Logout failed:', error);
setIsLoggingOut(false);
}
};
const handleSettings = () => {
navigate('/settings');
setShowMenu(false);
};
const handleBilling = () => {
navigate('/billing/transactions');
setShowMenu(false);
};
const handleLegal = () => {
setShowLegalModal(true);
setShowMenu(false);
};
const handleOnboarding = () => {
_showOnboarding();
setOnboardingHidden(false);
navigate('/', { state: { showOnboarding: Date.now() } });
setShowMenu(false);
};
if (!user) {
return null;
}
// Initialen für Avatar
const initials = (() => {
const name = user.fullName || user.username || '';
const parts = name.trim().split(/\s+/);
if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toLocaleUpperCase();
return [...name.trim()].slice(0, 2).join('').toLocaleUpperCase() || '?';
})();
return (
<div className={`${styles.userSection} ${collapsed ? styles.userSectionCollapsed : ''}`}>
{!collapsed && (
<NotificationBell className={styles.notificationBell} />
)}
<button
ref={userButtonRef}
className={`${styles.userButton} ${collapsed ? styles.userButtonCollapsed : ''}`}
onClick={() => { setShowMenu(!showMenu); setOnboardingHidden(_isOnboardingHidden()); }}
aria-expanded={showMenu}
title={collapsed ? (user.fullName || user.username) : undefined}
>
<div className={styles.avatar}>
{initials}
</div>
{!collapsed && (
<>
<div className={styles.userInfo}>
<span className={styles.userName}>{user.fullName || user.username}</span>
<span className={styles.userEmail}>{user.email}</span>
</div>
<span className={styles.chevron}>
{showMenu ? '▲' : '▼'}
</span>
</>
)}
</button>
<FloatingPortal
open={showMenu}
anchorRef={userButtonRef}
onClose={() => setShowMenu(false)}
placement="top"
align="start"
>
<div className={`${styles.menu} ${collapsed ? styles.menuCollapsed : ''}`}>
<button
className={styles.menuItem}
onClick={handleBilling}
>
<span className={styles.menuIcon}>💰</span>
{t('Guthaben')}
</button>
<button
className={styles.menuItem}
onClick={handleSettings}
>
<span className={styles.menuIcon}></span>
{t('Einstellungen')}
</button>
{onboardingHidden && (
<button
className={styles.menuItem}
onClick={handleOnboarding}
>
<span className={styles.menuIcon}>{'\uD83E\uDDED'}</span>
{t('Onboarding-Assistent')}
</button>
)}
<button
className={styles.menuItem}
onClick={handleLegal}
>
<span className={styles.menuIcon}>📜</span>
{t('Rechtliche Hinweise')}
</button>
<div className={styles.menuDivider} />
<button
className={styles.menuItem}
onClick={handleLogout}
disabled={isLoggingOut}
>
<span className={styles.menuIcon}>🚪</span>
{isLoggingOut ? t('Abmelden...') : t('Abmelden')}
</button>
</div>
</FloatingPortal>
{/* Legal Modal */}
{showLegalModal && (
<div className={styles.modalOverlay}>
<div className={styles.modal}>
<div className={styles.modalHeader}>
<h2>{t('Legal notices')}</h2>
<button
className={styles.modalClose}
onClick={() => setShowLegalModal(false)}
>
</button>
</div>
<div className={styles.modalContent}>
<div className={styles.legalSection}>
<h3>{t('Datenverarbeitung und KI-Nutzung')}</h3>
<h4>{t('userSection.1EinwilligungZurDatenverarbeitung')}</h4>
<p>{t('By using this application')}</p>
<ul>
<li>{t('You authorize the collection and processing')}</li>
<li>{t('User data may be shared with third-party providers of')}</li>
<li>{t('This consent extends to')}</li>
</ul>
<h4>{t('userSection.2AnerkennungDerKiverarbeitungsrisiken')}</h4>
<ul>
<li>{t('AI systems may produce unexpected or inaccurate')}</li>
<li>{t('AI services can process data according to their')}</li>
<li>{t('Despite security measures, data may be vulnerable')}</li>
</ul>
<h4>{t('userSection.3Haftungsausschluss')}</h4>
<p>{t('To the fullest extent possible, you waive')}</p>
</div>
<div className={styles.legalLinks}>
<a href="/poweron-privacy.html" target="_blank" rel="noopener noreferrer">
{t('Datenschutzrichtlinie')}
</a>
<a href="/poweron-terms.html" target="_blank" rel="noopener noreferrer">
{t('Nutzungsbedingungen')}
</a>
<a href="/poweron-home.html" target="_blank" rel="noopener noreferrer">
{t('Über PowerOn')}
</a>
</div>
</div>
</div>
</div>
)}
</div>
);
};
export default UserSection;