"use client"; import { useState, useEffect } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { auth } from '@/lib/firebase/config'; import { toast } from 'sonner'; import { Download, ExternalLink, Eye, EyeOff, MessageSquare, FolderKanban, Bot, Upload } from 'lucide-react'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { OpenAIIcon } from '@/components/icons/custom-icons'; interface ChatGPTImportCardProps { projectId?: string; onImportComplete?: (importData: any) => void; } export function ChatGPTImportCard({ projectId, onImportComplete }: ChatGPTImportCardProps) { const [conversationUrl, setConversationUrl] = useState(''); const [openaiApiKey, setOpenaiApiKey] = useState(''); const [showApiKey, setShowApiKey] = useState(false); const [loading, setLoading] = useState(false); const [importedData, setImportedData] = useState(null); const [dialogOpen, setDialogOpen] = useState(false); const [hasStoredKey, setHasStoredKey] = useState(false); const [checkingKey, setCheckingKey] = useState(true); const [importType, setImportType] = useState<'conversation' | 'project' | 'gpt' | 'file'>('file'); // Check for stored OpenAI key on mount useEffect(() => { const checkStoredKey = async () => { try { const user = auth.currentUser; if (!user) { setCheckingKey(false); return; } const token = await user.getIdToken(); const response = await fetch('/api/keys', { headers: { 'Authorization': `Bearer ${token}`, }, }); if (response.ok) { const data = await response.json(); const hasOpenAI = data.keys.some((k: any) => k.service === 'openai'); setHasStoredKey(hasOpenAI); } } catch (error) { console.error('Error checking for stored key:', error); } finally { setCheckingKey(false); } }; checkStoredKey(); }, []); const extractConversationId = (urlOrId: string): { id: string | null; isShareLink: boolean } => { // If it's already just an ID if (!urlOrId.includes('/') && !urlOrId.includes('http')) { const trimmed = urlOrId.trim(); // Check if it's a share link ID (UUID format) const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmed); return { id: trimmed, isShareLink: isUUID }; } // Extract from URL patterns: // https://chat.openai.com/c/{id} - regular conversation (old) // https://chatgpt.com/c/{id} - regular conversation (new) // https://chat.openai.com/share/{id} - shared conversation (not supported) // https://chatgpt.com/share/{id} - shared conversation (not supported) // Check for share links first const sharePatterns = [ /chat\.openai\.com\/share\/([a-zA-Z0-9-]+)/, /chatgpt\.com\/share\/([a-zA-Z0-9-]+)/, ]; for (const pattern of sharePatterns) { const match = urlOrId.match(pattern); if (match) { return { id: match[1], isShareLink: true }; } } // Regular conversation patterns const conversationPatterns = [ /chat\.openai\.com\/c\/([a-zA-Z0-9-]+)/, /chatgpt\.com\/c\/([a-zA-Z0-9-]+)/, // GPT project conversations: https://chatgpt.com/g/g-p-[id]-[name]/c/[conversation-id] /chatgpt\.com\/g\/g-p-[a-zA-Z0-9-]+\/c\/([a-zA-Z0-9-]+)/, ]; for (const pattern of conversationPatterns) { const match = urlOrId.match(pattern); if (match) { return { id: match[1], isShareLink: false }; } } return { id: null, isShareLink: false }; }; const handleImport = async () => { if (!conversationUrl.trim()) { toast.error('Please enter a conversation ID or project ID'); return; } // If no stored key and no manual key provided, show error if (!hasStoredKey && !openaiApiKey) { toast.error('Please enter your OpenAI API key or add one in Keys page'); return; } setLoading(true); try { const user = auth.currentUser; if (!user) { toast.error('Please sign in to import'); return; } const token = await user.getIdToken(); if (importType === 'file') { // Import from uploaded conversations.json file try { const conversations = JSON.parse(conversationUrl); const response = await fetch('/api/chatgpt/import-file', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ conversations, projectId, }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.details || error.error || 'Import failed'); } const data = await response.json(); toast.success(`✅ Imported ${data.imported} conversations!`); setImportedData({ messageCount: data.imported, title: `${data.imported} conversations` }); if (onImportComplete) { onImportComplete(data); } } catch (error: any) { throw new Error(`File import failed: ${error.message}`); } // Reset form setConversationUrl(''); setDialogOpen(false); return; } // Determine which key to use let keyToUse = openaiApiKey; // If no manual key provided, try to get stored key if (!keyToUse && hasStoredKey) { const keyResponse = await fetch('/api/keys/get', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ service: 'openai' }), }); if (keyResponse.ok) { const keyData = await keyResponse.json(); keyToUse = keyData.keyValue; } } if (importType === 'project') { // Import OpenAI Project const response = await fetch('/api/openai/projects', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ openaiApiKey: keyToUse, projectId: conversationUrl.trim(), }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.details || error.error || 'Failed to fetch project'); } const data = await response.json(); toast.success(`Retrieved OpenAI Project: ${data.data.name || data.data.id}`); setImportedData(data.data); if (onImportComplete) { onImportComplete(data.data); } } else { // Import ChatGPT Conversation const { id: conversationId, isShareLink } = extractConversationId(conversationUrl); if (!conversationId) { toast.error('Invalid ChatGPT URL or conversation ID'); return; } // Check if it's a share link if (isShareLink) { toast.error('Share links are not supported. Please use the direct conversation URL from your ChatGPT chat history.', { description: 'Look for URLs like: chatgpt.com/c/... or chat.openai.com/c/...', duration: 5000, }); return; } const response = await fetch('/api/chatgpt/import', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ conversationId, openaiApiKey: keyToUse, projectId, }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.details || error.error || 'Import failed'); } const data = await response.json(); setImportedData(data); toast.success(`Imported: ${data.title} (${data.messageCount} messages)`); if (onImportComplete) { onImportComplete(data); } } // Reset form setConversationUrl(''); setDialogOpen(false); } catch (error) { console.error('Import error:', error); toast.error(error instanceof Error ? error.message : 'Failed to import'); } finally { setLoading(false); } }; return ( <>
Import from OpenAI Import ChatGPT conversations or OpenAI Projects
Import from OpenAI Import ChatGPT conversations or OpenAI Projects setImportType(v as 'conversation' | 'project' | 'gpt' | 'file')} className="w-full"> Upload File Single Chat {/* File Upload */}
{ const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { try { const conversations = JSON.parse(event.target?.result as string); setConversationUrl(JSON.stringify(conversations)); // Store in existing state toast.success(`Loaded ${conversations.length} conversations`); } catch (error) { toast.error('Invalid JSON file'); } }; reader.readAsText(file); } }} />

Upload your ChatGPT exported conversations.json file

{/* Instructions */}

How to export your ChatGPT data:

  1. Go to ChatGPT Settings → Data Controls
  2. Click "Export data"
  3. Wait for the email from OpenAI (can take up to 24 hours)
  4. Download and extract the ZIP file
  5. Upload the conversations.json file here
{/* Info banner */}

💡 Privacy-friendly import

Your conversations are processed locally and only stored in your Vibn account. We never send your data to third parties.

{/* Show stored key status */} {hasStoredKey && (

✓ Using your stored OpenAI API key

)} {/* OpenAI API Key (optional if stored) */} {!hasStoredKey && (
setOpenaiApiKey(e.target.value)} className="pr-10" />
)} {/* Conversation URL */}
setConversationUrl(e.target.value)} />

Copy the URL from your ChatGPT conversation or paste the conversation ID

{/* Instructions */}

How to find your conversation URL:

  1. Open the ChatGPT conversation you want to import
  2. Look at the URL in your browser (must show /c/)
  3. Copy the full URL: chatgpt.com/c/...
  4. Note: Share links (/share/) won't work - you need the direct conversation URL
{/* GPT URL Input */}
setConversationUrl(e.target.value)} />

Paste the full URL of your custom GPT

{/* Instructions */}

How to find your GPT URL:

  1. Open your custom GPT in ChatGPT
  2. Copy the full URL from your browser
  3. Paste it here
  4. To import conversations with this GPT, switch to the "Chat" tab
{/* Info banner */}

💡 About GPT imports

This saves a reference to your custom GPT. To import actual conversations with this GPT, go to a specific chat and use the "Chat" tab to import that conversation.

{/* Show stored key status */} {hasStoredKey && (

✓ Using your stored OpenAI API key

)} {/* OpenAI API Key (optional if stored) */} {!hasStoredKey && (
setOpenaiApiKey(e.target.value)} className="pr-10" />
)} {/* Project ID */}
setConversationUrl(e.target.value)} />

Enter your OpenAI Project ID

{/* Instructions */}

How to find your Project ID:

  1. Go to OpenAI Projects
  2. Click on the project you want to import
  3. Copy the Project ID (starts with proj_)
  4. Or use the API to list all projects

What you can import:

  • Conversations: Full ChatGPT chat history with planning & brainstorming
  • Projects: OpenAI Platform projects with API keys, usage, and settings
  • Requirements, specifications, and design decisions
  • Architecture notes and technical discussions
{importedData && (
{importedData.messageCount ? ( ) : ( )}

Recently imported: {importedData.title || importedData.name || importedData.id}

{importedData.messageCount ? `${importedData.messageCount} messages • Conversation ID: ${importedData.conversationId}` : `Project ID: ${importedData.id}` }

)}

💡 Why import from OpenAI?

Connect your planning discussions from ChatGPT and your OpenAI Platform projects with your actual coding sessions in Vibn. Our AI can reference everything to provide better context and insights.

); }