import React, { lazy, Component, Suspense } from 'react'; import { Link } from 'react-router-dom'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; import DialogTitle from '@mui/material/DialogTitle'; import DialogContent from '@mui/material/DialogContent'; import Tabs from '@mui/material/Tabs'; import Tab from '@mui/material/Tab'; import TextField from '@mui/material/TextField'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; import Alert from '@mui/material/Alert'; import CircularProgress from '@mui/material/CircularProgress'; import MenuItem from '@mui/material/MenuItem'; import Menu from '@mui/material/Menu'; import Divider from '@mui/material/Divider'; import CloseIcon from '@mui/icons-material/Close'; import PersonIcon from '@mui/icons-material/Person'; import { withRouter } from './withRouter.js'; import GoogleLoginButton from './GoogleLoginButton.js'; import CartSyncDialog from './CartSyncDialog.js'; import { localAndArchiveServer, mergeCarts } from '../utils/cartUtils.js'; import config from '../config.js'; import { withI18n } from '../i18n/withTranslation.js'; import { hasPendingWirePaymentOrder, WIRE_PAYMENT_PENDING_EVENT, } from '../utils/wireGirocodeEligibility.js'; import GoogleIcon from '@mui/icons-material/Google'; // Lazy load GoogleAuthProvider const GoogleAuthProvider = lazy(() => import('../providers/GoogleAuthProvider.js')); const getTokenFromAuthResponse = (response) => response?.token || response?.accessToken || response?.jwt || response?.user?.token || response?.user?.accessToken || null; const persistSessionAuth = (response) => { if (response?.user) { sessionStorage.setItem('user', JSON.stringify(response.user)); } const token = getTokenFromAuthResponse(response); if (token) { sessionStorage.setItem('authToken', token); } else { sessionStorage.removeItem('authToken'); } }; // Function to check if user is logged in export const isUserLoggedIn = () => { const storedUser = sessionStorage.getItem('user'); if (storedUser) { try { const parsedUser = JSON.parse(storedUser); console.log('Parsed User:', parsedUser); return { isLoggedIn: true, user: parsedUser, isAdmin: !!parsedUser.admin }; } catch (error) { console.error('Error parsing user from sessionStorage:', error); sessionStorage.removeItem('user'); } } console.log('isUserLoggedIn', false); return { isLoggedIn: false, user: null, isAdmin: false }; }; // Hilfsfunktion zum Vergleich zweier Cart-Arrays function cartsAreIdentical(cartA, cartB) { console.log('Vergleiche Carts:', {cartA, cartB}); if (!Array.isArray(cartA) || !Array.isArray(cartB)) { console.log('Mindestens eines der Carts ist kein Array'); return false; } if (cartA.length !== cartB.length) { console.log('Unterschiedliche Längen:', cartA.length, cartB.length); return false; } const sortById = arr => [...arr].sort((a, b) => (a.id > b.id ? 1 : -1)); const aSorted = sortById(cartA); const bSorted = sortById(cartB); for (let i = 0; i < aSorted.length; i++) { if (aSorted[i].id !== bSorted[i].id) { console.log('Unterschiedliche IDs:', aSorted[i].id, bSorted[i].id, aSorted[i], bSorted[i]); return false; } if (aSorted[i].quantity !== bSorted[i].quantity) { console.log('Unterschiedliche Mengen:', aSorted[i].id, aSorted[i].quantity, bSorted[i].quantity); return false; } } console.log('Carts sind identisch'); return true; } export class LoginComponent extends Component { constructor(props) { super(props); const { isLoggedIn, user, isAdmin } = isUserLoggedIn(); this.state = { open: false, tabValue: 0, email: '', password: '', confirmPassword: '', error: '', loading: false, success: '', isLoggedIn, isAdmin, user, anchorEl: null, showGoogleAuth: false, cartSyncOpen: false, localCartSync: [], serverCartSync: [], pendingNavigate: null, privacyConfirmed: sessionStorage.getItem('privacyConfirmed') === 'true', pendingWirePaymentOrders: false }; } refreshPendingWireOrders = () => { if (typeof window === 'undefined' || !window.socketManager) return; window.socketManager.emit('getOrders', (response) => { if (response.success && Array.isArray(response.orders)) { this.setState({ pendingWirePaymentOrders: hasPendingWirePaymentOrder(response.orders), }); } }); }; handleWirePaymentPendingEvent = (e) => { if (e.detail && typeof e.detail.pending === 'boolean') { this.setState({ pendingWirePaymentOrders: e.detail.pending }); } }; componentDidMount() { // Make the open function available globally window.openLoginDrawer = this.handleOpen; if (this.props.open) { this.setState({ open: true }); } if (this.state.isLoggedIn) { this.refreshPendingWireOrders(); } window.addEventListener(WIRE_PAYMENT_PENDING_EVENT, this.handleWirePaymentPendingEvent); } componentDidUpdate(prevProps, prevState) { if (this.props.open !== prevProps.open) { this.setState({ open: this.props.open }); } if (this.state.isLoggedIn && !prevState.isLoggedIn) { this.refreshPendingWireOrders(); } } componentWillUnmount() { // Cleanup function to remove global reference when component unmounts window.openLoginDrawer = undefined; window.removeEventListener(WIRE_PAYMENT_PENDING_EVENT, this.handleWirePaymentPendingEvent); } resetForm = () => { this.setState({ email: '', password: '', confirmPassword: '', error: '', success: '', loading: false, showGoogleAuth: false // Reset Google auth state when form is reset }); }; handleOpen = () => { this.setState({ open: true, loading: false, privacyConfirmed: sessionStorage.getItem('privacyConfirmed') === 'true' }); this.resetForm(); }; handleClose = () => { this.setState({ open: false, showGoogleAuth: false // Reset Google auth state when dialog closes }); this.resetForm(); }; handleTabChange = (event, newValue) => { this.setState({ tabValue: newValue, error: '', success: '' }); }; validateEmail = (email) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); }; handleLogin = () => { const { email, password } = this.state; const { location, navigate } = this.props; if (!email || !password) { this.setState({ error: this.props.t ? this.props.t('auth.errors.fillAllFields') : 'Bitte füllen Sie alle Felder aus' }); return; } if (!this.validateEmail(email)) { this.setState({ error: this.props.t ? this.props.t('auth.errors.invalidEmail') : 'Bitte geben Sie eine gültige E-Mail-Adresse ein' }); return; } this.setState({ loading: true, error: '' }); window.socketManager.emit('verifyUser', { email, password }, (response) => { console.log('LoginComponent: verifyUser', response); if (response.success) { persistSessionAuth(response); this.setState({ user: response.user, isLoggedIn: true, isAdmin: !!response.user.admin }); const redirectTo = location && location.hash ? `/profile${location.hash}` : '/profile'; const dispatchLoginEvent = () => { window.dispatchEvent(new CustomEvent('userLoggedIn')); navigate(redirectTo); } try { const newCart = JSON.parse(response.user.cart); const localCartArr = window.cart ? Object.values(window.cart) : []; const serverCartArr = newCart ? Object.values(newCart) : []; if (serverCartArr.length === 0) { window.socketManager.emit('updateCart', window.cart); this.handleClose(); dispatchLoginEvent(); } else if (localCartArr.length === 0 && serverCartArr.length > 0) { window.cart = serverCartArr; window.dispatchEvent(new CustomEvent('cart')); this.handleClose(); dispatchLoginEvent(); } else if (cartsAreIdentical(localCartArr, serverCartArr)) { this.handleClose(); dispatchLoginEvent(); } else { this.setState({ cartSyncOpen: true, localCartSync: localCartArr, serverCartSync: serverCartArr, pendingNavigate: dispatchLoginEvent }); } } catch (error) { console.error('Error parsing cart:', response.user, error); this.handleClose(); dispatchLoginEvent(); } } else { this.setState({ loading: false, error: response.message || (this.props.t ? this.props.t('auth.errors.loginFailed') : 'Anmeldung fehlgeschlagen') }); } }); }; handleRegister = () => { const { email, password, confirmPassword } = this.state; if (!email || !password || !confirmPassword) { this.setState({ error: this.props.t ? this.props.t('auth.errors.fillAllFields') : 'Bitte füllen Sie alle Felder aus' }); return; } if (!this.validateEmail(email)) { this.setState({ error: this.props.t ? this.props.t('auth.errors.invalidEmail') : 'Bitte geben Sie eine gültige E-Mail-Adresse ein' }); return; } if (password !== confirmPassword) { this.setState({ error: this.props.t ? this.props.t('auth.errors.passwordsNotMatchShort') : 'Passwörter stimmen nicht überein' }); return; } if (password.length < 8) { this.setState({ error: this.props.t ? this.props.t('auth.passwordMinLength') : 'Das Passwort muss mindestens 8 Zeichen lang sein' }); return; } this.setState({ loading: true, error: '' }); window.socketManager.emit('createUser', { email, password }, (response) => { if (response.success) { this.setState({ loading: false, success: this.props.t ? this.props.t('auth.success.registerComplete') : 'Registrierung erfolgreich. Sie können sich jetzt anmelden.', tabValue: 0 // Switch to login tab }); } else { let errorMessage = this.props.t ? this.props.t('auth.errors.registerFailed') : 'Registrierung fehlgeschlagen'; if (response.cause === 'emailExists') { errorMessage = this.props.t ? this.props.t('auth.errors.emailExists') : 'Ein Benutzer mit dieser E-Mail-Adresse existiert bereits. Bitte verwenden Sie eine andere E-Mail-Adresse oder melden Sie sich an.'; } else if (response.message) { errorMessage = response.message; } this.setState({ loading: false, error: errorMessage }); } }); }; handleUserMenuClick = (event) => { this.setState({ anchorEl: event.currentTarget }); this.refreshPendingWireOrders(); }; handleUserMenuClose = () => { this.setState({ anchorEl: null }); }; handleLogout = () => { window.socketManager.emit('logout', (response) => { if(response.success){ sessionStorage.removeItem('user'); sessionStorage.removeItem('authToken'); window.dispatchEvent(new CustomEvent('userLoggedIn')); this.props.navigate('/'); this.setState({ user: null, isLoggedIn: false, isAdmin: false, anchorEl: null, pendingWirePaymentOrders: false, }); } }); }; handleForgotPassword = () => { const { email } = this.state; if (!email) { this.setState({ error: this.props.t ? this.props.t('auth.errors.enterEmail') : 'Bitte geben Sie Ihre E-Mail-Adresse ein' }); return; } if (!this.validateEmail(email)) { this.setState({ error: this.props.t ? this.props.t('auth.errors.invalidEmail') : 'Bitte geben Sie eine gültige E-Mail-Adresse ein' }); return; } this.setState({ loading: true, error: '' }); window.socketManager.emit('resetPassword', { email, domain: window.location.origin }, (response) => { console.log('Reset Password Response:', response); if (response.success) { this.setState({ loading: false, success: this.props.t ? this.props.t('auth.resetPassword.emailSent') : 'Ein Link zum Zurücksetzen des Passworts wurde an Ihre E-Mail-Adresse gesendet.' }); } else { this.setState({ loading: false, error: response.message || (this.props.t ? this.props.t('auth.resetPassword.emailError') : 'Fehler beim Senden der E-Mail') }); } }); }; // Google login functionality handleGoogleLoginSuccess = (credentialResponse) => { const { location, navigate } = this.props; this.setState({ loading: true, error: '' }); console.log('beforeG',credentialResponse) window.socketManager.emit('verifyGoogleUser', { credential: credentialResponse.credential }, (response) => { console.log('google respo',response); if (response.success) { persistSessionAuth(response); this.setState({ isLoggedIn: true, isAdmin: !!response.user.admin, user: response.user }); const redirectTo = location && location.hash ? `/profile${location.hash}` : '/profile'; const dispatchLoginEvent = () => { window.dispatchEvent(new CustomEvent('userLoggedIn')); navigate(redirectTo); }; try { const newCart = JSON.parse(response.user.cart); const localCartArr = window.cart ? Object.values(window.cart) : []; const serverCartArr = newCart ? Object.values(newCart) : []; if (serverCartArr.length === 0) { window.socketManager.emit('updateCart', window.cart); this.handleClose(); dispatchLoginEvent(); } else if (localCartArr.length === 0 && serverCartArr.length > 0) { window.cart = serverCartArr; window.dispatchEvent(new CustomEvent('cart')); this.handleClose(); dispatchLoginEvent(); } else if (cartsAreIdentical(localCartArr, serverCartArr)) { this.handleClose(); dispatchLoginEvent(); } else { this.setState({ cartSyncOpen: true, localCartSync: localCartArr, serverCartSync: serverCartArr, pendingNavigate: dispatchLoginEvent }); } } catch (error) { console.error('Error parsing cart:', response.user, error); this.handleClose(); dispatchLoginEvent(); } } else { this.setState({ loading: false, error: this.props.t ? this.props.t('auth.errors.googleLoginFailed') : 'Google-Anmeldung fehlgeschlagen', showGoogleAuth: false // Reset Google auth state on failed login }); } }); }; handleGoogleLoginError = (error) => { console.error('Google Login Error:', error); this.setState({ error: this.props.t ? this.props.t('auth.errors.googleLoginFailed') : 'Google-Anmeldung fehlgeschlagen', showGoogleAuth: false, // Reset Google auth state on error loading: false }); }; handleCartSyncConfirm = async (option) => { const { localCartSync, serverCartSync, pendingNavigate } = this.state; switch (option) { case 'useLocalArchive': localAndArchiveServer(localCartSync, serverCartSync); break; case 'deleteServer': window.socketManager.emit('updateCart', window.cart) break; case 'useServer': window.cart = serverCartSync; break; case 'merge': default: { const merged = mergeCarts(localCartSync, serverCartSync); console.log('MERGED CART RESULT:', merged); window.cart = merged; break; } } window.dispatchEvent(new CustomEvent('cart')); this.setState({ cartSyncOpen: false, localCartSync: [], serverCartSync: [], pendingNavigate: null }); this.handleClose(); if (pendingNavigate) pendingNavigate(); }; render() { const { open, tabValue, email, password, confirmPassword, error, loading, success, isLoggedIn, isAdmin, anchorEl, showGoogleAuth, cartSyncOpen, localCartSync, serverCartSync, privacyConfirmed, pendingWirePaymentOrders } = this.state; const { open: openProp, handleClose: handleCloseProp } = this.props; const isExternallyControlled = openProp !== undefined; return ( <> {!isExternallyControlled && ( isLoggedIn ? ( <> {this.props.t ? this.props.t('auth.menu.profile') : 'Profil'} {this.props.t ? this.props.t('auth.menu.checkout') : 'Bestellabschluss'} {this.props.t ? this.props.t('auth.menu.orders') : 'Bestellungen'} {pendingWirePaymentOrders ? ( [!] ) : null} {this.props.t ? this.props.t('auth.menu.settings') : 'Einstellungen'} {isAdmin ? {this.props.t ? this.props.t('auth.menu.adminDashboard') : 'Admin Dashboard'} : null} {isAdmin ? {this.props.t ? this.props.t('auth.menu.adminUsers') : 'Admin Users'} : null} {this.props.t ? this.props.t('auth.logout') : 'Abmelden'} ) : ( ) )} {tabValue === 0 ? (this.props.t ? this.props.t('auth.login') : 'Anmelden') : (this.props.t ? this.props.t('auth.register') : 'Registrieren') } {/* Google Sign In Button */} {!privacyConfirmed && ( {this.props.t ? <> {this.props.t('auth.privacyAccept')} {this.props.t('auth.privacyPolicy')} : <> Mit dem Click auf "Mit Google anmelden" akzeptiere ich die Datenschutzbestimmungen } )} {!showGoogleAuth && ( )} {showGoogleAuth && ( } disabled fullWidth style={{backgroundColor: '#4285F4', color: 'white' }} > Loading... }> )} {/* OR Divider */} {this.props.t ? this.props.t('auth.or') : 'ODER'} {error && {error}} {success && {success}} this.setState({ email: e.target.value })} disabled={loading} /> this.setState({ password: e.target.value })} disabled={loading} /> {tabValue === 0 && ( )} {tabValue === 1 && ( this.setState({ confirmPassword: e.target.value })} disabled={loading} /> )} {loading ? ( ) : ( )} { const { pendingNavigate } = this.state; this.setState({ cartSyncOpen: false, pendingNavigate: null }); this.handleClose(); if (pendingNavigate) pendingNavigate(); }} onConfirm={this.handleCartSyncConfirm} /> ); } } export default withRouter(withI18n()(LoginComponent));