'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 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 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('Failed to fetch news') 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 || 'Failed to load news') } finally { if (active) setNewsLoading(false) } })() return () => { active = false } }, []) // 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 // NEW: if everything is done, quickaction-dashboard is no longer accessible const allDone = 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]) const statusItems: StatusItem[] = [ { key: 'email', label: 'Email Verification', description: emailVerified ? 'Verified' : 'Missing', complete: emailVerified, icon: EnvelopeOpenIcon }, { key: 'id', label: 'ID Document', description: idUploaded ? 'Uploaded' : 'Missing', complete: idUploaded, icon: IdentificationIcon }, { key: 'info', label: 'Additional Info', description: additionalInfo ? 'Completed' : 'Missing', complete: additionalInfo, icon: InformationCircleIcon }, { key: 'contract', label: 'Contract', description: contractSigned ? 'Signed' : '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 && (
Redirecting…
Taking you to your dashboard
)}
{/* Title */}

Welcome{isClient && user?.firstName ? `, ${user.firstName}` : ''}!

{isClient && user?.userType === 'company' ? 'Company Account' : 'Personal Account'}

{loading &&

Loading status...

} {error && (

Error loading account status

{error}

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

Status Overview

{/* CHANGED: mobile 2x2 grid */}
{statusItems.map(item => { const CompleteIcon = item.complete ? CheckCircleIcon : XCircleIcon return (
{item.label} {item.description}
) })}
{/* Quick Actions */}
i

Quick Actions

{/* CHANGED: mobile 2x2 grid (order already matches desired layout) */}
{/* Email Verification */}
{/* NEW: resend feedback (only when not verified) */} {!emailVerified && (

{resendRemainingSec > 0 ? `Resend available in ${formatMmSs(resendRemainingSec)}` : 'You can request a new code now'}

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

Complete previous steps (email, ID, profile) before signing the contract.

)}
{/* Latest News */}

Latest News

View all
{newsLoading && (
{Array.from({ length: 3 }).map((_, i) => (
))}
)} {newsError && !newsLoading && (
{newsError}
)} {!newsLoading && !newsError && latestNews.length === 0 && (
No news yet.
)} {!newsLoading && !newsError && latestNews.length > 0 && (
    {latestNews.map(item => (
  • {item.published_at ? new Date(item.published_at).toLocaleDateString('de-DE') : 'Recent'}
    {item.title}
    {item.summary && (
    {item.summary}
    )}
  • ))}
)}
{/* Tutorial Modal */}
) }