Files
ihaleist/src/components/CompanyScannerModal.jsx
T

294 lines
14 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import { useApp } from '../context/AppContext';
import { Globe, Bot, Sparkles, CheckCircle2, ShieldCheck, Tag, X, Loader2, Database, ArrowRight } from 'lucide-react';
import confetti from 'canvas-confetti';
export const CompanyScannerModal = () => {
const { isCompanyScannerOpen, setIsCompanyScannerOpen, userProfile, scanCompanyWebsite, updateCompanyCapabilities } = useApp();
const [urlInput, setUrlInput] = useState(userProfile.website || 'https://www.tanriverdimedikal.com.tr');
const [isScanning, setIsScanning] = useState(false);
const [scanStep, setScanStep] = useState(0);
const [scanSuccess, setScanSuccess] = useState(false);
const [newProductInput, setNewProductInput] = useState('');
if (!isCompanyScannerOpen) return null;
const handleStartScan = async (e) => {
e.preventDefault();
if (!urlInput.trim()) return;
setIsScanning(true);
setScanSuccess(false);
setScanStep(1);
setTimeout(() => setScanStep(2), 600);
setTimeout(() => setScanStep(3), 1200);
const capabilities = await scanCompanyWebsite(urlInput);
setIsScanning(false);
setScanSuccess(true);
confetti({ particleCount: 70, spread: 60 });
};
const caps = userProfile.scrapedCapabilities;
const handleRemoveSector = (indexToRemove) => {
if (!caps?.extractedSectors) return;
const updated = caps.extractedSectors.filter((_, idx) => idx !== indexToRemove);
updateCompanyCapabilities({ extractedSectors: updated });
};
const handleRemoveProduct = (indexToRemove) => {
if (!caps?.extractedProducts) return;
const updated = caps.extractedProducts.filter((_, idx) => idx !== indexToRemove);
updateCompanyCapabilities({ extractedProducts: updated });
};
const handleRemoveCert = (indexToRemove) => {
if (!caps?.extractedCertifications) return;
const updated = caps.extractedCertifications.filter((_, idx) => idx !== indexToRemove);
updateCompanyCapabilities({ extractedCertifications: updated });
};
const handleAddCustomProduct = (e) => {
e.preventDefault();
if (!newProductInput.trim() || !caps) return;
const updated = [...(caps.extractedProducts || []), newProductInput.trim()];
updateCompanyCapabilities({ extractedProducts: updated });
setNewProductInput('');
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/70 backdrop-blur-xs animate-fade-in">
<div className="bg-white rounded-2xl border border-slate-200 shadow-2xl w-full max-w-3xl overflow-hidden my-auto flex flex-col max-h-[92vh]">
{/* Header */}
<div className="bg-linear-to-r from-blue-900 via-slate-900 to-indigo-900 text-white px-6 py-4 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-blue-600 flex items-center justify-center text-white shadow-md">
<Globe className="w-6 h-6" />
</div>
<div>
<h2 className="font-extrabold text-base flex items-center gap-2">
Firma Web Sitesi Tarama & Detaylı Ürün Analiz Motoru
</h2>
<p className="text-xs text-blue-200">Sayfalarınızdaki gerçek ürün ve faaliyet kollarını tarar, alakasız harici sektörleri kesinlikle engeller.</p>
</div>
</div>
<button
onClick={() => setIsCompanyScannerOpen(false)}
className="text-slate-400 hover:text-white p-1 rounded-lg transition-colors"
>
<X className="w-6 h-6" />
</button>
</div>
{/* Modal Body */}
<div className="p-6 space-y-6 overflow-y-auto">
{/* Strict Sector Protection Banner */}
<div className="bg-blue-50 border border-blue-200 rounded-xl p-3 flex items-center justify-between text-xs text-blue-900">
<div className="flex items-center gap-2 font-bold">
<ShieldCheck className="w-4 h-4 text-blue-600" />
<span>Sektör Filtresi Aktif: Sadece web sitenizdeki kesinleşmiş faaliyet alanları ve detaylı ürün kataloğu işlenir.</span>
</div>
<span className="bg-blue-600 text-white text-[10px] font-extrabold px-2 py-0.5 rounded-full">
Sıfır Alakasız Sektör Garantisi
</span>
</div>
{/* URL Form */}
<form onSubmit={handleStartScan} className="bg-slate-50 p-4 rounded-xl border border-slate-200 space-y-3">
<label className="block text-xs font-bold text-slate-800 uppercase tracking-wider">
FİRMANIZA AİT WEB SİTESİ ADRESİ
</label>
<div className="flex gap-2">
<div className="relative flex-1">
<Globe className="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2" />
<input
type="url"
required
value={urlInput}
onChange={(e) => setUrlInput(e.target.value)}
placeholder="https://www.firmaniz.com.tr"
className="w-full bg-white border border-slate-300 rounded-lg pl-9 pr-4 py-2.5 text-xs text-slate-900 font-semibold focus:ring-2 focus:ring-blue-500"
/>
</div>
<button
type="submit"
disabled={isScanning}
className="bg-blue-600 hover:bg-blue-500 text-white font-extrabold px-5 py-2.5 rounded-lg text-xs flex items-center gap-2 transition-colors shadow-md disabled:opacity-50 shrink-0"
>
{isScanning ? <Loader2 className="w-4 h-4 animate-spin" /> : <Sparkles className="w-4 h-4 text-amber-300" />}
<span>{isScanning ? 'Detaylı Ürünler Taranıyor...' : 'Web Sitemi Tara & Profili Güncelle'}</span>
</button>
</div>
</form>
{/* Scanning Live Indicator */}
{isScanning && (
<div className="bg-blue-50 border border-blue-200 p-5 rounded-xl space-y-3 animate-fade-in">
<div className="flex items-center justify-between text-xs font-extrabold text-blue-900">
<span className="flex items-center gap-2">
<Loader2 className="w-4 h-4 text-blue-600 animate-spin" />
Derin Sayfa Metin Analizi ve Ürün Çıkarımı Sürüyor...
</span>
<span>%{(scanStep * 33).toFixed(0)}</span>
</div>
<div className="w-full bg-blue-200 rounded-full h-2 overflow-hidden">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-500"
style={{ width: `${scanStep * 33}%` }}
/>
</div>
<div className="space-y-1 text-xs text-slate-600 font-medium">
<p className={scanStep >= 1 ? 'text-blue-900 font-bold' : 'text-slate-400'}>
1. 🌐 {urlInput} alan adına bağlanıldı, harici ve alakasız sektör tanımları elendi.
</p>
<p className={scanStep >= 2 ? 'text-blue-900 font-bold' : 'text-slate-400'}>
2. 📄 Web sitesindeki "Hakkımızda", "Ürünler" ve "Katalog" alt sayfalarındaki ürün isimleri ayıklandı.
</p>
<p className={scanStep >= 3 ? 'text-blue-900 font-bold' : 'text-slate-400'}>
3. 💾 Firmanın gerçek ürün yelpazesi ve ISO/ÜTS kalite belgeleri İhaleist veritabanına işlendi.
</p>
</div>
</div>
)}
{scanSuccess && (
<div className="bg-emerald-50 border border-emerald-200 text-emerald-900 p-4 rounded-xl text-xs font-bold flex items-center gap-2 animate-fade-in shadow-xs">
<CheckCircle2 className="w-5 h-5 text-emerald-600 shrink-0" />
<span>Web sitenizdeki ürünler ve doğrudan doğrulanmış sektörler veritabanına işlendi! Alakasız sektörler elendi.</span>
</div>
)}
{/* Current Extracted Capabilities Database View */}
{caps && (
<div className="space-y-4">
<div className="flex items-center justify-between border-b border-slate-200 pb-2">
<h3 className="font-extrabold text-sm text-slate-900 flex items-center gap-2">
<Database className="w-4 h-4 text-blue-600" /> Veritabanındaki Doğrulanmış Firma Ürün & Sektör Profili
</h3>
<span className="text-[10px] text-slate-400 font-semibold">Son Tarama: {caps.scrapedAt}</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* Sectors */}
<div className="bg-slate-50 p-4 rounded-xl border border-slate-200 space-y-2">
<span className="font-bold text-xs text-slate-800 uppercase tracking-wider flex items-center gap-1.5">
<Tag className="w-3.5 h-3.5 text-blue-600" /> Doğrulanmış Sektörler ({caps.extractedSectors?.length || 0})
</span>
<div className="space-y-1.5">
{caps.extractedSectors?.map((s, i) => (
<div key={i} className="flex items-center justify-between text-xs font-bold text-blue-900 bg-blue-100/70 border border-blue-200 px-2.5 py-1.5 rounded-lg group">
<span>{s}</span>
<button
type="button"
onClick={() => handleRemoveSector(i)}
className="text-blue-400 hover:text-red-600 transition-colors p-0.5"
title="Sektörü Kaldır"
>
<X className="w-3.5 h-3.5" />
</button>
</div>
))}
</div>
</div>
{/* Products */}
<div className="bg-slate-50 p-4 rounded-xl border border-slate-200 space-y-3 md:col-span-2">
<span className="font-bold text-xs text-slate-800 uppercase tracking-wider flex items-center gap-1.5 justify-between">
<span className="flex items-center gap-1.5">
<Bot className="w-3.5 h-3.5 text-emerald-600" /> Detaylı Ürün & Hizmet Kataloğu ({caps.extractedProducts?.length || 0})
</span>
<span className="text-[10px] text-emerald-700 font-semibold">Sayfadan Ayıklanan Detaylı Kalemler</span>
</span>
{/* Add Custom Item Form */}
<form onSubmit={handleAddCustomProduct} className="flex gap-2">
<input
type="text"
value={newProductInput}
onChange={(e) => setNewProductInput(e.target.value)}
placeholder="+ Yeni Ürün / İhale Kalemi Ekle..."
className="flex-1 bg-white border border-slate-300 rounded-lg px-3 py-1.5 text-xs text-slate-900 font-semibold focus:ring-2 focus:ring-emerald-500"
/>
<button
type="submit"
className="bg-emerald-600 hover:bg-emerald-500 text-white font-bold px-3 py-1.5 rounded-lg text-xs transition-colors shrink-0"
>
Ekle
</button>
</form>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-1.5">
{caps.extractedProducts?.map((p, i) => (
<div key={i} className="text-xs font-semibold text-slate-700 bg-white border border-slate-200 p-2 rounded-lg flex items-start justify-between gap-1.5 group">
<div className="flex items-start gap-1.5">
<span className="shrink-0 text-emerald-600 font-bold">📦</span>
<span className="line-clamp-2">{p}</span>
</div>
<button
type="button"
onClick={() => handleRemoveProduct(i)}
className="text-slate-300 hover:text-red-600 transition-colors p-0.5 shrink-0"
title="Ürünü Kaldır"
>
<X className="w-3.5 h-3.5" />
</button>
</div>
))}
</div>
</div>
</div>
{/* Certifications */}
<div className="bg-slate-50 p-4 rounded-xl border border-slate-200 space-y-2">
<span className="font-bold text-xs text-slate-800 uppercase tracking-wider flex items-center gap-1.5">
<ShieldCheck className="w-3.5 h-3.5 text-purple-600" /> Web Sitesinde Tespit Edilen Sertifika ve Kayıtlar
</span>
<div className="flex flex-wrap gap-2">
{caps.extractedCertifications?.map((c, i) => (
<span key={i} className="text-xs font-semibold text-purple-900 bg-purple-100/70 border border-purple-200 px-3 py-1 rounded-lg flex items-center gap-1.5">
📜 {c}
<button
type="button"
onClick={() => handleRemoveCert(i)}
className="text-purple-400 hover:text-red-600 transition-colors"
>
<X className="w-3 h-3" />
</button>
</span>
))}
</div>
</div>
</div>
)}
</div>
{/* Footer */}
<div className="bg-slate-50 p-4 border-t border-slate-200 flex justify-between items-center">
<p className="text-[11px] text-slate-500 font-medium">
💡 Sayfalarınızdaki detaylı ürün kataloğu sayesinde AI ihale eşleşme doğruluk skorunuz <strong className="text-emerald-600">%98+</strong> seviyesine çıkarılır.
</p>
<button
onClick={() => setIsCompanyScannerOpen(false)}
className="bg-slate-800 hover:bg-slate-700 text-white font-bold text-xs px-6 py-2.5 rounded-lg transition-colors shadow-xs shrink-0"
>
Tamam & Profili Kaydet
</button>
</div>
</div>
</div>
);
};