diff --git a/apps/landing/src/app/docs/layout.tsx b/apps/landing/src/app/docs/layout.tsx index 69e1d52..ea7575e 100644 --- a/apps/landing/src/app/docs/layout.tsx +++ b/apps/landing/src/app/docs/layout.tsx @@ -5,6 +5,8 @@ import { usePathname } from 'next/navigation'; import { SubsectionNav } from '@/components/shared/SubsectionNav'; import { DocsSidebar } from '@/components/docs/layout/DocsSidebar'; import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout'; +import { ApiKeyWidget } from '@/components/shared/ApiKeyWidget/apikey-widget'; +import { ApiKeyProvider } from '@/components/shared/ApiKeyWidget/apikey-context'; /** * Root Documentation Layout @@ -44,32 +46,35 @@ export default function DocsRootLayout({ children }: DocsRootLayoutProps) { const pathname = usePathname(); return ( -
- {/* Animated gradient background (matching landing page) */} -
-
-
-
+ +
+ {/* Animated gradient background (matching landing page) */} +
+
+
+
- {/* Subsection Navigation */} - - - {/* Three-column Documentation Layout */} -
- - -
- } - center={children} + {/* Subsection Navigation */} + } /> + + {/* Three-column Documentation Layout */} +
+ + +
+ } + center={children} + /> +
- + ); } diff --git a/apps/landing/src/components/docs/blocks/InteractiveAPIWidget.tsx b/apps/landing/src/components/docs/blocks/InteractiveAPIWidget.tsx index 8b91a4d..195b913 100644 --- a/apps/landing/src/components/docs/blocks/InteractiveAPIWidget.tsx +++ b/apps/landing/src/components/docs/blocks/InteractiveAPIWidget.tsx @@ -1,5 +1,6 @@ 'use client'; +import { useApiKey } from '@/components/shared/ApiKeyWidget/apikey-context'; /** * Interactive API Widget - Production Version * @@ -60,6 +61,8 @@ export const InteractiveAPIWidget = ({ const [isExpanded, setIsExpanded] = useState(false); const [paramValues, setParamValues] = useState>({}); + const {apiKeyValidated, focus} = useApiKey() + // Load API key from localStorage and listen for changes useEffect(() => { const loadApiKey = () => { @@ -266,7 +269,7 @@ func main() { // Expand API key input in navigation const expandApiKey = () => { - window.dispatchEvent(new CustomEvent('expandApiKeyInput')); + focus(); }; const isSuccess = response && response.success === true; @@ -336,7 +339,7 @@ func main() { onClick={expandApiKey} className="text-sm text-gray-400 hover:text-white underline-offset-4 hover:underline transition-colors" > - {apiKey ? 'API Key Set' : 'Enter API Key'} + {apiKeyValidated ? 'API Key Set' : 'Enter API Key'} + + {/* EXPANDED STATE - Rendered as overlay when expanded */} + {ui.expanded && ( +
+ {/* Header */} +
+
+

+ {apiKeyValidated ? 'API Key Active' : 'Enter API Key'} +

+ {apiKeyValidated && apiKeyInfo && ( +

+ {apiKeyInfo.organizationSlug} / {apiKeyInfo.projectSlug} +

+ )} +
+ +
+ + {/* API Key Input/Display */} +
+ +
+ setApiKey(e.target.value)} + onKeyDown={handleKeyDown} + placeholder="Enter your API key" + disabled={apiKeyValidated} + className={`flex-1 px-4 bg-slate-800 border border-slate-700 rounded-lg text-gray-300 font-mono placeholder:text-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500/50 focus:border-purple-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all ${ + isMobileContext ? 'py-3 text-base' : 'py-2 text-sm' + }`} + /> + +
+
+ + {/* Error Message */} + {apiKeyError && ( +
+

{apiKeyError}

+
+ )} + + {/* Actions */} +
+ {!apiKeyValidated ? ( + + ) : ( + + )} +
+ + {/* Helper Text */} + {!apiKeyValidated && ( +

+ Press Enter or click Validate to verify your API key +

+ )} +
+ )} + + + ); +} diff --git a/apps/landing/src/components/shared/SubsectionNav.tsx b/apps/landing/src/components/shared/SubsectionNav.tsx index 33ce3aa..4c24e8c 100644 --- a/apps/landing/src/components/shared/SubsectionNav.tsx +++ b/apps/landing/src/components/shared/SubsectionNav.tsx @@ -7,7 +7,6 @@ * Features: * - Dark nav bar with decorative wave line * - Active state indicator (purple color) - * - Optional API Key input on the right * - Responsive (hamburger menu on mobile) * - Three-column layout matching docs structure * - Customizable left/right slots @@ -19,13 +18,12 @@ * } * rightSlot={} * /> */ -import { useState, useEffect, useRef, ReactNode } from 'react'; +import { useState, ReactNode } from 'react'; import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout'; interface NavItem { @@ -39,212 +37,19 @@ interface SubsectionNavProps { ctaText?: string; ctaHref?: string; onCtaClick?: () => void; - showApiKeyInput?: boolean; /** Optional content for left column (w-64, hidden until lg) */ leftSlot?: ReactNode; - /** Optional content for right column (w-56, hidden until xl). If not provided and showApiKeyInput is true, API key input is used. */ + /** Optional content for right column (w-56, hidden until xl) */ rightSlot?: ReactNode; } -const API_KEY_STORAGE = 'banatie_docs_api_key'; - -export const SubsectionNav = ({ - items, - currentPath, - showApiKeyInput = false, - leftSlot, - rightSlot, -}: SubsectionNavProps) => { +export const SubsectionNav = ({ items, currentPath, leftSlot, rightSlot }: SubsectionNavProps) => { const [mobileMenuOpen, setMobileMenuOpen] = useState(false); - const [apiKey, setApiKey] = useState(''); - const [isApiKeyExpanded, setIsApiKeyExpanded] = useState(false); - const [keyVisible, setKeyVisible] = useState(false); - const dropdownRef = useRef(null); const isActive = (href: string) => currentPath.startsWith(href); - // Load API key from localStorage - useEffect(() => { - if (showApiKeyInput) { - const stored = localStorage.getItem(API_KEY_STORAGE); - if (stored) setApiKey(stored); - } - }, [showApiKeyInput]); - - // Handle API key changes - const handleApiKeyChange = (value: string) => { - setApiKey(value); - if (value) { - localStorage.setItem(API_KEY_STORAGE, value); - } else { - localStorage.removeItem(API_KEY_STORAGE); - } - - // Dispatch event to notify widgets - window.dispatchEvent(new CustomEvent('apiKeyChanged', { detail: value })); - }; - - // Handle clear API key - const handleClear = () => { - setApiKey(''); - localStorage.removeItem(API_KEY_STORAGE); - window.dispatchEvent(new CustomEvent('apiKeyChanged', { detail: '' })); - }; - - // Close dropdown when clicking outside - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { - setIsApiKeyExpanded(false); - } - }; - - if (isApiKeyExpanded) { - document.addEventListener('mousedown', handleClickOutside); - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [isApiKeyExpanded]); - - // Listen for custom event to expand API key from widgets - useEffect(() => { - const handleExpandApiKey = () => { - setIsApiKeyExpanded(true); - // Scroll to top to show nav - window.scrollTo({ top: 0, behavior: 'smooth' }); - }; - - window.addEventListener('expandApiKeyInput', handleExpandApiKey); - - return () => { - window.removeEventListener('expandApiKeyInput', handleExpandApiKey); - }; - }, []); - - // Desktop API Key Input Component - const apiKeyComponent = showApiKeyInput && ( -
- - - {/* Dropdown Card */} - {isApiKeyExpanded && ( -
-
-
-

API Key

-

- Enter once, use across all examples -

-
- -
- -
- -
- handleApiKeyChange(e.target.value)} - placeholder="Enter your API key" - className="flex-1 px-3 py-2 bg-slate-800 border border-slate-700 focus:border-purple-500 focus:ring-1 focus:ring-purple-500 rounded-lg text-sm text-gray-300 font-mono placeholder-gray-500 focus:outline-none transition-colors" - /> - -
- - {apiKey && ( - - )} -
- -
-

Stored locally in your browser

-
-
- )} -
- ); - return ( -