MicProtocol / apps /web /src /components /help /HelpSystem.tsx
Benaah's picture
Upload folder using huggingface_hub
fb12607 verified
Raw History Blame Contribute Delete
53.6 kB
"use client"; import React, { useState, useCallback, useMemo, useEffect, ReactNode } from "react"; import { X, Search, BookOpen, Rocket, Users, Calendar, Mic, Scissors, Send, FileText, Settings, HelpCircle, ChevronRight, Keyboard, Lightbulb, Zap, Globe, Play, BarChart3, ExternalLink, MessageSquare, Sparkles, } from "lucide-react"; // ─── Help Content Types ──────────────────────────────── interface HelpSection { id: string; title: string; icon: React.ElementType; color: string; articles: HelpArticle[]; } interface HelpArticle { id: string; title: string; content: ReactNode; shortcut?: string; tips?: string[]; } // ─── Help Content Database ───────────────────────────── const helpSections: HelpSection[] = [ { id: "getting-started", title: "Getting Started", icon: Rocket, color: "var(--color-primary)", articles: [ { id: "welcome", title: "Welcome to MicProtocol", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> MicProtocol is your all-in-one platform for podcast guest management. From discovering guests to publishing episodes, we handle the entire workflow so you can focus on creating great content. </p> <h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>Quick Start Checklist:</h4> <ol style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-2)" }}> <li>Create your first podcast</li> <li>Add guests to your Pipeline</li> <li>Send outreach sequences</li> <li>Schedule recordings</li> <li>Record with MicProtocol Studio</li> <li>Create viral clips in Content Studio</li> </ol> </div> ), tips: [ "Press ? or Ctrl+/ anytime to see all keyboard shortcuts", "Use Ctrl+K to quickly search across the platform", ], }, { id: "keyboard-shortcuts", title: "Keyboard Shortcuts", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> MicProtocol is designed for power users. Use keyboard shortcuts to navigate and perform actions without touching your mouse. </p> <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "var(--space-3)" }}> <ShortcutCard shortcut="Ctrl + K" description="Focus search bar" /> <ShortcutCard shortcut="?" description="Show all shortcuts" /> <ShortcutCard shortcut="Ctrl + 1-9" description="Navigate to section" /> <ShortcutCard shortcut="N" description="Add new item (context-aware)" /> <ShortcutCard shortcut="V" description="Toggle view mode" /> <ShortcutCard shortcut="Escape" description="Close / clear selection" /> </div> </div> ), }, { id: "navigation", title: "Navigating the Dashboard", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> The sidebar provides quick access to all major sections. The active section is highlighted with an orange indicator bar. </p> <div style={{ display: "flex", flexDirection: "column", gap: "var(--space-2)" }}> <NavHelpItem icon={<BarChart3 size={16} />} label="Dashboard" description="Overview of your podcast activity" /> <NavHelpItem icon={<Users size={16} />} label="Guest Pipeline" description="Kanban board for managing guest stages" /> <NavHelpItem icon={<Calendar size={16} />} label="Schedule" description="Calendar view of recording sessions" /> <NavHelpItem icon={<Scissors size={16} />} label="Content Studio" description="AI-powered clip detection and export" /> <NavHelpItem icon={<Globe size={16} />} label="Network" description="Discover guests in the verified network" /> <NavHelpItem icon={<Send size={16} />} label="Outreach" description="Email sequences and engagement tracking" /> <NavHelpItem icon={<FileText size={16} />} label="Diary" description="Content planning and notes" /> </div> </div> ), }, ], }, { id: "guest-pipeline", title: "Guest Pipeline", icon: Users, color: "var(--status-prospect)", articles: [ { id: "pipeline-overview", title: "Understanding the Pipeline", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> The Guest Pipeline is a kanban board that tracks guests through five stages: <strong>Prospect</strong> β†’ <strong>Contacted</strong> β†’ <strong>Scheduled</strong> β†’ <strong>Recorded</strong> β†’ <strong>Published</strong>. </p> <p style={{ marginBottom: "var(--space-3)" }}> Drag and drop guest cards between columns to update their status. The pipeline provides a visual overview of your entire guest workflow. </p> <div style={{ padding: "var(--space-3)", background: "var(--bg-secondary)", borderRadius: "var(--radius-md)", borderLeft: "3px solid var(--color-primary)" }}> <strong>Pro Tip:</strong> Use the priority filter to focus on high-value guests first. High priority (70+) guests are most likely to convert. </div> </div> ), tips: [ "Drag cards between columns to change guest status", "Use Ctrl+A to select all visible guests for bulk actions", "Press N to quickly add a new guest", ], }, { id: "adding-guests", title: "Adding Guests", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Click the &quot;Add Guest&quot; button or press <kbd>N</kbd> to open the add guest modal. You&apos;ll need at minimum a name and email address. </p> <h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>Guest Fields:</h4> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> <li><strong>Full Name</strong> β€” Required. The guest&apos;s display name.</li> <li><strong>Email</strong> β€” Required. Used for outreach sequences.</li> <li><strong>Company</strong> β€” Optional. Helps with personalization.</li> <li><strong>Title</strong> β€” Optional. Job title or role.</li> <li><strong>Bio</strong> β€” Optional. Auto-enriched from LinkedIn when available.</li> <li><strong>Social Links</strong> β€” Optional. LinkedIn, Twitter, website.</li> </ul> </div> ), }, { id: "guest-detail", title: "Guest Detail Panel", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Click any guest card to open the detail panel. This gives you a complete view of the guest including contact info, technical comfort profile, timeline, and episodes. </p> <h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>Tabs:</h4> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> <li><strong>Overview</strong> β€” Contact info, bio, technical profile, tags, notes</li> <li><strong>Timeline</strong> β€” Journey events, outreach history, notes</li> <li><strong>Episodes</strong> β€” Scheduled and past episodes with this guest</li> </ul> </div> ), tips: [ "Press Escape to close the detail panel", "The technical profile helps you prepare for recording sessions", ], }, { id: "bulk-actions", title: "Bulk Actions", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Select multiple guests using checkboxes (or press <kbd>Ctrl+A</kbd> to select all). A bulk actions bar will appear at the top. </p> <h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>Available Bulk Actions:</h4> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> <li><strong>Move to Status</strong> β€” Batch update guest status</li> <li><strong>Delete</strong> β€” Remove multiple guests at once</li> </ul> </div> ), }, ], }, { id: "scheduling", title: "Scheduling", icon: Calendar, color: "var(--status-scheduled)", articles: [ { id: "schedule-overview", title: "Managing Your Schedule", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> The Schedule page shows a weekly calendar view of your recording sessions. Navigate between weeks using the arrow buttons or keyboard shortcuts. </p> <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "var(--space-3)" }}> <ShortcutCard shortcut="←" description="Previous week" /> <ShortcutCard shortcut="β†’" description="Next week" /> <ShortcutCard shortcut="T" description="Go to today" /> <ShortcutCard shortcut="A" description="Add recording slot" /> </div> </div> ), }, { id: "recording-slots", title: "Recording Slots", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Click &quot;Add Slot&quot; to create a new recording session. You&apos;ll need to select an episode, date, and time range. </p> <h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>Slot Fields:</h4> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> <li><strong>Episode</strong> β€” Select a scheduled episode</li> <li><strong>Date</strong> β€” Recording date</li> <li><strong>Start/End Time</strong> β€” Time window for the session</li> <li><strong>Timezone</strong> β€” Auto-detected from your browser</li> <li><strong>Meet Link</strong> β€” Optional. Zoom, Google Meet, etc.</li> </ul> </div> ), }, ], }, { id: "recording", title: "Recording Studio", icon: Mic, color: "var(--color-error)", articles: [ { id: "studio-overview", title: "MicProtocol Studio", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> MicProtocol Studio provides local-first, lossless double-ender recording. Audio is recorded locally on each participant&apos;s device and synced via NTP timestamps for perfect quality. </p> <div style={{ padding: "var(--space-3)", background: "var(--bg-secondary)", borderRadius: "var(--radius-md)", borderLeft: "3px solid var(--color-error)" }}> <strong>Important:</strong> Both host and guest should use the Studio for the best quality. The recording continues even if the internet connection drops. </div> </div> ), }, { id: "recording-controls", title: "Recording Controls", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> The recording interface is simple and distraction-free. Use keyboard shortcuts for quick control during recording. </p> <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "var(--space-3)" }}> <ShortcutCard shortcut="Space" description="Start/Stop recording" /> <ShortcutCard shortcut="M" description="Toggle mute" /> </div> <div style={{ marginTop: "var(--space-3)", padding: "var(--space-3)", background: "var(--bg-secondary)", borderRadius: "var(--radius-md)" }}> <strong>Mute Warning:</strong> If you&apos;re speaking while muted, a visual alert will appear. This prevents &quot;Can you hear me?&quot; moments. </div> </div> ), tips: [ "The timer glows red during recording for visibility", "Audio chunks are persisted to IndexedDB as a safety net", "Storage estimation helps avoid mid-recording space issues", ], }, { id: "double-ender", title: "Double-End Recording", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Double-ender recording means each participant records their own audio locally. This eliminates quality issues from internet compression and provides studio-quality audio for post-production. </p> <h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>How It Works:</h4> <ol style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-2)" }}> <li>Both participants join the Studio session</li> <li>Audio is recorded locally on each device</li> <li>NTP clock sync ensures timestamps align</li> <li>After recording, tracks are uploaded and synced</li> <li>The Content Studio processes the merged audio</li> </ol> </div> ), }, ], }, { id: "content-studio", title: "Content Studio", icon: Scissors, color: "var(--color-success)", articles: [ { id: "studio-overview", title: "Content Studio Overview", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Content Studio uses AI to automatically detect viral moments in your episodes and generate platform-ready clips. Transform long-form content into TikTok, YouTube Shorts, LinkedIn, and Twitter content. </p> <div style={{ display: "flex", flexDirection: "column", gap: "var(--space-2)" }}> <PlatformBadge name="TikTok / Reels" aspect="9:16" color="#00F2EA" /> <PlatformBadge name="YouTube Shorts" aspect="16:9" color="#FF0000" /> <PlatformBadge name="LinkedIn" aspect="1:1" color="#0A66C2" /> <PlatformBadge name="Twitter / X" aspect="16:9" color="#1DA1F2" /> </div> </div> ), tips: [ "Press D to auto-detect clips from the latest episode", "High virality scores (90+) indicate strong viral potential", ], }, { id: "clip-detection", title: "AI Clip Detection", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Click &quot;Auto-Detect Clips&quot; or press <kbd>D</kbd> to analyze your latest recorded episode. The AI identifies engaging moments based on: </p> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> <li>Emotional peaks and energy spikes</li> <li>Contrarian or surprising statements</li> <li>Actionable advice and frameworks</li> <li>Storytelling moments</li> <li>Quote-worthy soundbites</li> </ul> </div> ), }, { id: "exporting", title: "Exporting Clips", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Once you approve a clip, click &quot;Export&quot; to generate a platform-ready file. Choose your target platform and the clip will be reformatted with the correct aspect ratio and dimensions. </p> <h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>Export includes:</h4> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> <li>Correct aspect ratio for the platform</li> <li>Suggested caption and hashtags</li> <li>Subtitle burn-in (when available)</li> <li>Optimized encoding for each platform</li> </ul> </div> ), }, ], }, { id: "outreach", title: "Outreach", icon: Send, color: "var(--color-info)", articles: [ { id: "outreach-overview", title: "Email Sequences", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Outreach sequences automate your guest communication. Create multi-step email sequences that adapt based on guest responses. </p> <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "var(--space-4)" }}> <StatCard label="Open Rate" description="Track email opens" /> <StatCard label="Reply Rate" description="Track guest responses" /> </div> </div> ), }, { id: "creating-sequences", title: "Creating Sequences", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> To start an outreach sequence, open a guest in the Pipeline and click &quot;Send Outreach&quot;. You can customize the email template and timing for each step. </p> <h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>Sequence Steps:</h4> <ol style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-2)" }}> <li><strong>Initial Outreach</strong> β€” Introduction and invitation</li> <li><strong>Follow-up</strong> β€” Reminder if no response</li> <li><strong>Last Chance</strong> β€” Final attempt with urgency</li> </ol> </div> ), }, ], }, { id: "network", title: "Guest Network", icon: Globe, color: "var(--color-secondary)", articles: [ { id: "network-overview", title: "Discovering Guests", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> The Guest Network is a verified marketplace of podcast guests. Browse experts by category, view their profiles, and invite them to your show. </p> <div style={{ padding: "var(--space-3)", background: "var(--bg-secondary)", borderRadius: "var(--radius-md)", borderLeft: "3px solid var(--color-secondary)" }}> <strong>Verification:</strong> All network guests have verified profiles with social links, bio, and episode history. Look for the verification badge. </div> </div> ), }, ], }, { id: "settings", title: "Settings", icon: Settings, color: "var(--text-tertiary)", articles: [ { id: "profile", title: "Profile Settings", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Manage your account details, avatar, and notification preferences. Your profile information is visible to guests in the network. </p> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> <li><strong>Avatar</strong> β€” Upload a professional photo</li> <li><strong>Display Name</strong> β€” How you appear to guests</li> <li><strong>Email Notifications</strong> β€” Configure alert preferences</li> </ul> </div> ), }, { id: "platform", title: "Platform Settings", content: ( <div> <p style={{ marginBottom: "var(--space-3)" }}> Configure your podcast platform integrations, default recording settings, and team permissions. </p> </div> ), }, ], }, ]; // ─── Helper Components ───────────────────────────────── function ShortcutCard({ shortcut, description }: { shortcut: string; description: string }) { return ( <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "var(--space-2) var(--space-3)", background: "var(--bg-secondary)", borderRadius: "var(--radius-md)", border: "1px solid var(--border-color-subtle)", }} > <span style={{ fontSize: "var(--text-sm)", color: "var(--text-secondary)" }}>{description}</span> <kbd style={{ display: "inline-flex", alignItems: "center", padding: "2px 8px", background: "var(--bg-tertiary)", border: "1px solid var(--border-color)", borderRadius: "var(--radius-sm)", fontSize: "var(--text-xs)", fontWeight: 600, fontFamily: "var(--font-mono, monospace)", color: "var(--text-primary)", minWidth: 24, justifyContent: "center", lineHeight: "1.6", }} > {shortcut} </kbd> </div> ); } function NavHelpItem({ icon, label, description }: { icon: ReactNode; label: string; description: string }) { return ( <div style={{ display: "flex", alignItems: "center", gap: "var(--space-3)", padding: "var(--space-2) var(--space-3)", background: "var(--bg-secondary)", borderRadius: "var(--radius-md)", }} > <div style={{ color: "var(--color-primary)", flexShrink: 0 }}>{icon}</div> <div> <div style={{ fontWeight: 600, fontSize: "var(--text-sm)" }}>{label}</div> <div style={{ fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }}>{description}</div> </div> </div> ); } function StatCard({ label, description }: { label: string; description: string }) { return ( <div style={{ padding: "var(--space-3)", background: "var(--bg-secondary)", borderRadius: "var(--radius-md)" }}> <div style={{ fontWeight: 600, fontSize: "var(--text-sm)" }}>{label}</div> <div style={{ fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }}>{description}</div> </div> ); } function PlatformBadge({ name, aspect, color }: { name: string; aspect: string; color: string }) { return ( <div style={{ display: "flex", alignItems: "center", gap: "var(--space-2)", padding: "var(--space-2) var(--space-3)", background: `${color}10`, border: `1px solid ${color}30`, borderRadius: "var(--radius-md)", fontSize: "var(--text-sm)", }} > <span style={{ color, fontWeight: 700 }}>{name}</span> <span style={{ color: "var(--text-tertiary)", fontSize: "var(--text-xs)" }}>{aspect}</span> </div> ); } // ─── Context-Aware Page Help ─────────────────────────── interface PageHelpConfig { pattern: RegExp; title: string; description: string; quickActions: { label: string; shortcut?: string; description: string }[]; tips: string[]; } const pageHelpConfigs: PageHelpConfig[] = [ { pattern: /\/podcasts\/.*\/guests/, title: "Guest Pipeline", description: "Manage your guest workflow from prospect to published. Drag cards between columns to update status.", quickActions: [ { label: "Add Guest", shortcut: "N", description: "Open the add guest modal" }, { label: "Search", shortcut: "/", description: "Focus the search bar" }, { label: "Toggle View", shortcut: "V", description: "Switch between kanban and list" }, { label: "Select All", shortcut: "Ctrl+A", description: "Select all visible guests" }, { label: "Filter: High", shortcut: "2", description: "Show high-priority guests" }, { label: "Clear Selection", shortcut: "Esc", description: "Deselect all guests" }, ], tips: [ "Drag guests between columns to change their status", "Click a guest card to see full details and actions", "Use the priority filter to focus on high-value guests", ], }, { pattern: /\/podcasts\/.*\/schedule/, title: "Schedule", description: "Manage recording sessions and availabilities. Navigate weeks with arrow keys.", quickActions: [ { label: "Previous Week", shortcut: "←", description: "Go to previous week" }, { label: "Next Week", shortcut: "β†’", description: "Go to next week" }, { label: "Go to Today", shortcut: "T", description: "Jump to current week" }, { label: "Add Slot", shortcut: "A", description: "Create a recording slot" }, ], tips: [ "Slots show confirmed (blue) and pending (yellow) states", "Click a slot to view details or add a meet link", "Calendar sync is coming soon", ], }, { pattern: /\/podcasts\/.*\/record/, title: "Recording Studio", description: "Local-first, lossless double-ender recording. Keep your hands on the keyboard.", quickActions: [ { label: "Start/Stop", shortcut: "Space", description: "Toggle recording" }, { label: "Toggle Mute", shortcut: "M", description: "Mute/unmute microphone" }, ], tips: [ "The timer glows red during active recording", "A warning appears if you speak while muted", "Audio is persisted to IndexedDB as a safety net", ], }, { pattern: /\/podcasts\/.*\/content-studio/, title: "Content Studio", description: "AI-powered clip detection and multi-platform export. Turn episodes into viral content.", quickActions: [ { label: "Detect Clips", shortcut: "D", description: "AI-analyze the latest episode" }, ], tips: [ "High virality scores (90+) indicate strong viral potential", "Approve clips before exporting to ensure quality", "Each platform has optimized aspect ratios", ], }, { pattern: /\/podcasts\/.*\/episodes/, title: "Episodes", description: "View and manage all episodes for your podcast. Track status from recording to publication.", quickActions: [], tips: [ "Episode status updates automatically as guests move through the pipeline", "Click an episode to see detailed information and actions", ], }, { pattern: /\/outreach/, title: "Outreach", description: "Manage email sequences and track engagement metrics. Automate guest communication.", quickActions: [], tips: [ "Create sequences from the Guest Pipeline", "Track open rates and reply rates for optimization", "Sequences pause automatically when a guest replies", ], }, { pattern: /\/guest-network/, title: "Guest Network", description: "Discover verified podcast guests. Browse by category and invite experts to your show.", quickActions: [], tips: [ "All network guests have verified profiles", "Use filters to find guests by expertise", "Invite guests directly to your pipeline", ], }, { pattern: /\/diary/, title: "Diary", description: "Content planning and episode notes. Organize your thoughts before recording.", quickActions: [], tips: [ "Use the flow view to visualize content connections", "Notes are auto-saved as you type", ], }, ]; function usePageHelp(pathname: string): PageHelpConfig | null { return useMemo(() => { return pageHelpConfigs.find((config) => config.pattern.test(pathname)) || null; }, [pathname]); } // ─── Main HelpSystem Component ───────────────────────── interface HelpSystemProps { isOpen: boolean; onClose: () => void; pathname?: string; } export function HelpSystem({ isOpen, onClose, pathname = "" }: HelpSystemProps) { const [searchQuery, setSearchQuery] = useState(""); const [activeSection, setActiveSection] = useState<string | null>(null); const [activeArticle, setActiveArticle] = useState<string | null>(null); const pageHelp = usePageHelp(pathname); // Close the help system and reset internal state const handleClose = useCallback(() => { setSearchQuery(""); setActiveSection(null); setActiveArticle(null); onClose(); }, [onClose]); // Close on Escape useEffect(() => { if (!isOpen) return; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { e.preventDefault(); e.stopPropagation(); handleClose(); } }; document.addEventListener("keydown", handleKeyDown, true); return () => document.removeEventListener("keydown", handleKeyDown, true); }, [isOpen, onClose, handleClose]); // Navigate to article const goToArticle = useCallback((sectionId: string, articleId: string) => { setActiveSection(sectionId); setActiveArticle(articleId); setSearchQuery(""); }, []); // Go back to section list const goToSectionList = useCallback(() => { setActiveSection(null); setActiveArticle(null); }, []); // Filter articles based on search const filteredSections = useMemo(() => { if (!searchQuery.trim()) return helpSections; const q = searchQuery.toLowerCase(); return helpSections .map((section) => ({ ...section, articles: section.articles.filter( (article) => article.title.toLowerCase().includes(q) || section.title.toLowerCase().includes(q) ), })) .filter((section) => section.articles.length > 0); }, [searchQuery]); // Find active article content const currentArticle = useMemo(() => { if (!activeSection || !activeArticle) return null; const section = helpSections.find((s) => s.id === activeSection); if (!section) return null; return section.articles.find((a) => a.id === activeArticle) || null; }, [activeSection, activeArticle]); if (!isOpen) return null; return ( <> <div className="modal-overlay" onClick={handleClose} style={{ zIndex: 10000 }} /> <div style={{ position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)", background: "var(--bg-primary)", border: "1px solid var(--border-color)", borderRadius: "var(--radius-lg)", boxShadow: "var(--shadow-xl)", width: 780, maxWidth: "92vw", height: "80vh", maxHeight: "80vh", zIndex: 10001, overflow: "hidden", display: "flex", flexDirection: "column", animation: "revealScale 0.25s var(--ease-out-expo) forwards", }} role="dialog" aria-label="Help center" > {/* Header */} <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "var(--space-4) var(--space-5)", borderBottom: "1px solid var(--border-color)", flexShrink: 0, }} > <div style={{ display: "flex", alignItems: "center", gap: "var(--space-2)" }}> <HelpCircle size={20} color="var(--color-primary)" /> <h2 style={{ fontWeight: 700, fontSize: "var(--text-lg)", fontFamily: "var(--font-display)" }}> {activeArticle ? currentArticle?.title : activeSection ? helpSections.find((s) => s.id === activeSection)?.title : "Help Center"} </h2> </div> <button onClick={handleClose} style={{ background: "none", border: "none", cursor: "pointer", color: "var(--text-tertiary)", padding: "var(--space-1)", borderRadius: "var(--radius-sm)", }} aria-label="Close help" > <X size={20} /> </button> </div> {/* Content */} <div style={{ display: "flex", flex: 1, overflow: "hidden" }}> {/* Left Sidebar β€” Sections */} <div style={{ width: 220, flexShrink: 0, borderRight: "1px solid var(--border-color)", overflowY: "auto", padding: "var(--space-3)", display: activeSection ? "none" : "block", }} > {/* Search */} <div style={{ display: "flex", alignItems: "center", gap: "var(--space-2)", padding: "var(--space-2) var(--space-3)", background: "var(--bg-tertiary)", borderRadius: "var(--radius-md)", marginBottom: "var(--space-3)", }} > <Search size={14} color="var(--text-tertiary)" /> <input type="text" placeholder="Search help..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} style={{ border: "none", background: "transparent", outline: "none", fontSize: "var(--text-sm)", width: "100%", color: "var(--text-primary)", }} autoFocus /> </div> {/* Page-specific quick help */} {pageHelp && !searchQuery && ( <div style={{ padding: "var(--space-3)", background: "rgba(255, 159, 10, 0.06)", border: "1px solid rgba(255, 159, 10, 0.15)", borderRadius: "var(--radius-md)", marginBottom: "var(--space-3)", }} > <div style={{ display: "flex", alignItems: "center", gap: "var(--space-2)", marginBottom: "var(--space-2)" }}> <Sparkles size={14} color="var(--color-primary)" /> <span style={{ fontWeight: 600, fontSize: "var(--text-sm)", color: "var(--color-primary)" }}> On this page </span> </div> <p style={{ fontSize: "var(--text-xs)", color: "var(--text-secondary)", marginBottom: "var(--space-2)" }}> {pageHelp.description} </p> {pageHelp.quickActions.length > 0 && ( <div style={{ display: "flex", flexDirection: "column", gap: 4 }}> {pageHelp.quickActions.slice(0, 4).map((action) => ( <div key={action.label} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", fontSize: "var(--text-xs)" }}> <span style={{ color: "var(--text-secondary)" }}>{action.label}</span> {action.shortcut && ( <kbd style={{ padding: "1px 5px", background: "var(--bg-primary)", border: "1px solid var(--border-color)", borderRadius: "var(--radius-sm)", fontSize: "10px", fontFamily: "var(--font-mono, monospace)", color: "var(--text-tertiary)", }}> {action.shortcut} </kbd> )} </div> ))} </div> )} </div> )} {/* Section list */} <div className="stagger-children" style={{ display: "flex", flexDirection: "column", gap: 2 }}> {filteredSections.map((section) => { const Icon = section.icon; return ( <button key={section.id} onClick={() => goToArticle(section.id, section.articles[0].id)} style={{ display: "flex", alignItems: "center", gap: "var(--space-2)", padding: "var(--space-2) var(--space-3)", background: "transparent", border: "none", borderRadius: "var(--radius-md)", cursor: "pointer", fontSize: "var(--text-sm)", color: "var(--text-secondary)", textAlign: "left", width: "100%", transition: "background var(--transition-fast), color var(--transition-fast)", }} onMouseEnter={(e) => { e.currentTarget.style.background = "var(--bg-secondary)"; e.currentTarget.style.color = "var(--text-primary)"; }} onMouseLeave={(e) => { e.currentTarget.style.background = "transparent"; e.currentTarget.style.color = "var(--text-secondary)"; }} > <Icon size={16} color={section.color} /> <span style={{ flex: 1 }}>{section.title}</span> <ChevronRight size={14} color="var(--text-tertiary)" /> </button> ); })} </div> </div> {/* Main Content */} <div style={{ flex: 1, overflowY: "auto", padding: "var(--space-5)" }}> {/* Section articles list */} {activeSection && !activeArticle && ( <div className="stagger-children" style={{ display: "flex", flexDirection: "column", gap: "var(--space-2)" }}> <button onClick={goToSectionList} style={{ display: "flex", alignItems: "center", gap: "var(--space-2)", background: "none", border: "none", cursor: "pointer", color: "var(--color-primary)", fontSize: "var(--text-sm)", fontWeight: 600, padding: "var(--space-2) 0", marginBottom: "var(--space-2)", }} > ← Back to all sections </button> {helpSections .find((s) => s.id === activeSection) ?.articles.map((article) => ( <button key={article.id} onClick={() => setActiveArticle(article.id)} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "var(--space-3) var(--space-4)", background: "var(--bg-secondary)", border: "1px solid var(--border-color-subtle)", borderRadius: "var(--radius-md)", cursor: "pointer", fontSize: "var(--text-sm)", color: "var(--text-primary)", fontWeight: 500, textAlign: "left", width: "100%", transition: "border-color var(--transition-fast), box-shadow var(--transition-fast)", }} onMouseEnter={(e) => { e.currentTarget.style.borderColor = "var(--color-primary)"; e.currentTarget.style.boxShadow = "var(--shadow-sm)"; }} onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--border-color-subtle)"; e.currentTarget.style.boxShadow = "none"; }} > <span>{article.title}</span> <ChevronRight size={14} color="var(--text-tertiary)" /> </button> ))} </div> )} {/* Article content */} {currentArticle && ( <div className="animate-fade-in"> <button onClick={goToSectionList} style={{ display: "flex", alignItems: "center", gap: "var(--space-2)", background: "none", border: "none", cursor: "pointer", color: "var(--color-primary)", fontSize: "var(--text-sm)", fontWeight: 600, padding: "var(--space-2) 0", marginBottom: "var(--space-4)", }} > ← Back to {helpSections.find((s) => s.id === activeSection)?.title} </button> <div style={{ fontSize: "var(--text-sm)", color: "var(--text-secondary)", lineHeight: 1.7 }}> {currentArticle.content} </div> {/* Tips */} {currentArticle.tips && currentArticle.tips.length > 0 && ( <div style={{ marginTop: "var(--space-5)", padding: "var(--space-4)", background: "rgba(16, 185, 129, 0.06)", border: "1px solid rgba(16, 185, 129, 0.15)", borderRadius: "var(--radius-md)", }} > <div style={{ display: "flex", alignItems: "center", gap: "var(--space-2)", marginBottom: "var(--space-2)" }}> <Lightbulb size={16} color="var(--color-success)" /> <span style={{ fontWeight: 600, fontSize: "var(--text-sm)", color: "var(--color-success)" }}> Tips </span> </div> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> {currentArticle.tips.map((tip, i) => ( <li key={i} style={{ fontSize: "var(--text-sm)", color: "var(--text-secondary)" }}> {tip} </li> ))} </ul> </div> )} {/* Page-specific tips */} {pageHelp && pageHelp.tips.length > 0 && !currentArticle.tips && ( <div style={{ marginTop: "var(--space-5)", padding: "var(--space-4)", background: "rgba(255, 159, 10, 0.06)", border: "1px solid rgba(255, 159, 10, 0.15)", borderRadius: "var(--radius-md)", }} > <div style={{ display: "flex", alignItems: "center", gap: "var(--space-2)", marginBottom: "var(--space-2)" }}> <Zap size={16} color="var(--color-primary)" /> <span style={{ fontWeight: 600, fontSize: "var(--text-sm)", color: "var(--color-primary)" }}> Quick Tips for {pageHelp.title} </span> </div> <ul style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-1)" }}> {pageHelp.tips.map((tip, i) => ( <li key={i} style={{ fontSize: "var(--text-sm)", color: "var(--text-secondary)" }}> {tip} </li> ))} </ul> </div> )} </div> )} {/* Default view β€” no section/article selected */} {!activeSection && !activeArticle && !searchQuery && ( <div> <div style={{ textAlign: "center", marginBottom: "var(--space-6)" }}> <HelpCircle size={48} color="var(--text-tertiary)" style={{ opacity: 0.3, margin: "0 auto var(--space-3)" }} /> <h3 style={{ fontWeight: 700, fontSize: "var(--text-lg)", marginBottom: "var(--space-1)" }}> How can we help? </h3> <p style={{ fontSize: "var(--text-sm)", color: "var(--text-tertiary)" }}> Browse sections or search for specific topics </p> </div> {/* Quick Links */} <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "var(--space-3)" }}> {helpSections.slice(0, 6).map((section) => { const Icon = section.icon; return ( <button key={section.id} onClick={() => goToArticle(section.id, section.articles[0].id)} style={{ display: "flex", alignItems: "center", gap: "var(--space-3)", padding: "var(--space-3) var(--space-4)", background: "var(--bg-secondary)", border: "1px solid var(--border-color-subtle)", borderRadius: "var(--radius-md)", cursor: "pointer", textAlign: "left", width: "100%", transition: "border-color var(--transition-fast), transform var(--transition-fast)", }} onMouseEnter={(e) => { e.currentTarget.style.borderColor = section.color; e.currentTarget.style.transform = "translateY(-1px)"; }} onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--border-color-subtle)"; e.currentTarget.style.transform = "none"; }} > <div style={{ width: 36, height: 36, borderRadius: "var(--radius-md)", background: `${section.color}15`, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, }} > <Icon size={18} color={section.color} /> </div> <div> <div style={{ fontWeight: 600, fontSize: "var(--text-sm)" }}>{section.title}</div> <div style={{ fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }}> {section.articles.length} article{section.articles.length !== 1 ? "s" : ""} </div> </div> </button> ); })} </div> {/* Keyboard shortcuts hint */} <div style={{ marginTop: "var(--space-6)", padding: "var(--space-4)", background: "var(--bg-secondary)", borderRadius: "var(--radius-md)", border: "1px solid var(--border-color-subtle)", textAlign: "center", }} > <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: "var(--space-2)", marginBottom: "var(--space-2)" }}> <Keyboard size={16} color="var(--color-primary)" /> <span style={{ fontWeight: 600, fontSize: "var(--text-sm)" }}>Keyboard Shortcuts</span> </div> <p style={{ fontSize: "var(--text-xs)", color: "var(--text-tertiary)", marginBottom: "var(--space-2)" }}> Press <kbd style={{ padding: "1px 6px", background: "var(--bg-tertiary)", border: "1px solid var(--border-color)", borderRadius: "var(--radius-sm)", fontSize: "10px", fontFamily: "var(--font-mono, monospace)" }}>?</kbd> or <kbd style={{ padding: "1px 6px", background: "var(--bg-tertiary)", border: "1px solid var(--border-color)", borderRadius: "var(--radius-sm)", fontSize: "10px", fontFamily: "var(--font-mono, monospace)" }}>Ctrl+/</kbd> to see all shortcuts </p> </div> </div> )} {/* Search results */} {searchQuery && ( <div> {filteredSections.length === 0 ? ( <div style={{ textAlign: "center", padding: "var(--space-8)", color: "var(--text-tertiary)" }}> <Search size={32} style={{ margin: "0 auto var(--space-2)", opacity: 0.3 }} /> <p style={{ fontSize: "var(--text-sm)" }}>No results for &quot;{searchQuery}&quot;</p> <p style={{ fontSize: "var(--text-xs)" }}>Try different keywords</p> </div> ) : ( <div className="stagger-children" style={{ display: "flex", flexDirection: "column", gap: "var(--space-4)" }}> {filteredSections.map((section) => ( <div key={section.id}> <h3 style={{ fontSize: "var(--text-xs)", fontWeight: 600, color: "var(--text-tertiary)", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: "var(--space-2)" }}> {section.title} </h3> <div style={{ display: "flex", flexDirection: "column", gap: "var(--space-2)" }}> {section.articles.map((article) => ( <button key={article.id} onClick={() => goToArticle(section.id, article.id)} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "var(--space-3) var(--space-4)", background: "var(--bg-secondary)", border: "1px solid var(--border-color-subtle)", borderRadius: "var(--radius-md)", cursor: "pointer", fontSize: "var(--text-sm)", color: "var(--text-primary)", fontWeight: 500, textAlign: "left", width: "100%", }} > <span>{article.title}</span> <ChevronRight size={14} color="var(--text-tertiary)" /> </button> ))} </div> </div> ))} </div> )} </div> )} </div> </div> {/* Footer */} <div style={{ padding: "var(--space-3) var(--space-5)", borderTop: "1px solid var(--border-color)", display: "flex", alignItems: "center", justifyContent: "space-between", flexShrink: 0, }} > <span style={{ fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }}> Need more help?{" "} <a href="mailto:support@micprotocol.com" style={{ color: "var(--color-primary)" }}> Contact Support </a> </span> <span style={{ fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }}> Press <kbd style={{ padding: "0 4px", background: "var(--bg-tertiary)", border: "1px solid var(--border-color)", borderRadius: "var(--radius-xs, 3px)", fontSize: "10px", fontFamily: "var(--font-mono, monospace)" }}>Esc</kbd> to close </span> </div> </div> </> ); }
"use client";
import React, { useState, useCallback, useMemo, useEffect, ReactNode } from "react";
import {
X,
Search,
BookOpen,
Rocket,
Users,
Calendar,
Mic,
Scissors,
Send,
FileText,
Settings,
HelpCircle,
ChevronRight,
Keyboard,
Lightbulb,
Zap,
Globe,
Play,
BarChart3,
ExternalLink,
MessageSquare,
Sparkles,
} from "lucide-react";
// ─── Help Content Types ────────────────────────────────
interface HelpSection {
id: string;
title: string;
icon: React.ElementType;
color: string;
articles: HelpArticle[];
}
interface HelpArticle {
id: string;
title: string;
content: ReactNode;
shortcut?: string;
tips?: string[];
}
// ─── Help Content Database ─────────────────────────────
const helpSections: HelpSection[] = [
{
id: "getting-started",
title: "Getting Started",
icon: Rocket,
color: "var(--color-primary)",
articles: [
{
id: "welcome",
title: "Welcome to MicProtocol",
content: (
<div>
<p style={{ marginBottom: "var(--space-3)" }}>
MicProtocol is your all-in-one platform for podcast guest management. From discovering guests to publishing episodes, we handle the entire workflow so you can focus on creating great content.
</p>
<h4 style={{ fontWeight: 600, marginBottom: "var(--space-2)" }}>Quick Start Checklist:</h4>
<ol style={{ paddingLeft: "var(--space-5)", display: "flex", flexDirection: "column", gap: "var(--space-2)" }}>
<li>Create your first podcast</li>
<li>Add guests to your Pipeline</li>
<li>Send outreach sequences</li>
<li>Schedule recordings</li>
<li>Record with MicProtocol Studio</li>
<li>Create viral clips in Content Studio</li>
</ol>
</div>
),
tips: [
"Press ? or Ctrl+/ anytime to see all keyboard shortcuts",
"Use Ctrl+K to quickly search across the platform",
],
},
{
id: "keyboard-shortcuts",
title: "Keyboard Shortcuts",
content: (
<div>
<p style={{ marginBottom: "var(--space-3)" }}>
MicProtocol is designed for power users. Use keyboard shortcuts to navigate and perform actions without touching your mouse.
</p>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "var(--space-3)" }}>
<ShortcutCard shortcut="Ctrl + K" description="Focus search bar" />
<ShortcutCard shortcut="?" description="Show all shortcuts" />
<ShortcutCard shortcut="Ctrl + 1-9" description="Navigate to section" />
<ShortcutCard shortcut="N" description="Add new item (context-aware)" />
<ShortcutCard shortcut="V" description="Toggle view mode" />
<ShortcutCard shortcut="Escape" description="Close / clear selection" />
</div>
</div>
),
},
{
id: "navigation",
title: "Navigating the Dashboard",
content: (