<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FiyatAvcısı - Akıllı Fiyat Takip Paneli</title>
<script src="https://cdn.tailwindcss.com"></script>
<!-- Markdown Parser for AI responses -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body { font-family: 'Inter', sans-serif; }
/* Tema Değişiklikleri: Loş ve Sade */
body {
background-color: #0f172a; /* bg-slate-900 */
color: #e2e8f0; /* text-slate-200 */
}
.bg-card {
background-color: #1e293b; /* bg-slate-800 */
}
.text-primary {
color: #22d3ee; /* text-cyan-400 */
}
.bg-primary {
background-color: #0891b2; /* bg-cyan-600 */
}
.loading-spin {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.store-badge {
transition: all 0.2s;
}
.store-badge:hover {
transform: translateY(-2px);
}
/* Ayarlar Paneli Animasyonu */
#settingsPanel {
transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
max-height: 0;
opacity: 0;
overflow: hidden;
}
#settingsPanel.open {
max-height: 400px;
opacity: 1;
}
/* AI Modal Animasyonu */
.modal {
transition: opacity 0.3s ease-in-out;
opacity: 0;
pointer-events: none;
}
.modal.active {
opacity: 1;
pointer-events: auto;
}
/* Markdown İçerik Stilleri (Koyu Temaya Uyarlandı) */
.markdown-content ul { list-style-type: disc; margin-left: 1.5rem; margin-top: 0.5rem; }
.markdown-content li { margin-bottom: 0.25rem; }
.markdown-content p { margin-bottom: 0.75rem; }
.markdown-content strong { color: #22d3ee; /* cyan-400 */ }
.modal-content-bg { background-color: #1e293b; } /* Modal arka planı */
</style>
</head>
<body class="bg-slate-900 text-slate-200 min-h-screen">
<!-- Navbar -->
<nav class="bg-slate-800 text-white shadow-lg sticky top-0 z-50 border-b border-slate-700">
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fa-solid fa-tags text-primary text-2xl"></i>
<h1 class="text-xl font-bold tracking-wide text-slate-100">FiyatAvcısı</h1>
</div>
<div class="flex items-center gap-3">
<div class="text-xs md:text-sm bg-slate-700 px-3 py-1 rounded-full flex items-center transition-colors text-slate-300" id="apiStatus">
<i class="fa-solid fa-gamepad text-yellow-300 mr-1"></i> <span class="hidden md:inline">Simülasyon</span>
</div>
<button onclick="toggleSettings()" class="bg-slate-700 hover:bg-slate-600 p-2 rounded-lg transition text-slate-200" title="API Ayarları">
<i class="fa-solid fa-gear"></i>
</button>
</div>
</div>
</nav>
<div class="container mx-auto px-4 py-6">
<!-- YENİ: Basitleştirilmiş Ayarlar Paneli (Sadece Gemini API) -->
<div id="settingsPanel" class="mb-6">
<div class="bg-slate-800 border border-slate-700 rounded-xl shadow-lg p-6 relative">
<div class="absolute top-0 left-0 w-1 h-full bg-cyan-600 rounded-l-xl"></div>
<h3 class="text-lg font-bold text-primary mb-4 flex items-center gap-2">
<i class="fa-solid fa-wand-magic-sparkles"></i> API Bağlantı Ayarları
</h3>
<p class="text-sm text-slate-400 mb-4">
Sistem artık hem fiyat takibi hem de analiz için <strong>tek bir anahtar</strong> kullanıyor.
<a href="https://aistudio.google.com/app/apikey" target="_blank" class="text-cyan-400 underline font-medium">Buradan ücretsiz bir Gemini API Key alın</a> ve yapıştırın.
</p>
<div class="mb-4">
<label class="block text-xs font-semibold text-slate-400 mb-1">GEMINI API KEY</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa-solid fa-key text-slate-500"></i>
</div>
<input type="password" id="inputGeminiKey" class="w-full border border-slate-700 bg-slate-900 text-slate-200 rounded-lg pl-10 pr-3 py-2 text-sm focus:ring-2 focus:ring-cyan-500 outline-none transition" placeholder="AIzaSy...">
</div>
</div>
<div class="flex justify-end gap-3 pt-2 border-t border-slate-700">
<button onclick="clearApiSettings()" class="text-red-400 hover:text-red-500 text-sm font-medium px-3 py-2">
Ayarları Sıfırla
</button>
<button onclick="saveApiSettings()" class="bg-cyan-600 hover:bg-cyan-500 text-white text-sm font-medium px-6 py-2 rounded-lg shadow-sm transition">
Kaydet ve Bağlan
</button>
</div>
</div>
</div>
<!-- Üst Bilgi & Aksiyonlar -->
<div class="flex flex-col md:flex-row justify-between items-center mb-6 gap-4">
<div>
<h2 class="text-2xl font-bold text-slate-100">Takip Listem</h2>
<p class="text-slate-400 text-sm mt-1">Eklediğin ürünlerin fiyat değişimlerini buradan izleyebilirsin.</p>
</div>
<button onclick="updateAllPrices()" id="updateAllBtn" class="bg-cyan-600 hover:bg-cyan-700 text-white px-6 py-2 rounded-lg shadow transition flex items-center gap-2">
<i class="fa-solid fa-rotate"></i>
<span>Tüm Fiyatları Güncelle</span>
</button>
</div>
<!-- Ürün Ekleme Formu -->
<div class="bg-slate-800 rounded-xl shadow-sm border border-slate-700 p-6 mb-8">
<h3 class="text-lg font-semibold mb-4 flex items-center gap-2 text-primary">
<i class="fa-solid fa-plus-circle text-cyan-400"></i> Yeni Ürün Ekle
</h3>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
<div class="md:col-span-2">
<label class="block text-xs text-slate-400 font-medium mb-1 uppercase">Ürün Adı</label>
<input type="text" id="productName" placeholder="Örn: iPhone 15 Pro Max" class="w-full border border-slate-700 bg-slate-900 text-slate-200 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:border-transparent transition">
</div>
<div>
<label class="block text-xs text-slate-400 font-medium mb-1 uppercase">Hedef Fiyat (TL)</label>
<input type="number" id="targetPrice" placeholder="Örn: 75000" class="w-full border border-slate-700 bg-slate-900 text-slate-200 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:border-transparent transition">
</div>
<div class="flex items-end">
<button id="addBtn" onclick="addProduct()" class="w-full bg-cyan-600 hover:bg-cyan-700 text-white font-medium px-4 py-2 rounded-lg transition shadow-lg transform active:scale-95 flex justify-center items-center gap-2">
<span>Listeye Ekle</span>
</button>
</div>
</div>
<!-- Tavsiye Edilen Ürünler (Hızlı Ekleme) -->
<div class="pt-4 border-t border-slate-700">
<span class="text-xs font-bold text-slate-500 uppercase tracking-wider mb-2 block">🔥 Hızlı Ekle / Tavsiyeler</span>
<div class="flex flex-wrap gap-2" id="recommendations">
<!-- JS ile doldurulacak -->
</div>
</div>
</div>
<!-- Ürün Listesi Grid -->
<div id="productList" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- JavaScript ile doldurulacak -->
<div class="col-span-full text-center py-12 text-slate-500 bg-slate-800 rounded-xl border border-dashed border-slate-700">
<i class="fa-solid fa-basket-shopping text-6xl mb-4 opacity-20"></i>
<p>Listeniz boş. Yukarıdan ürün ekleyerek başlayın.</p>
</div>
</div>
<!-- Ekstra Seçenekler -->
<div class="mt-12 pt-8 border-t border-slate-700">
<h3 class="text-xl font-bold text-slate-100 mb-6 flex items-center gap-2">
<i class="fa-solid fa-sliders text-cyan-400"></i> Yönetim Araçları
</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<button onclick="exportToCSV()" class="flex items-center justify-center gap-3 bg-slate-800 border border-slate-700 text-slate-300 hover:bg-slate-700 hover:text-green-400 px-4 py-4 rounded-xl transition shadow-sm group">
<div class="bg-slate-700 group-hover:bg-slate-600 p-2 rounded-lg transition">
<i class="fa-solid fa-file-csv text-xl"></i>
</div>
<div class="text-left">
<div class="font-semibold text-sm">Excel/CSV İndir</div>
</div>
</button>
<button onclick="sortProducts('price')" class="flex items-center justify-center gap-3 bg-slate-800 border border-slate-700 text-slate-300 hover:bg-slate-700 hover:text-cyan-400 px-4 py-4 rounded-xl transition shadow-sm group">
<div class="bg-slate-700 group-hover:bg-slate-600 p-2 rounded-lg transition">
<i class="fa-solid fa-arrow-down-1-9 text-xl"></i>
</div>
<div class="text-left">
<div class="font-semibold text-sm">Fiyata Görala</div>
</div>
</button>
<button onclick="sortProducts('name')" class="flex items-center justify-center gap-3 bg-slate-800 border border-slate-700 text-slate-300 hover:bg-slate-700 hover:text-blue-400 px-4 py-4 rounded-xl transition shadow-sm group">
<div class="bg-slate-700 group-hover:bg-slate-600 p-2 rounded-lg transition">
<i class="fa-solid fa-arrow-down-a-z text-xl"></i>
</div>
<div class="text-left">
<div class="font-semibold text-sm">İsme Göre Sırala</div>
</div>
</button>
<button onclick="clearAllData()" class="flex items-center justify-center gap-3 bg-slate-800 border border-slate-700 text-slate-300 hover:bg-slate-700 hover:text-red-400 px-4 py-4 rounded-xl transition shadow-sm group">
<div class="bg-slate-700 group-hover:bg-slate-600 p-2 rounded-lg transition">
<i class="fa-solid fa-trash-can text-xl"></i>
</div>
<div class="text-left">
<div class="font-semibold text-sm">Listeyi Temizle</div>
</div>
</button>
</div>
</div>
</div>
<!-- AI Modal (Popup) -->
<div id="aiModal" class="modal fixed inset-0 z-[100] flex items-center justify-center px-4">
<!-- Backdrop -->
<div class="absolute inset-0 bg-slate-900/80 backdrop-blur-sm" onclick="closeAiModal()"></div>
<!-- Content -->
<div class="bg-slate-800 rounded-2xl shadow-2xl w-full max-w-lg relative z-10 flex flex-col max-h-[80vh]">
<!-- Header -->
<div class="p-6 border-b border-slate-700 flex justify-between items-center bg-cyan-700 rounded-t-2xl text-white">
<div class="flex items-center gap-3">
<i class="fa-solid fa-wand-magic-sparkles text-yellow-300 text-xl"></i>
<div>
<h3 class="font-bold text-lg">Gemini Alışveriş Asistanı</h3>
<p class="text-xs text-slate-200 opacity-90">Yapay Zeka Destekli Analiz</p>
</div>
</div>
<button onclick="closeAiModal()" class="text-white/80 hover:text-white hover:bg-white/20 rounded-lg w-8 h-8 flex items-center justify-center transition">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<!-- Body -->
<div class="p-6 overflow-y-auto flex-1 custom-scrollbar">
<!-- Loading State -->
<div id="aiLoading" class="hidden flex-col items-center justify-center py-8 text-center space-y-4">
<div class="w-16 h-16 border-4 border-slate-700 border-t-cyan-500 rounded-full animate-spin"></div>
<p class="text-slate-400 font-medium animate-pulse">Gemini internette fiyatları araştırıyor...</p>
</div>
<!-- Result State -->
<div id="aiContent" class="hidden">
<div id="aiResultText" class="markdown-content text-sm text-slate-300 leading-relaxed"></div>
</div>
</div>
<!-- Footer -->
<div class="p-4 border-t border-slate-700 bg-slate-700 rounded-b-2xl text-xs text-center text-slate-500">
Gemini 2.5 AI modeli tarafından internet aramasıyla oluşturulmuştur.
</div>
</div>
</div>
<!-- Bildirim Kutusu (Toast) -->
<div id="toast" class="fixed bottom-5 right-5 bg-slate-700 text-white px-6 py-3 rounded-lg shadow-2xl transform translate-y-24 transition-transform duration-300 z-50 flex items-center gap-3">
<i class="fa-solid fa-check-circle text-green-400"></i>
<span id="toastMessage">İşlem başarılı!</span>
</div>
<script>
// GLOBAL DEĞİŞKENLER
// Sadece Gemini Key Yeterli
let GEMINI_API_KEY = localStorage.getItem('geminiApiKey') || '';
let products = JSON.parse(localStorage.getItem('trackedProducts')) || [];
// Tavsiye Edilen Ürünler
const recommendedProducts = [
{ name: "iPhone 15 128GB", price: 58000 },
{ name: "PlayStation 5 Slim", price: 23000 },
{ name: "Dyson Airwrap", price: 20000 },
{ name: "AirPods Pro 2", price: 9000 },
{ name: "Samsung S24 Ultra", price: 70000 },
{ name: "Philips Airfryer XXL", price: 8500 },
{ name: "MacBook Air M2", price: 42000 },
{ name: "Xiaomi Robot Süpürge", price: 12000 }
];
const fallbackStores = [
{ name: 'Trendyol', icon: 'fa-shipping-fast', color: 'text-orange-400' },
{ name: 'Hepsiburada', icon: 'fa-box-open', color: 'text-red-400' },
{ name: 'Amazon TR', icon: 'fa-amazon', color: 'text-yellow-400' },
{ name: 'N11', icon: 'fa-ladybug', color: 'text-pink-400' }
];
document.addEventListener('DOMContentLoaded', () => {
checkApiStatus();
renderProducts();
renderRecommendations();
// Ayarlar panelini doldur
document.getElementById('inputGeminiKey').value = GEMINI_API_KEY;
});
// ==========================================
// AYARLAR VE API YÖNETİMİ
// ==========================================
function toggleSettings() {
const panel = document.getElementById('settingsPanel');
panel.classList.toggle('open');
}
function saveApiSettings() {
const geminiKey = document.getElementById('inputGeminiKey').value.trim();
if (!geminiKey) {
showToast('Lütfen geçerli bir Gemini API anahtarı girin.', 'error');
return;
}
localStorage.setItem('geminiApiKey', geminiKey);
GEMINI_API_KEY = geminiKey;
// Temizlik (Eski anahtarlar varsa silelim, kafa karıştırmasın)
localStorage.removeItem('googleApiKey');
localStorage.removeItem('searchEngineId');
checkApiStatus();
toggleSettings();
showToast('Ayarlar kaydedildi! Tek anahtar modu aktif.');
}
function clearApiSettings() {
if(confirm('API anahtarını silmek istiyor musunuz?')) {
localStorage.removeItem('geminiApiKey');
document.getElementById('inputGeminiKey').value = '';
GEMINI_API_KEY = '';
checkApiStatus();
showToast('Ayarlar sıfırlandı.', 'error');
}
}
function checkApiStatus() {
const statusEl = document.getElementById('apiStatus');
const icon = statusEl.querySelector('i');
const text = statusEl.querySelector('span');
if (GEMINI_API_KEY) {
statusEl.className = "text-xs md:text-sm bg-green-700 px-3 py-1 rounded-full flex items-center transition-colors text-white shadow-sm border border-green-600";
icon.className = "fa-solid fa-bolt text-white mr-1";
text.textContent = "AI & Veri Aktif";
} else {
statusEl.className = "text-xs md:text-sm bg-slate-700 px-3 py-1 rounded-full flex items-center transition-colors text-slate-300 opacity-80";
icon.className = "fa-solid fa-gamepad text-yellow-300 mr-1";
text.textContent = "Simülasyon Modu";
}
}
// ==========================================
// AI ANALİZ FONKSİYONLARI (GEMINI)
// ==========================================
async function openAiAnalysis(productId) {
const product = products.find(p => p.id === productId);
if (!product) return;
if (!GEMINI_API_KEY) {
showToast('Önce Ayarlar kısmından Gemini API Anahtarı girin!', 'error');
toggleSettings();
return;
}
const modal = document.getElementById('aiModal');
const loading = document.getElementById('aiLoading');
const content = document.getElementById('aiContent');
const resultText = document.getElementById('aiResultText');
modal.classList.add('active');
loading.classList.remove('hidden');
loading.classList.add('flex');
content.classList.add('hidden');
resultText.innerHTML = '';
const currentPrice = product.bestPrice > 0 ? formatCurrency(product.bestPrice) : "Fiyat Bulunamadı";
const targetPrice = product.targetPrice > 0 ? formatCurrency(product.targetPrice) : "Belirtilmedi";
const sourceInfo = product.source === 'API' ? 'Gerçek Veri' : 'Simülasyon';
const prompt = `
Sen uzman bir e-ticaret danışmanısın. Aşağıdaki ürünü analiz et.
ÜRÜN: ${product.name}
GÜNCEL FİYAT: ${currentPrice} (${sourceInfo})
HEDEF FİYAT: ${targetPrice}
Lütfen cevabını Türkçe ve Markdown formatında ver:
1. **Fiyat Analizi:** Bu fiyat piyasaya göre nasıl?
2. **Ürün Özeti:** Öne çıkan 2 özellik.
3. **Tavsiye:** Almalı mı, beklemeli mi?
Kısa ve öz yaz.
`;
try {
// Grounding olmadan, sadece analiz için
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${GEMINI_API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }]
})
});
const data = await response.json();
if (data.error) {
console.error("Gemini API Hatası:", data.error);
if (data.error.message.includes("API key not valid")) {
resultText.innerHTML = `<div class="text-red-400 font-medium">HATA: Gemini API Anahtarı Geçersiz. Lütfen Ayarlar panelini kontrol edin.</div>`;
} else {
resultText.innerHTML = `<div class="text-red-400 font-medium">Hata: Analiz yapılırken bir sorun oluştu. Detay: ${data.error.message}</div>`;
}
loading.classList.remove('flex');
loading.classList.add('hidden');
content.classList.remove('hidden');
return;
}
// YENİ KONTROL: İçeriğin var olup olmadığını kontrol et
const candidate = data.candidates?.[0];
const aiResponseText = candidate?.content?.parts?.[0]?.text;
if (!aiResponseText) {
console.error("Gemini API: Analiz metni boş döndü.", data);
resultText.innerHTML = `<div class="text-red-400 font-medium">Hata: Analiz için yeterli bilgi oluşturulamadı.</div>`;
} else {
resultText.innerHTML = marked.parse(aiResponseText);
}
loading.classList.remove('flex');
loading.classList.add('hidden');
content.classList.remove('hidden');
} catch (error) {
console.error("Gemini Hatası:", error);
loading.classList.remove('flex');
loading.classList.add('hidden');
content.classList.remove('hidden');
resultText.innerHTML = `<div class="text-red-400 font-medium">Bağlantı Hatası: Lütfen internet bağlantınızı kontrol edin.</div>`;
}
}
function closeAiModal() {
document.getElementById('aiModal').classList.remove('active');
}
// ==========================================
// DİĞER FONKSİYONLAR
// ==========================================
function renderRecommendations() {
const container = document.getElementById('recommendations');
container.innerHTML = recommendedProducts.map(item => `
<button onclick="fillForm('${item.name}', ${item.price})"
class="px-3 py-1.5 bg-slate-700 hover:bg-slate-600 hover:text-cyan-400 hover:border-cyan-700 border border-slate-700 rounded-full text-xs text-slate-300 font-medium transition active:scale-95">
${item.name}
</button>
`).join('');
}
function fillForm(name, price) {
document.getElementById('productName').value = name;
document.getElementById('targetPrice').value = price;
}
async function addProduct() {
const nameInput = document.getElementById('productName');
const priceInput = document.getElementById('targetPrice');
const btn = document.getElementById('addBtn');
const name = nameInput.value.trim();
const target = parseFloat(priceInput.value);
if (!name) {
showToast('Lütfen bir ürün adı girin.', 'error');
return;
}
const originalBtnContent = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Aranıyor...';
const newProduct = {
id: Date.now(),
name: name,
targetPrice: target || 0,
lastChecked: new Date().toLocaleString('tr-TR'),
bestPrice: 0,
prices: [],
source: 'SIMULATION'
};
// EĞER GEMINI KEY VARSA -> GERÇEK ARAMA YAP
if (GEMINI_API_KEY) {
newProduct.prices = await fetchRealPrices(name);
newProduct.source = 'API';
// Hata bildirimi fetchRealPrices içinde yapıldığı için burada sadece veri kontrolü
if (newProduct.prices.length === 0) {
// Sadece fiyat bulunamadıysa (API hatası değilse) uyarı ver
if (!localStorage.getItem('geminiApiKey')) {
// Eğer anahtar silinmişse, simülasyona dön
} else {
showToast('Fiyat bilgisi bulunamadı. Lütfen daha spesifik arama yapın.', 'error');
}
}
} else {
// KEY YOKSA -> SİMÜLASYON
await new Promise(r => setTimeout(r, 800));
newProduct.prices = generateSimulatedPrices(target || 1000);
newProduct.source = 'SIMULATION';
}
if (newProduct.prices.length > 0) {
newProduct.bestPrice = Math.min(...newProduct.prices.map(p => p.price));
} else {
newProduct.bestPrice = 0;
}
products.unshift(newProduct);
saveData();
renderProducts();
nameInput.value = '';
priceInput.value = '';
btn.disabled = false;
btn.innerHTML = originalBtnContent;
showToast('Ürün listeye eklendi!');
}
function deleteProduct(id) {
if(confirm('Bu ürünü takipten çıkarmak istiyor musunuz?')) {
products = products.filter(p => p.id !== id);
saveData();
renderProducts();
showToast('Ürün silindi.');
}
}
// ======================================================
// CORE: TEK KEY İLE FİYAT ARAMA (GEMINI GROUNDING)
// ======================================================
async function fetchRealPrices(query) {
// Google Arama aracını kullanarak Gemini'ye soruyoruz
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${GEMINI_API_KEY}`;
// Hepsiburada'yı önceliklendiren yeni prompt
const prompt = `
Türkiye'deki online mağazalarda şu ürünün güncel fiyatını ara: "${query}".
Özellikle Hepsiburada.com ve diğer büyük Türk e-ticaret sitelerinden (Trendyol, Amazon TR, N11, Pazarama vb.) en ucuz ve güvenilir 3-4 sonucu listele.
Lütfen bulduğun sonuçları şu JSON formatında ver:
[
{"storeName": "Mağaza Adı", "price": 12345.00, "link": "https://..."}
]
ÖNEMLİ:
1. Sadece JSON döndür. Başka açıklama yazma.
2. Fiyatları sadece sayı olarak ver (TL sembolü koyma).
3. Eğer mağaza linki bulamazsan link yerine "#" koy.
`;
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }],
tools: [{ google_search: {} }], // Google Search Grounding Aktif
})
});
const data = await response.json();
// Hata Kontrolü (API anahtarı vb. hatalar burada yakalanır)
if (data.error) {
console.error("Gemini API Hatası:", data.error);
if (data.error.message.includes("API key not valid") || data.error.reason === "API_KEY_INVALID") {
showToast('HATA: Geçersiz Gemini API Anahtarı. Lütfen Ayarlar panelini kontrol edin.', 'error');
return [];
}
showToast(`API Hatası: ${data.error.message.substring(0, 50)}...`, 'error');
return [];
}
// YENİ KONTROL: Modelin yanıtının boş olup olmadığını kontrol et
const candidate = data.candidates?.[0];
const jsonText = candidate?.content?.parts?.[0]?.text;
if (!jsonText) {
console.error("Gemini API: Fiyat bilgisi boş döndü. Prompt veya model yanıtı eksik.", data);
return [];
}
// Markdown temizliği (```json ve ``` işaretlerini kaldır)
let cleanedJsonText = jsonText.replace(/```json/g, '').replace(/```/g, '').trim();
let results = JSON.parse(cleanedJsonText);
// Veriyi bizim formata uydur (ikon ve renk ekle)
results = results.map(item => {
const storeInfo = identifyStore(item.storeName.toLowerCase() + " " + item.link);
return {
storeName: item.storeName,
price: parseFloat(item.price),
link: item.link,
icon: storeInfo.icon,
color: storeInfo.color
};
});
// Sıralama
return results.sort((a,b) => a.price - b.price);
} catch (error) {
console.error('Fiyat Arama Hatası:', error);
// JSON ayrıştırma hatası, ağ bağlantısı hatası vb.
showToast('Ağ bağlantısı veya JSON ayrıştırma hatası.', 'error');
return [];
}
}
function identifyStore(text) {
text = text.toLowerCase();
if (text.includes('trendyol')) return { name: 'Trendyol', icon: 'fa-shipping-fast', color: 'text-orange-400' };
if (text.includes('hepsiburada')) return { name: 'Hepsiburada', icon: 'fa-box-open', color: 'text-red-400' };
if (text.includes('amazon')) return { name: 'Amazon', icon: 'fa-amazon', color: 'text-yellow-400' };
if (text.includes('n11')) return { name: 'N11', icon: 'fa-ladybug', color: 'text-pink-400' };
if (text.includes('teknosa')) return { name: 'Teknosa', icon: 'fa-plug', color: 'text-blue-400' };
if (text.includes('mediamarkt')) return { name: 'MediaMarkt', icon: 'fa-mobile-screen-button', color: 'text-red-400' };
if (text.includes('pazarama')) return { name: 'Pazarama', icon: 'fa-bag-shopping', color: 'text-fuchsia-400' };
return { name: 'Online Mağaza', icon: 'fa-store', color: 'text-slate-500' };
}
function generateSimulatedPrices(basePrice) {
const result = [];
const selectedStores = fallbackStores.sort(() => 0.5 - Math.random()).slice(0, 3);
selectedStores.forEach(store => {
const randomVariation = (Math.random() * 0.4) - 0.2;
let simPrice = basePrice * (1 + randomVariation);
if (basePrice === 0) simPrice = Math.random() * 1000 + 100;
result.push({
storeName: store.name,
price: Math.floor(simPrice),
link: '#',
icon: store.icon,
color: store.color
});
});
return result.sort((a, b) => a.price - b.price);
}
async function updateAllPrices() {
const btn = document.getElementById('updateAllBtn');
const icon = btn.querySelector('i');
icon.classList.add('loading-spin');
btn.disabled = true;
btn.classList.add('opacity-75');
const updates = products.map(async (p) => {
// API VARSA -> GERÇEK ARAMA
if (GEMINI_API_KEY) {
const realPrices = await fetchRealPrices(p.name);
p.source = 'API';
if (realPrices.length > 0) {
p.prices = realPrices;
p.bestPrice = Math.min(...p.prices.map(item => item.price));
}
} else {
// KEY YOKSA -> SİMÜLASYON
await new Promise(r => setTimeout(r, 500));
const baseRef = p.targetPrice > 0 ? p.targetPrice : p.bestPrice || 1000;
p.prices = generateSimulatedPrices(baseRef);
p.source = 'SIMULATION';
p.bestPrice = Math.min(...p.prices.map(item => item.price));
}
p.lastChecked = new Date().toLocaleString('tr-TR');
return p;
});
await Promise.all(updates);
saveData();
renderProducts();
icon.classList.remove('loading-spin');
btn.disabled = false;
btn.classList.remove('opacity-75');
showToast('Tüm fiyatlar güncellendi!');
}
function renderProducts() {
const container = document.getElementById('productList');
if (products.length === 0) {
container.innerHTML = `
<div class="col-span-full text-center py-16 bg-slate-800 rounded-xl border border-dashed border-slate-700">
<div class="w-20 h-20 bg-slate-700 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fa-solid fa-magnifying-glass-plus text-3xl text-cyan-400"></i>
</div>
<h3 class="text-lg font-semibold text-slate-200">Listeniz Henüz Boş</h3>
<p class="text-slate-400 text-sm mt-2">Takip etmek istediğiniz ilk ürünü yukarıdan ekleyin.</p>
</div>
`;
return;
}
container.innerHTML = products.map(product => {
const hasData = product.prices.length > 0;
const bestDeal = hasData ? product.prices[0] : { price: 0, storeName: 'Veri Bulunamadı', color: 'text-slate-500' };
const isTargetMet = product.targetPrice > 0 && bestDeal.price <= product.targetPrice && bestDeal.price > 0;
const isRealData = product.source === 'API';
// Kart HTML
return `
<div class="bg-slate-800 rounded-xl shadow-lg border border-slate-700 hover:shadow-xl transition duration-300 overflow-hidden group flex flex-col h-full relative">
<!-- Kaynak Rozeti -->
<div class="absolute top-2 right-2 px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wide ${isRealData ? 'bg-emerald-800 text-emerald-300' : 'bg-yellow-800 text-yellow-300'}">
${isRealData ? '<i class="fa-solid fa-bolt"></i> Yapay Zeka' : '<i class="fa-solid fa-robot"></i> Simülasyon'}
</div>
<!-- Üst: Başlık ve Silme -->
<div class="p-5 border-b border-slate-700 flex justify-between items-start mt-2">
<div>
<h3 class="font-bold text-slate-100 text-lg leading-tight mb-1">${product.name}</h3>
<div class="text-xs text-slate-400 flex items-center gap-1">
<i class="fa-regular fa-clock"></i> Son güncel: ${product.lastChecked.split(' ')[1]}
</div>
</div>
<button onclick="deleteProduct(${product.id})" class="text-slate-500 hover:text-red-400 transition px-2">
<i class="fa-solid fa-trash"></i>
</button>
</div>
<!-- Orta: Fiyat Alanı -->
<div class="p-5 bg-gradient-to-r ${isTargetMet ? 'from-emerald-900 to-emerald-800' : 'from-slate-700 to-slate-800'}">
<p class="text-xs font-semibold uppercase tracking-wider ${isTargetMet ? 'text-emerald-300' : 'text-slate-400'} mb-1">En İyi Fiyat</p>
<div class="flex items-end gap-2">
<span class="text-3xl font-bold ${isTargetMet ? 'text-emerald-400' : 'text-cyan-400'}">
${bestDeal.price > 0 ? formatCurrency(bestDeal.price) : '---'}
</span>
<span class="text-sm text-slate-300 mb-1 font-medium bg-slate-700 px-2 py-0.5 rounded border border-slate-600 shadow-sm">
${bestDeal.storeName}
</span>
</div>
${product.targetPrice > 0 ? `
<div class="mt-2 text-xs flex items-center gap-1 ${isTargetMet ? 'text-emerald-400' : 'text-orange-400'}">
<i class="fa-solid ${isTargetMet ? 'fa-check-circle' : 'fa-bell'}"></i>
Hedef: ${formatCurrency(product.targetPrice)}
</div>
` : ''}
</div>
<!-- Mağaza Listesi -->
<div class="p-4 bg-slate-800 space-y-3 flex-1">
${hasData ? product.prices.map((offer, index) => `
<a href="${offer.link}" target="_blank" class="flex justify-between items-center text-sm p-2 rounded-lg hover:bg-slate-700 border border-transparent hover:border-slate-600 transition cursor-pointer store-badge decoration-transparent">
<div class="flex items-center gap-3">
<div class="w-8 flex justify-center ${offer.color}">
<i class="fa-solid ${offer.icon} text-lg"></i>
</div>
<span class="font-medium text-slate-200">${offer.storeName}</span>
${index === 0 ? '<span class="bg-cyan-800 text-cyan-300 text-[10px] px-2 py-0.5 rounded-full font-bold">EN UCUZ</span>' : ''}
</div>
<div class="font-semibold text-slate-300">
${formatCurrency(offer.price)}
</div>
</a>
`).join('') : `
<div class="text-center text-sm text-slate-500 py-4">
<i class="fa-solid fa-circle-exclamation mb-1"></i><br>
Fiyat verisi bulunamadı.
</div>
`}
</div>
<!-- Alt: AI Analiz Butonu -->
<div class="p-3 border-t border-slate-700 bg-slate-900">
<button onclick="openAiAnalysis(${product.id})" class="w-full flex items-center justify-center gap-2 bg-cyan-600 hover:bg-cyan-700 text-white text-sm font-medium py-2 rounded-lg transition shadow-md hover:shadow-lg transform active:scale-95 group">
<i class="fa-solid fa-wand-magic-sparkles text-yellow-300 group-hover:animate-pulse"></i>
Gemini ile Analiz Et
</button>
</div>
</div>
`;
}).join('');
}
function saveData() {
localStorage.setItem('trackedProducts', JSON.stringify(products));
}
function exportToCSV() {
if (products.length === 0) {
showToast('İndirilecek veri yok!', 'error');
return;
}
let csvContent = "data:text/csv;charset=utf-8,";
csvContent += "Urun Adi,En Iyi Fiyat,Hedef Fiyat,Magaza,Tarih\n";
products.forEach(p => {
const best = p.prices[0] || {price:0, storeName:'-'};
const row = `"${p.name}",${best.price},${p.targetPrice},"${best.storeName}","${p.lastChecked}"`;
csvContent += row + "\n";
});
const encodedUri = encodeURI(csvContent);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "fiyat_takip_listesi.csv");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToast('Liste Excel/CSV olarak indirildi.');
}
function sortProducts(criteria) {
if (products.length === 0) return;
if (criteria === 'price') {
products.sort((a, b) => (a.bestPrice || 0) - (b.bestPrice || 0));
showToast('Ürünler fiyata göre sıralandı.');
} else if (criteria === 'name') {
products.sort((a, b) => a.name.localeCompare(b.name, 'tr'));
showToast('Ürünler isme göre sıralandı.');
}
renderProducts();
saveData();
}
function clearAllData() {
if (products.length === 0) return;
if(confirm('DİKKAT: Tüm takip listeniz silinecek. Bu işlem geri alınamaz. Emin misiniz?')) {
products = [];
localStorage.removeItem('trackedProducts');
renderProducts();
showToast('Tüm liste temizlendi.', 'error');
}
}
function formatCurrency(amount) {
return new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(amount);
}
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
const msgEl = document.getElementById('toastMessage');
const icon = toast.querySelector('i');
// Renkleri loş temaya uygun ayarla
const bgColor = type === 'error' ? 'bg-red-800' : 'bg-slate-700';
const iconColor = type === 'error' ? 'text-red-400' : 'text-green-400';
toast.className = `fixed bottom-5 right-5 ${bgColor} text-white px-6 py-3 rounded-lg shadow-2xl transform translate-y-24 transition-transform duration-300 z-50 flex items-center gap-3`;
icon.className = `fa-solid ${type === 'error' ? 'fa-circle-exclamation' : 'fa-check-circle'} ${iconColor}`;
msgEl.textContent = message;
toast.classList.remove('translate-y-24');
setTimeout(() => {
toast.classList.add('translate-y-24');
}, 3000);
}
</script>
</body>
</html>