Spaces:
Sleeping
Sleeping
File size: 6,394 Bytes
a70baf7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | import React, { useState } from 'react';
import { Database, FileCode, Play, Send, RefreshCw, Layers, CheckCircle2, Copy } from 'lucide-react';
const DataExtractionTab: React.FC = () => {
const [url, setUrl] = useState('https://news.ycombinator.com');
const [schema, setSchema] = useState(
JSON.stringify({
title: 'Hacker News Scrape',
selectors: {
stories: '.athing',
title: '.titleline > a',
score: '.score'
}
}, null, 2)
);
const [isScraping, setIsScraping] = useState(false);
const [results, setResults] = useState('');
const [isImported, setIsImported] = useState(false);
const handleScrape = () => {
if (!url.trim()) {
alert('Please enter a target URL.');
return;
}
setIsScraping(true);
setResults('');
setTimeout(() => {
setIsScraping(false);
setResults(JSON.stringify({
status: 'success',
extracted_at: new Date().toISOString(),
data: {
items_scraped: 3,
competitors: [
{ name: "NovaAgent", price_tier: "$49/mo", reviews: "Positive" },
{ name: "SyncUsers Pro", price_tier: "$99/mo", reviews: "Highly Engaged" },
{ name: "SaaS Simulator", price_tier: "$15/mo", reviews: "Neutral" }
]
}
}, null, 2));
}, 1500);
};
const handleExportToUserSync = () => {
setIsImported(true);
setTimeout(() => {
setIsImported(false);
alert('Scraped competitive profiles successfully mapped and imported into Tab 4 (Persona Builder) database!');
}, 1000);
};
return (
<div className="max-w-7xl mx-auto px-6 py-10 text-white grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Scraper Configuration Form */}
<div className="bg-[#0c0c0c] border border-gray-800 rounded-2xl p-6 space-y-6 h-fit">
<div>
<h2 className="font-bold text-lg flex items-center gap-2">
<Database className="text-indigo-400" size={20} />
Nova Act Data Extraction Studio
</h2>
<p className="text-xs text-gray-400">Define extraction selectors & structured JSON schemas to gather competitor intelligence.</p>
</div>
<div className="space-y-2">
<label className="text-xs text-gray-400 font-bold uppercase tracking-wider">Target URL</label>
<input
type="text"
value={url}
onChange={(e) => setUrl(e.target.value)}
className="w-full bg-black border border-gray-800 rounded-xl p-3 text-sm focus:border-indigo-500 outline-none text-white font-mono"
placeholder="e.g. https://news.ycombinator.com"
/>
</div>
<div className="space-y-2">
<label className="text-xs text-gray-400 font-bold uppercase tracking-wider flex items-center gap-1.5">
<FileCode size={14} /> Schema Blueprint (JSON Format)
</label>
<textarea
value={schema}
onChange={(e) => setSchema(e.target.value)}
className="w-full bg-black border border-gray-800 rounded-xl p-4 text-xs focus:border-indigo-500 outline-none h-48 resize-none text-indigo-300 font-mono leading-relaxed"
/>
</div>
<button
onClick={handleScrape}
disabled={isScraping}
className="w-full py-3 bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-bold rounded-xl transition-all flex items-center justify-center gap-2 shadow-lg shadow-indigo-900/20 disabled:opacity-50"
>
{isScraping ? (
<>
<RefreshCw className="animate-spin" size={14} />
Executing Scraping Action...
</>
) : (
<>
<Play size={14} />
Execute Structured Scrape
</>
)}
</button>
</div>
{/* Scraper Output & UserSync Mapping */}
<div className="bg-[#0c0c0c] border border-gray-800 rounded-2xl p-6 flex flex-col justify-between">
<div className="space-y-4">
<div className="border-b border-gray-800 pb-4">
<h3 className="font-bold text-sm text-indigo-400 uppercase tracking-wider flex items-center gap-2">
<Layers size={16} />
Extracted Scrape Payload
</h3>
<p className="text-xs text-gray-400">Structured competitor attributes fetched from target HTML DOM.</p>
</div>
{!results ? (
<div className="text-sm text-gray-500 italic text-center py-24">
Configure scraping selectors and tap "Execute Structured Scrape" to generate structured data.
</div>
) : (
<div className="space-y-4">
<div className="bg-black/50 border border-gray-900 rounded-xl p-4">
<div className="flex justify-between items-center text-[10px] text-indigo-400/80 font-bold uppercase tracking-wider mb-2">
<span>Structured Response Object</span>
<button className="flex items-center gap-1 hover:text-white transition-colors">
<Copy size={10} /> Copy Scrape Data
</button>
</div>
<pre className="text-xs text-gray-300 leading-relaxed font-mono overflow-y-auto max-h-[250px] whitespace-pre">
{results}
</pre>
</div>
</div>
)}
</div>
{results && (
<div className="bg-gray-900/40 border border-gray-800 rounded-xl p-4 mt-6 flex items-center justify-between">
<div className="space-y-0.5">
<span className="text-[10px] text-indigo-400 font-bold uppercase">Feed UserSync Personas</span>
<p className="text-[11px] text-gray-400">Import these scraped SaaS prices and reviews into synthetic buyer focus groups.</p>
</div>
<button
onClick={handleExportToUserSync}
className="px-4 py-2 bg-indigo-500/10 border border-indigo-500/20 rounded-lg text-xs font-bold text-indigo-400 hover:bg-indigo-500 hover:text-white transition-all flex items-center gap-1.5"
>
<Send size={12} />
Export to UserSync
</button>
</div>
)}
</div>
</div>
);
};
export default DataExtractionTab; |