'use client' import { useState, useCallback, useEffect, useRef } from 'react' import Link from 'next/link' import { useRouter } from 'next/navigation' import PageLayout from '../components/PageLayout' import TutorialModal, { createTutorialSteps } from '../components/TutorialModal' import useAuthStore from '../store/authStore' import { useUserStatus } from '../hooks/useUserStatus' import { useTranslation } from '../i18n/useTranslation' import BlueBlurryBackground from '../components/background/blueblurry' // NEW import { CheckCircleIcon, XCircleIcon, EnvelopeOpenIcon, IdentificationIcon, InformationCircleIcon, DocumentCheckIcon, ArrowUpOnSquareIcon, PencilSquareIcon, ClipboardDocumentCheckIcon, AcademicCapIcon } from '@heroicons/react/24/outline' interface StatusItem { key: string label: string description: string complete: boolean icon: React.ComponentType> } type LatestNewsItem = { id: number title: string summary?: string slug: string category?: string imageUrl?: string published_at?: string | null } export default function QuickActionDashboardPage() { const router = useRouter() const { t } = useTranslation() const user = useAuthStore(s => s.user) const isAuthReady = useAuthStore(s => (s as any).isAuthReady) // NEW const accessToken = useAuthStore(s => s.accessToken) // NEW const { userStatus, loading, error, refreshStatus } = useUserStatus() const [isClient, setIsClient] = useState(false) const [latestNews, setLatestNews] = useState([]) const [newsLoading, setNewsLoading] = useState(false) const [newsError, setNewsError] = useState(null) // Tutorial state const [isTutorialOpen, setIsTutorialOpen] = useState(false) const [currentTutorialStep, setCurrentTutorialStep] = useState(1) const [hasSeenTutorial, setHasSeenTutorial] = useState(false) const forceOpenRef = useRef(false) useEffect(() => { setIsClient(true) // Check if user has seen tutorial before const tutorialSeen = localStorage.getItem('tutorial_seen') setHasSeenTutorial(!!tutorialSeen) }, []) useEffect(() => { let active = true ;(async () => { setNewsLoading(true) setNewsError(null) try { const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001' const res = await fetch(`${BASE_URL}/api/news/active`) if (!res.ok) throw new Error(t('quickactionDashboard.noNewsYet')) const json = await res.json() const data = Array.isArray(json.data) ? json.data : [] if (active) setLatestNews(data.slice(0, 3)) } catch (e: any) { if (active) setNewsError(e?.message || t('quickactionDashboard.noNewsYet')) } finally { if (active) setNewsLoading(false) } })() return () => { active = false } }, [t]) // Derive status from real backend data const emailVerified = userStatus?.email_verified || false const idUploaded = userStatus?.documents_uploaded || false const additionalInfo = userStatus?.profile_completed || false const contractSigned = userStatus?.contract_signed || false // Detect guest user — guests only need email verification const isGuest = isClient && user?.role === 'guest' // For guests: only email verification matters. For regular users: all 4 steps. const allDone = isGuest ? emailVerified : emailVerified && idUploaded && additionalInfo && contractSigned // NEW: smooth redirect (prevents snappy double navigation) const [redirectTo, setRedirectTo] = useState(null) const redirectOnceRef = useRef(false) const smoothReplace = useCallback((to: string) => { if (redirectOnceRef.current) return redirectOnceRef.current = true setRedirectTo(to) window.setTimeout(() => router.replace(to), 200) }, [router]) useEffect(() => { if (!isClient) return if (loading || !userStatus) return const urlParams = new URLSearchParams(window.location.search) const forceOpen = urlParams.get('tutorial') === 'true' if (allDone && !forceOpen) smoothReplace('/dashboard') }, [isClient, loading, userStatus, allDone, smoothReplace]) // NEW: decide which tutorial step to start on const getNextTutorialStep = useCallback(() => { const noneCompleted = !emailVerified && !idUploaded && !additionalInfo && !contractSigned if (noneCompleted) return 1 if (!emailVerified) return 2 if (!idUploaded) return 3 if (!additionalInfo) return 4 if (!contractSigned) return 5 return 6 }, [emailVerified, idUploaded, additionalInfo, contractSigned]) // CHANGED: single auto-open mechanism (works even if tutorial_seen exists) useEffect(() => { if (!isClient) return const urlParams = new URLSearchParams(window.location.search) const forceOpen = urlParams.get('tutorial') === 'true' if (forceOpen && !forceOpenRef.current && !isTutorialOpen) { // Open immediately to avoid dashboard flash; step will be corrected once status loads setCurrentTutorialStep(1) setIsTutorialOpen(true) forceOpenRef.current = true return } if (loading || !userStatus) return if (isTutorialOpen && forceOpenRef.current) { setCurrentTutorialStep(getNextTutorialStep()) } if (isTutorialOpen) return const uid = (user as any)?.id ?? (user as any)?._id ?? (user as any)?.userId ?? (user as any)?.email ?? null if (!uid) return const tokenSuffix = (accessToken || '').slice(-12) || 'no-token' const sessionKey = `pp:tutorialShown:${uid}:${tokenSuffix}` if (forceOpen) { window.history.replaceState({}, '', window.location.pathname) } const alreadyShownThisLogin = sessionStorage.getItem(sessionKey) === '1' if (alreadyShownThisLogin && !forceOpen) return setCurrentTutorialStep(getNextTutorialStep()) setIsTutorialOpen(true) sessionStorage.setItem(sessionKey, '1') }, [isClient, loading, userStatus, isTutorialOpen, user, accessToken, getNextTutorialStep]) // For guests: only show email verification step. For regular users: all 4 steps. const statusItems: StatusItem[] = isGuest ? [ { key: 'email', label: t('quickactionDashboard.statusCards.emailVerification'), description: emailVerified ? t('quickactionDashboard.statusCards.verified') : t('quickactionDashboard.statusCards.missing'), complete: emailVerified, icon: EnvelopeOpenIcon } ] : [ { key: 'email', label: t('quickactionDashboard.statusCards.emailVerification'), description: emailVerified ? t('quickactionDashboard.statusCards.verified') : t('quickactionDashboard.statusCards.missing'), complete: emailVerified, icon: EnvelopeOpenIcon }, { key: 'id', label: t('quickactionDashboard.statusCards.idDocument'), description: idUploaded ? t('quickactionDashboard.statusCards.uploaded') : t('quickactionDashboard.statusCards.missing'), complete: idUploaded, icon: IdentificationIcon }, { key: 'info', label: t('quickactionDashboard.statusCards.additionalInfo'), description: additionalInfo ? t('quickactionDashboard.completed') : t('quickactionDashboard.statusCards.missing'), complete: additionalInfo, icon: InformationCircleIcon }, { key: 'contract', label: t('quickactionDashboard.statusCards.contract'), description: contractSigned ? t('quickactionDashboard.statusCards.signed') : t('quickactionDashboard.statusCards.missing'), complete: contractSigned, icon: DocumentCheckIcon } ] // Action handlers - navigate to proper QuickAction pages with tutorial callback const handleVerifyEmail = useCallback(() => { if (emailVerified) return router.push('/quickaction-dashboard/register-email-verify?tutorial=true') }, [router, emailVerified]) const handleUploadId = useCallback(() => { if (idUploaded) return const userType = user?.userType || 'personal' router.push(`/quickaction-dashboard/register-upload-id/${userType}?tutorial=true`) }, [router, user, idUploaded]) const handleCompleteInfo = useCallback(() => { if (additionalInfo) return const userType = user?.userType || 'personal' router.push(`/quickaction-dashboard/register-additional-information/${userType}?tutorial=true`) }, [router, user, additionalInfo]) const handleSignContract = useCallback(() => { if (contractSigned) return const userType = user?.userType || 'personal' router.push(`/quickaction-dashboard/register-sign-contract/${userType}?tutorial=true`) }, [router, user, contractSigned]) // Tutorial handlers const startTutorial = useCallback(() => { setCurrentTutorialStep(1) setIsTutorialOpen(true) }, []) const closeTutorial = useCallback(() => { setIsTutorialOpen(false) localStorage.setItem('tutorial_seen', 'true') setHasSeenTutorial(true) if (allDone) smoothReplace('/dashboard') }, [allDone, smoothReplace]) const nextTutorialStep = useCallback(() => { setCurrentTutorialStep(prev => prev + 1) }, []) const previousTutorialStep = useCallback(() => { setCurrentTutorialStep(prev => Math.max(1, prev - 1)) }, []) // Create tutorial steps const tutorialSteps = createTutorialSteps( emailVerified, idUploaded, additionalInfo, contractSigned, user?.userType || 'personal', handleVerifyEmail, handleUploadId, handleCompleteInfo, handleSignContract, closeTutorial, () => setCurrentTutorialStep(prev => prev + 1) // onNext function ) const canSignContract = emailVerified && idUploaded && additionalInfo // NEW: resend cooldown tracking (10 minutes like verify page) const RESEND_INTERVAL_MS = 10 * 60 * 1000 const getStorageKey = (email?: string | null) => `emailVerify:lastSent:${email || 'anon'}` const getLastSentAt = (email?: string | null) => { if (typeof window === 'undefined') return 0 try { return parseInt(localStorage.getItem(getStorageKey(email)) || '0', 10) || 0 } catch { return 0 } } const [resendRemainingSec, setResendRemainingSec] = useState(0) const formatMmSs = (total: number) => { const m = Math.floor(total / 60) const s = total % 60 return `${m}:${String(s).padStart(2, '0')}` } useEffect(() => { if (!isClient || emailVerified) return const last = getLastSentAt(user?.email) const remainingMs = Math.max(0, RESEND_INTERVAL_MS - (Date.now() - last)) setResendRemainingSec(Math.ceil(remainingMs / 1000)) if (remainingMs <= 0) return const id = setInterval(() => { setResendRemainingSec(s => (s > 0 ? s - 1 : 0)) }, 1000) return () => clearInterval(id) }, [isClient, emailVerified, user?.email]) // NEW: only logged-in users can access quickaction dashboard useEffect(() => { if (!isClient) return if (!isAuthReady) return if (!user) smoothReplace('/login') }, [isClient, isAuthReady, user, smoothReplace]) return ( {/* NEW: smooth redirect overlay */} {redirectTo && (
{t('quickactionDashboard.redirecting')}
{t('quickactionDashboard.takingToDashboard')}
)}
{/* Title */}

{t('dashboard.welcomeBack')}{isClient && user?.firstName ? `, ${user.firstName}` : ''}!

{isGuest ? t('quickactionDashboard.guestAccount') : isClient && user?.userType === 'company' ? t('quickactionDashboard.companyAccount') : t('quickactionDashboard.personalAccount')}

{loading &&

{t('quickactionDashboard.loadingStatus')}

} {error && (

{t('quickactionDashboard.errorLoadingAccountStatus')}

{error}

)}
{/* Cards */}
{/* Status Overview */}

{isGuest ? t('quickactionDashboard.emailVerificationStatus') : t('quickactionDashboard.statusOverview')}

{/* Guest: single centered card. Regular: 2x2 / 4-col grid */}
{statusItems.map(item => { const CompleteIcon = item.complete ? CheckCircleIcon : XCircleIcon return (
{item.label} {item.description}
) })}
{/* Quick Actions */}
i

{isGuest ? t('quickactionDashboard.actionRequired') : t('quickactionDashboard.quickActions')}

{/* Tutorial button — only for regular users */} {!isGuest && ( )}
{isGuest ? ( /* ── Guest view: single email verification action ── */

{t('quickactionDashboard.pleaseVerifyEmailAddress')}

{!emailVerified && (

{resendRemainingSec > 0 ? `${t('quickactionDashboard.resendAvailableIn')} ${formatMmSs(resendRemainingSec)}` : t('quickactionDashboard.requestNewCode')}

)}
) : ( /* ── Regular user view: all 4 quick action buttons ── */
{/* Email Verification */}
{!emailVerified && (

{resendRemainingSec > 0 ? `${t('quickactionDashboard.resendAvailableIn')} ${formatMmSs(resendRemainingSec)}` : t('quickactionDashboard.requestNewCode')}

)}
{/* ID Upload */} {/* Additional Info */} {/* Sign Contract */}
{!canSignContract && !contractSigned && (

{t('quickactionDashboard.contractNotReady')}

)}
)}
{/* Latest News */}

{t('quickactionDashboard.latestNews')}

{t('quickactionDashboard.viewAll')}
{newsLoading && (
{Array.from({ length: 3 }).map((_, i) => (
))}
)} {newsError && !newsLoading && (
{newsError}
)} {!newsLoading && !newsError && latestNews.length === 0 && (
{t('quickactionDashboard.noNewsYet')}
)} {!newsLoading && !newsError && latestNews.length > 0 && (
    {latestNews.map(item => (
  • {item.published_at ? new Date(item.published_at).toLocaleDateString('de-DE') : t('quickactionDashboard.recent')}
    {item.title}
    {item.summary && (
    {item.summary}
    )}
  • ))}
)}
{/* Tutorial Modal */}
) }