"use client"; import { use, useState, useEffect } from "react"; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { GitBranch, ChevronRight, Search, Lightbulb, ShoppingCart, UserPlus, Rocket, Zap, HelpCircle, CreditCard, Loader2, CheckCircle2, Circle, X, Palette, Sparkles, } from "lucide-react"; import { toast } from "sonner"; import { CollapsibleSidebar } from "@/components/ui/collapsible-sidebar"; interface WorkItem { id: string; title: string; path: string; status: "built" | "in_progress" | "missing"; category: string; sessionsCount: number; commitsCount: number; journeyStage?: string; } interface AssetNode { id: string; name: string; asset_type: string; must_have_for_v1: boolean; asset_metadata: { why_it_exists: string; which_user_it_serves?: string; problem_it_helps_with?: string; connection_to_magic_moment: string; journey_stage?: string; visual_style_notes?: string; implementation_notes?: string; }; children?: AssetNode[]; } interface JourneyStage { id: string; name: string; icon: any; description: string; color: string; items: WorkItem[]; assets: AssetNode[]; // Visual assets for this stage } export default function JourneyPage({ params, }: { params: Promise<{ workspace: string; projectId: string }>; }) { const { workspace, projectId } = use(params); const [workItems, setWorkItems] = useState([]); const [touchpointAssets, setTouchpointAssets] = useState([]); const [loading, setLoading] = useState(true); const [selectedStage, setSelectedStage] = useState(null); const [journeyStages, setJourneyStages] = useState([]); useEffect(() => { loadJourneyData(); }, [projectId]); const loadJourneyData = async () => { try { setLoading(true); // Load work items for stats const timelineResponse = await fetch(`/api/projects/${projectId}/timeline-view`); if (timelineResponse.ok) { const timelineData = await timelineResponse.json(); setWorkItems(timelineData.workItems); } // Load AI-generated touchpoints tree const mvpResponse = await fetch(`/api/projects/${projectId}/mvp-checklist`); if (mvpResponse.ok) { const mvpData = await mvpResponse.json(); // Extract touchpoints from AI response if it exists if (mvpData.aiGenerated && mvpData.touchpointsTree) { const allTouchpoints = flattenAssetNodes(mvpData.touchpointsTree.nodes || []); setTouchpointAssets(allTouchpoints); } } // Build journey stages from both work items and touchpoint assets const stages = buildJourneyStages(workItems, touchpointAssets); setJourneyStages(stages); } catch (error) { console.error("Error loading journey data:", error); toast.error("Failed to load journey data"); } finally { setLoading(false); } }; // Flatten nested asset nodes const flattenAssetNodes = (nodes: AssetNode[]): AssetNode[] => { const flattened: AssetNode[] = []; const flatten = (node: AssetNode) => { flattened.push(node); if (node.children && node.children.length > 0) { node.children.forEach(child => flatten(child)); } }; nodes.forEach(node => flatten(node)); return flattened; }; const getJourneySection = (item: WorkItem): string => { const title = item.title.toLowerCase(); const path = item.path.toLowerCase(); // Discovery if (path === '/' || title.includes('landing') || title.includes('marketing page')) return 'Discovery'; if (item.category === 'Social' && !path.includes('settings')) return 'Discovery'; // Research if (item.category === 'Content' || path.includes('/docs')) return 'Research'; if (title.includes('marketing dashboard')) return 'Research'; // Onboarding if (path.includes('auth') || path.includes('oauth')) return 'Onboarding'; if (path.includes('signup') || path.includes('signin')) return 'Onboarding'; // First Use if (title.includes('onboarding')) return 'First Use'; if (title.includes('getting started')) return 'First Use'; if (path.includes('workspace') && !path.includes('settings')) return 'First Use'; if (title.includes('creation flow')) return 'First Use'; // Active if (path.includes('overview') || path.includes('/dashboard')) return 'Active'; if (path.includes('timeline-plan') || path.includes('audit') || path.includes('mission')) return 'Active'; if (path.includes('/api/projects') || path.includes('mvp-checklist')) return 'Active'; // Support if (path.includes('settings')) return 'Support'; // Purchase if (path.includes('billing') || path.includes('payment')) return 'Purchase'; return 'Active'; }; const buildJourneyStages = (items: WorkItem[], assets: AssetNode[]): JourneyStage[] => { const stageDefinitions = [ { id: "discovery", name: "Discovery", stageMappings: ["Awareness", "Discovery"], icon: Search, description: "Found you online via social, blog, or ad", color: "bg-blue-100 border-blue-300 text-blue-900", }, { id: "research", name: "Research", stageMappings: ["Curiosity", "Research"], icon: Lightbulb, description: "Checking out features, pricing, and value", color: "bg-purple-100 border-purple-300 text-purple-900", }, { id: "onboarding", name: "Onboarding", stageMappings: ["First Try", "Onboarding"], icon: UserPlus, description: "Creating account to try the product", color: "bg-green-100 border-green-300 text-green-900", }, { id: "first-use", name: "First Use", stageMappings: ["First Real Day", "First Use"], icon: Rocket, description: "Zero to experiencing the magic", color: "bg-orange-100 border-orange-300 text-orange-900", }, { id: "active", name: "Active", stageMappings: ["Habit", "Active", "Post-MVP"], icon: Zap, description: "Using the magic repeatedly", color: "bg-yellow-100 border-yellow-300 text-yellow-900", }, { id: "support", name: "Support", stageMappings: ["Support"], icon: HelpCircle, description: "Getting help to maximize value", color: "bg-indigo-100 border-indigo-300 text-indigo-900", }, { id: "purchase", name: "Purchase", stageMappings: ["Decision to Pay", "Purchase"], icon: CreditCard, description: "Time to pay to keep using", color: "bg-pink-100 border-pink-300 text-pink-900", }, ]; return stageDefinitions.map(stage => { // Get work items for this stage const stageItems = items.filter(item => { const section = getJourneySection(item); return section === stage.name; }); // Get touchpoint assets for this stage from AI-generated metadata const stageAssets = assets.filter(asset => { const assetJourneyStage = asset.asset_metadata?.journey_stage || ''; return stage.stageMappings.some(mapping => assetJourneyStage.toLowerCase().includes(mapping.toLowerCase()) ); }); return { ...stage, items: stageItems, assets: stageAssets, }; }); }; const getStatusIcon = (status: string) => { if (status === "built") return ; if (status === "in_progress") return ; return ; }; const selectedStageData = journeyStages.find(s => s.id === selectedStage); return (
{/* Left Sidebar */}

Journey Stats

Stages {journeyStages.length}
Total Assets {journeyStages.reduce((sum, stage) => sum + stage.assets.length, 0)}
Work Items {workItems.length}
{/* Main Content */}
{/* Header */}

Customer Journey

Track touchpoints across the customer lifecycle

{loading ? (
) : (
{/* Journey Flow */}
{journeyStages.map((stage, index) => (
{/* Stage Card */} setSelectedStage(stage.id)} >
{/* Header */}

{stage.name}

{stage.items.length}
{/* Description */}

{stage.description}

{/* Stats */}
{stage.items.filter(i => i.status === "built").length} built
{stage.items.filter(i => i.status === "in_progress").length} in progress
{/* Progress Bar */}
0 ? (stage.items.filter(i => i.status === "built").length / stage.items.length) * 100 : 0 }%`, }} />
{/* Connector Arrow */} {index < journeyStages.length - 1 && ( )}
))}
{/* Stage Details Panel */} {selectedStageData && (

{selectedStageData.name} Touchpoints

{selectedStageData.description}

{selectedStageData.assets.length === 0 && selectedStageData.items.length === 0 ? (

No assets defined for this stage yet

) : (
{/* AI-Generated Visual Assets */} {selectedStageData.assets.length > 0 && (

Visual Assets

{selectedStageData.assets.map((asset) => ( {/* Visual Preview */}
{/* Placeholder for actual design preview */}

{asset.name}

{/* V1 Badge */} {asset.must_have_for_v1 && (
V1
)} {/* Asset Type Badge */}
{asset.asset_type.replace('_', ' ')}
{/* Hover overlay */}
{/* Card Content */}

{asset.name}

{asset.asset_metadata?.why_it_exists}

{asset.asset_metadata?.visual_style_notes && (

Style:{" "} {asset.asset_metadata.visual_style_notes}

)}
{asset.asset_metadata?.which_user_it_serves || "All users"}
))}
)} {/* Existing Work Items */} {selectedStageData.items.length > 0 && (

Existing Work

{selectedStageData.items.map((item) => (
{getStatusIcon(item.status)}

{item.title}

{item.path}

{item.sessionsCount} sessions {item.commitsCount} commits
{item.status === "built" ? "Done" : item.status === "in_progress" ? "In Progress" : "To-do"}
))}
)}
)}
)}
)}
{/* End Main Content */}
); }