make non submit buttons explicit
This commit is contained in:
@@ -2218,3 +2218,9 @@ Browser QA, dinamik sekme yüklemesi sonrası günlük bülten başlığını do
|
||||
Headless browser kabulü artık giriş sonrası Günlük Bülten sekmesini dinamik olarak açıyor ve `Günlük İhale Bülteni` başlığını doğruluyor. Böylece İstanbul günü düzeltmesiyle birlikte sekme yükleme ve erişilebilir başlık regresyonu da otomatik izleniyor.
|
||||
|
||||
Doğrulama: browser QA başarılı; günlük bülten, analitik, ICS, çıktı ve mobil kontrolleri aynı koşuda geçti.
|
||||
|
||||
### 2026-09-06 · Örtük düğme gönderimlerinin kaldırılması
|
||||
|
||||
Tüm JSX ekranlarında form dışında kalan düğmeler açık `type="button"` kullanacak şekilde standardize edildi. Filtre, gezinme, dışa aktarma, modal, pazar, bildirim ve yönetim eylemleri artık bir form kapsayıcısına taşınsa bile istemeden submit tetiklemiyor; gerçek form gönderimleri `type="submit"` olarak korunuyor.
|
||||
|
||||
Doğrulama: 293 otomatik test, üretim derlemesi ve browser QA başarılı. QA akışı Günlük Bülten, Analitik, ICS, çıktı ve 390px mobil kontrollerini geçti.
|
||||
|
||||
@@ -371,7 +371,7 @@ export const AiSpecAnalyzerModal = () => {
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-50 p-4 border-t border-slate-200 flex justify-end">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setIsAiSpecAnalyzerOpen(false)}
|
||||
className="bg-slate-800 hover:bg-slate-700 text-white font-bold text-xs px-6 py-2.5 rounded-xl transition-colors"
|
||||
>
|
||||
|
||||
@@ -139,7 +139,7 @@ export const KanbanBoard = () => {
|
||||
{info.label}
|
||||
</span>;
|
||||
})()}
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setActiveTenderDetail(t)}
|
||||
className="text-slate-400 hover:text-blue-600 p-0.5"
|
||||
aria-label={`${t.ikn} ihale detayını aç`}
|
||||
|
||||
@@ -200,7 +200,7 @@ export const LandingPage = ({ onGiris }) => {
|
||||
<a href="#nasil" className="transition hover:text-white">Nasıl çalışır?</a>
|
||||
<a href="#rakamlar" className="transition hover:text-white">Rakamlarla</a>
|
||||
</nav>
|
||||
<button onClick={onGiris} className="group flex items-center gap-2 rounded-xl bg-white px-5 py-2.5 text-sm font-black text-slate-900 transition hover:bg-cyan-300">
|
||||
<button type="button" onClick={onGiris} className="group flex items-center gap-2 rounded-xl bg-white px-5 py-2.5 text-sm font-black text-slate-900 transition hover:bg-cyan-300">
|
||||
Giriş Yap <ArrowRight size={15} className="transition group-hover:translate-x-0.5" />
|
||||
</button>
|
||||
</header>
|
||||
@@ -225,7 +225,7 @@ export const LandingPage = ({ onGiris }) => {
|
||||
fiyat analizini tek panelde toplar. Siz teklife odaklanın, takibi biz yapalım.
|
||||
</p>
|
||||
<div className="lp-hero-in mt-9 flex flex-col gap-3 sm:flex-row" style={{ animationDelay: '0.24s' }}>
|
||||
<button onClick={onGiris} className="group flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-cyan-400 to-blue-500 px-7 py-3.5 text-base font-black text-white shadow-xl shadow-cyan-500/25 transition hover:shadow-cyan-400/40 hover:brightness-110">
|
||||
<button type="button" onClick={onGiris} className="group flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-cyan-400 to-blue-500 px-7 py-3.5 text-base font-black text-white shadow-xl shadow-cyan-500/25 transition hover:shadow-cyan-400/40 hover:brightness-110">
|
||||
Hemen Başla — Ücretsiz <ArrowRight size={17} className="transition group-hover:translate-x-1" />
|
||||
</button>
|
||||
<a href="#ozellikler" className="flex items-center justify-center gap-2 rounded-xl border border-white/15 bg-white/[0.05] px-7 py-3.5 text-base font-bold text-white/85 backdrop-blur transition hover:border-white/30 hover:bg-white/10">
|
||||
@@ -329,7 +329,7 @@ export const LandingPage = ({ onGiris }) => {
|
||||
<CalendarDays size={34} className="mx-auto text-cyan-300" />
|
||||
<h2 className="mx-auto mt-5 max-w-2xl text-4xl font-black tracking-tight sm:text-5xl">Yarınki ilanlar sizi beklemeyecek.</h2>
|
||||
<p className="mx-auto mt-4 max-w-xl text-lg leading-8 text-white/55">Bugün girin, kriterlerinizi kurun; yarın sabah ilk bülteniniz hazır olsun.</p>
|
||||
<button onClick={onGiris} className="group mx-auto mt-9 flex items-center gap-2 rounded-xl bg-gradient-to-r from-cyan-400 to-blue-500 px-8 py-4 text-lg font-black text-white shadow-xl shadow-cyan-500/30 transition hover:shadow-cyan-400/50 hover:brightness-110">
|
||||
<button type="button" onClick={onGiris} className="group mx-auto mt-9 flex items-center gap-2 rounded-xl bg-gradient-to-r from-cyan-400 to-blue-500 px-8 py-4 text-lg font-black text-white shadow-xl shadow-cyan-500/30 transition hover:shadow-cyan-400/50 hover:brightness-110">
|
||||
Ücretsiz Giriş Yap <ArrowRight size={19} className="transition group-hover:translate-x-1" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -347,7 +347,7 @@ export const LandingPage = ({ onGiris }) => {
|
||||
<div className="flex flex-wrap items-center gap-x-6 gap-y-2 font-semibold">
|
||||
<a href="/gizlilik.html" className="transition hover:text-white">Gizlilik Politikası</a>
|
||||
<a href="/kullanim-kosullari.html" className="transition hover:text-white">Kullanım Şartları</a>
|
||||
<button onClick={onGiris} className="transition hover:text-white">Giriş</button>
|
||||
<button type="button" onClick={onGiris} className="transition hover:text-white">Giriş</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -90,7 +90,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
<Compass className="w-4 h-4 text-blue-600" /> Hızlı Erişim
|
||||
</span>
|
||||
{(detailedQueries.institution || detailedQueries.registration || selectedRegion || quickFilter !== 'all' || selectedSektorler.length || selectedCpvler.length || selectedCity || selectedAlimTuru || selectedKurumTipi) && (
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={clearAllFilters}
|
||||
className="text-[11px] font-medium text-blue-600 hover:underline"
|
||||
>
|
||||
@@ -103,7 +103,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
const Icon = item.icon;
|
||||
const isSelected = quickFilter === item.id;
|
||||
return (
|
||||
<button
|
||||
<button type="button"
|
||||
key={item.id}
|
||||
onClick={() => {
|
||||
setQuickFilter(item.id);
|
||||
@@ -156,12 +156,12 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
className="bg-slate-50 hover:bg-slate-100 p-2.5 rounded-lg border border-slate-200 flex items-center justify-between text-xs transition-colors group"
|
||||
>
|
||||
<div className="flex-1 min-w-0 pr-2">
|
||||
<button className="font-bold text-blue-800 underline text-left" onClick={()=>applySavedFilter(filter)} aria-label={`${filter.name} filtresini uygula`}>{filter.name}</button>
|
||||
<button type="button" className="font-bold text-blue-800 underline text-left" onClick={()=>applySavedFilter(filter)} aria-label={`${filter.name} filtresini uygula`}>{filter.name}</button>
|
||||
<p className="text-[10px] text-slate-500 truncate mt-0.5">
|
||||
👤 {filter.user} {filter.sehir && `• 📍 ${filter.sehir}`}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
<button type="button"
|
||||
disabled={savingCount > 0} aria-label={`${filter.name} filtresini sil`} onClick={() => deleteSavedFilter(filter.id)}
|
||||
title="Filtreyi Sil"
|
||||
className="text-slate-400 hover:text-red-600 p-1 opacity-60 group-hover:opacity-100 transition-opacity"
|
||||
@@ -173,7 +173,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setActiveTab('smart_filter_builder')}
|
||||
className="w-full bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-3 rounded-lg text-xs flex items-center justify-center gap-2 transition-colors shadow-xs"
|
||||
>
|
||||
@@ -187,7 +187,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
|
||||
{/* Alım Türü */}
|
||||
<div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => toggleSection('alimTuru')}
|
||||
className="w-full px-4 py-3 flex items-center justify-between text-xs font-bold text-slate-800 bg-slate-50/50 hover:bg-slate-100 transition-colors"
|
||||
>
|
||||
@@ -219,7 +219,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
|
||||
{/* Sektörler */}
|
||||
<div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => toggleSection('sektorler')}
|
||||
className="w-full px-4 py-3 flex items-center justify-between text-xs font-bold text-slate-800 bg-slate-50/50 hover:bg-slate-100 transition-colors"
|
||||
>
|
||||
@@ -238,7 +238,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
className="w-full border border-slate-200 rounded-lg px-2 py-1.5 mb-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-blue-500/30"
|
||||
/>
|
||||
<div className="max-h-56 overflow-y-auto space-y-1">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => { setSelectedSektorler([]); setActiveTab('list'); }}
|
||||
className={`w-full text-left px-2 py-1 rounded font-medium ${!selectedSektorler.length ? 'bg-blue-50 text-blue-700 font-bold' : 'text-slate-600 hover:bg-slate-50'}`}
|
||||
>
|
||||
@@ -271,7 +271,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
|
||||
{/* CPV Kodu */}
|
||||
<div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => toggleSection('cpv')}
|
||||
className="w-full px-4 py-3 flex items-center justify-between text-xs font-bold text-slate-800 bg-slate-50/50 hover:bg-slate-100 transition-colors"
|
||||
>
|
||||
@@ -313,7 +313,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
className="w-full border border-slate-200 rounded-lg px-2 py-1.5 mb-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-blue-500/30"
|
||||
/>
|
||||
<div className="max-h-56 overflow-y-auto space-y-1">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => { setSelectedCpvler([]); setActiveTab('list'); }}
|
||||
className={`w-full text-left px-2 py-1 rounded font-medium ${!selectedCpvler.length ? 'bg-blue-50 text-blue-700 font-bold' : 'text-slate-600 hover:bg-slate-50'}`}
|
||||
>
|
||||
@@ -346,7 +346,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
|
||||
{/* Kurum Tipleri */}
|
||||
<div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => toggleSection('kurumTipleri')}
|
||||
className="w-full px-4 py-3 flex items-center justify-between text-xs font-bold text-slate-800 bg-slate-50/50 hover:bg-slate-100 transition-colors"
|
||||
>
|
||||
@@ -357,14 +357,14 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
</button>
|
||||
{openSections.kurumTipleri && (
|
||||
<div className="p-3 space-y-1 text-xs max-h-48 overflow-y-auto">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => { setSelectedKurumTipi(''); setActiveTab('list'); }}
|
||||
className={`w-full text-left px-2 py-1 rounded font-medium ${!selectedKurumTipi ? 'bg-blue-50 text-blue-700 font-bold' : 'text-slate-600 hover:bg-slate-50'}`}
|
||||
>
|
||||
Tüm Kurumlar
|
||||
</button>
|
||||
{INSTITUTION_TYPES_LIST.map(k => (
|
||||
<button
|
||||
<button type="button"
|
||||
key={k}
|
||||
onClick={() => { setSelectedKurumTipi(k); setActiveTab('list'); }}
|
||||
className={`w-full text-left px-2 py-1 rounded truncate font-medium ${selectedKurumTipi === k ? 'bg-blue-50 text-blue-700 font-bold' : 'text-slate-600 hover:bg-slate-50'}`}
|
||||
@@ -378,7 +378,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
|
||||
{/* İl Dağılımları */}
|
||||
<div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => toggleSection('iller')}
|
||||
className="w-full px-4 py-3 flex items-center justify-between text-xs font-bold text-slate-800 bg-slate-50/50 hover:bg-slate-100 transition-colors"
|
||||
>
|
||||
@@ -389,14 +389,14 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
|
||||
</button>
|
||||
{openSections.iller && (
|
||||
<div className="p-3 space-y-1 text-xs max-h-48 overflow-y-auto">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => { setSelectedCity(''); setActiveTab('list'); }}
|
||||
className={`w-full text-left px-2 py-1 rounded font-medium ${!selectedCity ? 'bg-blue-50 text-blue-700 font-bold' : 'text-slate-600 hover:bg-slate-50'}`}
|
||||
>
|
||||
Tüm Şehirler
|
||||
</button>
|
||||
{CITIES_LIST.map(city => (
|
||||
<button
|
||||
<button type="button"
|
||||
key={city}
|
||||
onClick={() => { setSelectedCity(city); setActiveTab('list'); }}
|
||||
className={`w-full text-left px-2 py-1 rounded font-medium ${selectedCity === city ? 'bg-blue-50 text-blue-700 font-bold' : 'text-slate-600 hover:bg-slate-50'}`}
|
||||
|
||||
@@ -68,7 +68,7 @@ export const SmartFilterBuilder = () => {
|
||||
<p className="text-xs text-slate-500">Arama ölçütlerinizi kaydedin; soldaki filtre adına tıklayarak ihale listesine uygulayın. E-posta bildirimleri Yönetim → Mobil PWA bölümünden ayrıca kurulur.</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setActiveTab('list')}
|
||||
className="bg-slate-800 hover:bg-slate-700 text-white font-semibold text-xs px-4 py-2 rounded-lg flex items-center gap-2 transition-colors shadow-xs"
|
||||
>
|
||||
@@ -105,7 +105,7 @@ export const SmartFilterBuilder = () => {
|
||||
<div key={f.id} className="p-3 bg-slate-50 border border-slate-200 rounded-lg text-xs hover:bg-slate-100 transition-colors">
|
||||
<div className="flex items-center justify-between font-bold text-slate-900">
|
||||
<button type="button" className="text-blue-800 underline" onClick={()=>applySavedFilter(f)} aria-label={`${f.name} filtresini uygula`}>{f.name}</button>
|
||||
<button
|
||||
<button type="button"
|
||||
disabled={savingCount > 0} aria-label={`${f.name} filtresini sil`} onClick={async()=>{setSuccessMessage('');await deleteSavedFilter(f.id);}}
|
||||
className="text-slate-400 hover:text-red-600 text-xs"
|
||||
>
|
||||
@@ -117,7 +117,7 @@ export const SmartFilterBuilder = () => {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setActiveTab('list')}
|
||||
className="w-full bg-slate-100 hover:bg-slate-200 text-slate-700 font-bold py-2.5 px-3 rounded-lg text-xs flex items-center justify-center gap-2 transition-colors border border-slate-200"
|
||||
>
|
||||
|
||||
@@ -84,7 +84,7 @@ export const TenderCard = ({ tender }) => {
|
||||
<span className="flex items-center gap-1 text-slate-700 font-semibold truncate max-w-md">
|
||||
<Building2 className="w-3.5 h-3.5 text-slate-400 shrink-0" />
|
||||
<span className="truncate">{tender.kurum}</span>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={(e) => { e.stopPropagation(); toggleFavoriteKurum(tender.kurum); }}
|
||||
disabled={favoriteSaving || !tender.kurum?.trim()}
|
||||
aria-busy={favoriteSaving} aria-pressed={Boolean(isFavKurum)}
|
||||
|
||||
@@ -172,7 +172,7 @@ export const TenderList = () => {
|
||||
<option value="distant">Tarihi Uzak Olanlar</option>
|
||||
</select>
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={handleExportExcel} disabled={exporting || !filteredTenders.length}
|
||||
className="bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs px-3.5 py-2 rounded-lg flex items-center gap-1.5 transition-colors shadow-xs shrink-0"
|
||||
>
|
||||
@@ -218,7 +218,7 @@ export const TenderList = () => {
|
||||
{gorunenTenders.length.toLocaleString('tr-TR')} / {filteredTenders.length.toLocaleString('tr-TR')} ihale gösteriliyor
|
||||
</div>
|
||||
{/* Gözlemci çalışmazsa elle yükleme yolu açık kalsın */}
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={dahaGoster}
|
||||
className="mt-2 text-xs font-bold text-blue-600 hover:text-blue-700 hover:underline"
|
||||
>
|
||||
|
||||
@@ -151,7 +151,7 @@ export const TopNavigation = () => {
|
||||
const isActive = bekleyen ? bekleyen === item.id : activeTab === item.id;
|
||||
const yukleniyor = bekleyen === item.id;
|
||||
return (
|
||||
<button
|
||||
<button type="button"
|
||||
key={item.id}
|
||||
data-tab={item.id}
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
|
||||
@@ -290,7 +290,7 @@ export const UrunAramaModal = () => {
|
||||
)}
|
||||
<span className="text-slate-400">{t.durum}</span>
|
||||
{/^\d{4}\/\d+$/.test(String(t.ikn || '')) && (
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => { openSearchTender(t); setIsUrunAramaOpen(false); }}
|
||||
className="font-bold text-indigo-700 hover:underline"
|
||||
>
|
||||
@@ -312,7 +312,7 @@ export const UrunAramaModal = () => {
|
||||
|
||||
{sonuc.totalCount > SAYFA && (
|
||||
<div className="flex items-center justify-center gap-3 pt-2">
|
||||
<button
|
||||
<button type="button"
|
||||
disabled={durum.sayfa === 0 || durum.yukleniyor}
|
||||
onClick={() => sorgula(submitted, durum.sayfa - 1)}
|
||||
className="text-xs font-bold px-3 py-1.5 rounded-lg border border-slate-300 disabled:opacity-40"
|
||||
@@ -322,7 +322,7 @@ export const UrunAramaModal = () => {
|
||||
<span className="text-xs text-slate-500">
|
||||
{durum.sayfa + 1} / {Math.ceil(sonuc.totalCount / SAYFA).toLocaleString('tr-TR')}
|
||||
</span>
|
||||
<button
|
||||
<button type="button"
|
||||
disabled={(durum.sayfa + 1) * SAYFA >= sonuc.totalCount || durum.yukleniyor}
|
||||
onClick={() => sorgula(submitted, durum.sayfa + 1)}
|
||||
className="text-xs font-bold px-3 py-1.5 rounded-lg border border-slate-300 disabled:opacity-40"
|
||||
|
||||
@@ -98,7 +98,7 @@ export const AiModelSettings = () => {
|
||||
<p className="text-xs text-indigo-200 mt-0.5">Şartname analizi ve AI özelliklerinde kullanılacak modeli ve yedek (fallback) sırasını belirleyin.</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={handleSave}
|
||||
className={`px-4 py-2.5 rounded-xl font-bold text-sm flex items-center gap-2 transition ${saved ? 'bg-emerald-500 text-white' : 'bg-white text-indigo-900 hover:bg-indigo-50'}`}
|
||||
>
|
||||
@@ -127,11 +127,11 @@ export const AiModelSettings = () => {
|
||||
className={`flex items-center gap-4 p-4 rounded-xl border ${idx === 0 ? 'border-indigo-300 bg-indigo-50/40 ring-1 ring-indigo-200' : 'border-slate-200 bg-slate-50/60'}`}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-1 w-8">
|
||||
<button onClick={() => move(idx, -1)} disabled={idx === 0} className="text-slate-400 hover:text-indigo-600 disabled:opacity-30">
|
||||
<button type="button" onClick={() => move(idx, -1)} disabled={idx === 0} className="text-slate-400 hover:text-indigo-600 disabled:opacity-30">
|
||||
<ArrowUp className="w-4 h-4" />
|
||||
</button>
|
||||
<span className="text-xs font-black text-slate-400">{idx + 1}</span>
|
||||
<button onClick={() => move(idx, 1)} disabled={idx === chain.length - 1} className="text-slate-400 hover:text-indigo-600 disabled:opacity-30">
|
||||
<button type="button" onClick={() => move(idx, 1)} disabled={idx === chain.length - 1} className="text-slate-400 hover:text-indigo-600 disabled:opacity-30">
|
||||
<ArrowDown className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -155,11 +155,11 @@ export const AiModelSettings = () => {
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{idx !== 0 && (
|
||||
<button onClick={() => setPrimary(idx)} className="text-[11px] font-bold text-indigo-600 hover:text-indigo-800 border border-indigo-200 rounded-lg px-2.5 py-1.5">
|
||||
<button type="button" onClick={() => setPrimary(idx)} className="text-[11px] font-bold text-indigo-600 hover:text-indigo-800 border border-indigo-200 rounded-lg px-2.5 py-1.5">
|
||||
Birincil yap
|
||||
</button>
|
||||
)}
|
||||
<button onClick={() => remove(idx)} disabled={chain.length <= 1} className="text-slate-400 hover:text-red-500 disabled:opacity-30 p-1.5">
|
||||
<button type="button" onClick={() => remove(idx)} disabled={chain.length <= 1} className="text-slate-400 hover:text-red-500 disabled:opacity-30 p-1.5">
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -178,7 +178,7 @@ export const AiModelSettings = () => {
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{available.map(m => (
|
||||
<button
|
||||
<button type="button"
|
||||
key={m.id}
|
||||
onClick={() => add(m.id)}
|
||||
className="text-left p-4 rounded-xl border border-slate-200 hover:border-indigo-300 hover:bg-indigo-50/40 transition group"
|
||||
|
||||
@@ -89,7 +89,7 @@ const ApiKeySession = () => {
|
||||
|
||||
return (
|
||||
<div className="space-y-6 animate-fade-in">
|
||||
{hata && <button disabled={busy || loading} onClick={yukle} className="px-4 py-2 border rounded-lg">Yeniden dene</button>}
|
||||
{hata && <button type="button" disabled={busy || loading} onClick={yukle} className="px-4 py-2 border rounded-lg">Yeniden dene</button>}
|
||||
<div className="bg-linear-to-r from-slate-900 via-violet-950 to-indigo-900 text-white p-6 rounded-2xl shadow-lg border border-violet-800/40">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 rounded-2xl bg-violet-600 flex items-center justify-center shadow-md">
|
||||
@@ -124,7 +124,7 @@ const ApiKeySession = () => {
|
||||
<code className="flex-1 bg-white border border-emerald-200 rounded-lg px-3 py-2 text-[11px] font-mono break-all">
|
||||
{yeniAnahtar.anahtar}
|
||||
</code>
|
||||
<button onClick={() => kopyala(yeniAnahtar.anahtar)}
|
||||
<button type="button" onClick={() => kopyala(yeniAnahtar.anahtar)}
|
||||
className="bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs px-3 py-2 rounded-lg flex items-center gap-1.5 shrink-0">
|
||||
{kopyalandi ? <Check className="w-3.5 h-3.5" /> : <Copy className="w-3.5 h-3.5" />}
|
||||
{kopyalandi ? 'Kopyalandı' : 'Kopyala'}
|
||||
@@ -167,7 +167,7 @@ const ApiKeySession = () => {
|
||||
{k.son_kullanim ? `son kullanım: ${new Date(k.son_kullanim).toLocaleString('tr-TR')}` : 'hiç kullanılmadı'}
|
||||
</p>
|
||||
</div>
|
||||
<button onClick={() => iptal(k.id)} disabled={busy || loading}
|
||||
<button type="button" onClick={() => iptal(k.id)} disabled={busy || loading}
|
||||
className="text-slate-400 hover:text-red-500 shrink-0" title="Anahtarı iptal et">
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
@@ -180,7 +180,7 @@ const ApiKeySession = () => {
|
||||
<input value={yeniAd} onChange={(e) => setYeniAd(e.target.value)}
|
||||
aria-label="Anahtar adı" placeholder="Anahtar adı (ör. ERP Entegrasyonu)" disabled={kilitli || busy || loading || !durum}
|
||||
className="min-w-0 flex-1 bg-slate-50 border border-slate-200 rounded-lg px-2.5 py-2 text-xs disabled:opacity-50" />
|
||||
<button onClick={olustur} disabled={busy || loading || !durum || kilitli || (limit != null && durum.anahtarlar.length >= limit)}
|
||||
<button type="button" onClick={olustur} disabled={busy || loading || !durum || kilitli || (limit != null && durum.anahtarlar.length >= limit)}
|
||||
className="bg-violet-600 hover:bg-violet-500 disabled:opacity-40 text-white font-bold text-xs px-4 rounded-lg flex items-center gap-1.5">
|
||||
{busy ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Plus className="w-3.5 h-3.5" />} Oluştur
|
||||
</button>
|
||||
|
||||
@@ -34,9 +34,9 @@ function BriefPanel(){
|
||||
<h3 className="font-bold text-sm flex gap-2"><CalendarClock className="w-4 h-4"/>Haftalık Brief</h3>
|
||||
<p className="text-xs text-slate-500">Haftanın takipli ihaleleri ve geçen hafta sonuçlanan takipliler için özet tercihi. Gönderim, sunucu zamanlayıcısının ve ilgili e-posta/Telegram hizmetinin etkin olmasına bağlıdır.</p>
|
||||
<div className="flex gap-3 flex-wrap">
|
||||
{value&&<button role="switch" aria-label="Haftalık özet gönderimi" aria-checked={!value.kapali} disabled={busy} onClick={()=>run(false)} className="border rounded px-3 py-2 text-xs">{value.kapali?'Haftalık özet kapalı':'Haftalık özet açık'}</button>}
|
||||
<button disabled={busy||!value} onClick={()=>run(true)} className="border rounded px-3 py-2 text-xs">Önizle</button>
|
||||
<button disabled={busy} onClick={()=>setAttempt(n=>n+1)} className="border rounded px-3 py-2 text-xs">Özet ayarını yenile</button>
|
||||
{value&&<button type="button" role="switch" aria-label="Haftalık özet gönderimi" aria-checked={!value.kapali} disabled={busy} onClick={()=>run(false)} className="border rounded px-3 py-2 text-xs">{value.kapali?'Haftalık özet kapalı':'Haftalık özet açık'}</button>}
|
||||
<button type="button" disabled={busy||!value} onClick={()=>run(true)} className="border rounded px-3 py-2 text-xs">Önizle</button>
|
||||
<button type="button" disabled={busy} onClick={()=>setAttempt(n=>n+1)} className="border rounded px-3 py-2 text-xs">Özet ayarını yenile</button>
|
||||
</div>
|
||||
{busy&&<p role="status" className="text-xs">Haftalık özet işlemi sürüyor…</p>}
|
||||
{error&&<p role="alert" className="text-xs text-red-700">{error}</p>}
|
||||
|
||||
@@ -59,7 +59,7 @@ export const CompetitorRadar = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setActiveTab('sozlesme')}
|
||||
className="bg-white/10 hover:bg-white/20 text-white font-bold text-xs px-4 py-2.5 rounded-xl border border-white/20 transition-colors flex items-center gap-2 shrink-0"
|
||||
>
|
||||
@@ -120,7 +120,7 @@ export const CompetitorRadar = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => addFromReal(f)}
|
||||
disabled={competitorSaving || watched.has(f.name.trim().toLocaleLowerCase('tr-TR'))}
|
||||
className="text-[11px] font-bold px-3 py-1.5 rounded-lg border border-indigo-200 text-indigo-700 hover:bg-indigo-50 disabled:opacity-40 shrink-0"
|
||||
@@ -207,7 +207,7 @@ export const CompetitorRadar = () => {
|
||||
|
||||
{/* Notification Toggles & Actions */}
|
||||
<div className="flex items-center gap-3 shrink-0">
|
||||
<button
|
||||
<button type="button"
|
||||
disabled title="Rakip teklif bildirimleri henüz etkin değil"
|
||||
className="text-xs font-extrabold px-3 py-1.5 rounded-xl border flex items-center gap-1.5 bg-slate-100 text-slate-400 border-slate-200"
|
||||
>
|
||||
@@ -215,7 +215,7 @@ export const CompetitorRadar = () => {
|
||||
<span>Teklif uyarısı etkin değil</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
disabled title="Rakip kazanım bildirimleri henüz etkin değil"
|
||||
className="text-xs font-extrabold px-3 py-1.5 rounded-xl border flex items-center gap-1.5 bg-slate-100 text-slate-400 border-slate-200"
|
||||
>
|
||||
@@ -223,7 +223,7 @@ export const CompetitorRadar = () => {
|
||||
<span>Kazanım uyarısı etkin değil</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
disabled={competitorSaving} aria-label={`${comp.name} radardan çıkar`} onClick={() => removeCompetitor(comp.id)}
|
||||
className="p-2 text-slate-400 hover:text-red-600 rounded-lg transition-colors"
|
||||
title="Radardan Çıkar"
|
||||
|
||||
@@ -61,7 +61,7 @@ export const KikDecisions = () => {
|
||||
/>
|
||||
<Search className="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2" />
|
||||
</div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => ara()}
|
||||
disabled={loading || !q.trim()}
|
||||
className="bg-amber-600 hover:bg-amber-500 disabled:opacity-40 text-white font-bold text-xs px-5 rounded-lg flex items-center gap-2"
|
||||
@@ -74,7 +74,7 @@ export const KikDecisions = () => {
|
||||
<div className="flex gap-2 flex-wrap items-center">
|
||||
<span className="text-[11px] text-slate-400 font-semibold">Takibinizdeki idareler:</span>
|
||||
{oneriler.map((k) => (
|
||||
<button key={k} onClick={() => { setQ(k); ara(k); }}
|
||||
<button type="button" key={k} onClick={() => { setQ(k); ara(k); }}
|
||||
className="text-[11px] bg-slate-100 hover:bg-slate-200 text-slate-700 px-2 py-1 rounded-md font-medium truncate max-w-[220px]">
|
||||
{k}
|
||||
</button>
|
||||
|
||||
@@ -24,7 +24,7 @@ export const McpRssKarti = () => {
|
||||
<div key={o.k} className="space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-xs font-bold text-slate-800">{o.ad} <span className="font-normal text-slate-500">— {o.not}</span></p>
|
||||
<button onClick={() => kopyala(o.k, o.kod)} className="text-[11px] flex items-center gap-1 border border-slate-200 rounded-lg px-2 py-1 hover:bg-slate-50">{kopya === o.k ? <Check className="w-3 h-3 text-emerald-600" /> : <Copy className="w-3 h-3" />} Kopyala</button>
|
||||
<button type="button" onClick={() => kopyala(o.k, o.kod)} className="text-[11px] flex items-center gap-1 border border-slate-200 rounded-lg px-2 py-1 hover:bg-slate-50">{kopya === o.k ? <Check className="w-3 h-3 text-emerald-600" /> : <Copy className="w-3 h-3" />} Kopyala</button>
|
||||
</div>
|
||||
<pre className="overflow-x-auto rounded-lg bg-slate-900 text-slate-100 p-2 text-[11px]">{o.kod}</pre>
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@ export const McpRssKarti = () => {
|
||||
<div className="border-t border-slate-100 pt-3 space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-xs font-bold text-slate-800 flex items-center gap-1"><Rss className="w-3.5 h-3.5 text-orange-500" /> RSS beslemesi <span className="font-normal text-slate-500">— Feedly, Outlook, Slack RSS, n8n… (il, sektor, q filtreleri)</span></p>
|
||||
<button onClick={() => kopyala('rss', rss)} className="text-[11px] flex items-center gap-1 border border-slate-200 rounded-lg px-2 py-1 hover:bg-slate-50">{kopya === 'rss' ? <Check className="w-3 h-3 text-emerald-600" /> : <Copy className="w-3 h-3" />} Kopyala</button>
|
||||
<button type="button" onClick={() => kopyala('rss', rss)} className="text-[11px] flex items-center gap-1 border border-slate-200 rounded-lg px-2 py-1 hover:bg-slate-50">{kopya === 'rss' ? <Check className="w-3 h-3 text-emerald-600" /> : <Copy className="w-3 h-3" />} Kopyala</button>
|
||||
</div>
|
||||
<pre className="overflow-x-auto rounded-lg bg-slate-900 text-slate-100 p-2 text-[11px]">{rss}</pre>
|
||||
<p className="text-[11px] text-slate-500">Sonuç ilanları beslemesi anahtarsız: <code>{origin}/api/feeds/sonuc.rss</code> (firma, idare, il filtreleri). Anahtarı <code>ih_live_ANAHTARINIZ</code> yerine yazın.</p>
|
||||
|
||||
@@ -126,7 +126,7 @@ const SubscriptionForm = () => {
|
||||
{a.deliveryStatus && <span className="block text-amber-700">{a.deliveryStatus.state==='uncertain'?'Gönderim sonucu belirsiz. Uygulama yöneticisinin sağlayıcı kaydını kontrol etmesi gerekiyor.':'E-posta gönderimi bekliyor.'}</span>}
|
||||
</p>
|
||||
</div>
|
||||
<button onClick={() => sil(a.id)} disabled={busy} className="text-slate-400 hover:text-red-500 shrink-0 mt-0.5" aria-label={`Aboneliği sil: ${ozet(a)}`} title="Sil">
|
||||
<button type="button" onClick={() => sil(a.id)} disabled={busy} className="text-slate-400 hover:text-red-500 shrink-0 mt-0.5" aria-label={`Aboneliği sil: ${ozet(a)}`} title="Sil">
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -143,7 +143,7 @@ const SubscriptionForm = () => {
|
||||
<input value={site} onChange={(e) => setSite(e.target.value)}
|
||||
placeholder="firmasitesi.com"
|
||||
className="flex-1 bg-white border border-blue-200 rounded-lg px-2.5 py-2 text-xs" />
|
||||
<button onClick={siteyiTara} disabled={taraniyor || !site.trim()}
|
||||
<button type="button" onClick={siteyiTara} disabled={taraniyor || !site.trim()}
|
||||
className="bg-blue-600 hover:bg-blue-500 disabled:opacity-40 text-white font-bold text-xs px-3.5 rounded-lg flex items-center gap-1.5">
|
||||
{taraniyor ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Sparkles className="w-3.5 h-3.5" />}
|
||||
{taraniyor ? 'Taranıyor…' : 'Tara'}
|
||||
@@ -163,7 +163,7 @@ const SubscriptionForm = () => {
|
||||
<p className="text-[10px] text-blue-700 font-semibold">Önerilen konular — eklemek için tıklayın:</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{oneriler.map((k) => (
|
||||
<button key={k} onClick={() => konuEkle(k)}
|
||||
<button type="button" key={k} onClick={() => konuEkle(k)}
|
||||
className="text-[11px] bg-white hover:bg-blue-100 border border-blue-300 text-blue-900 px-2 py-1 rounded-md font-medium">
|
||||
+ {k}
|
||||
</button>
|
||||
@@ -185,7 +185,7 @@ const SubscriptionForm = () => {
|
||||
{konular.map((k) => (
|
||||
<span key={k} className="text-[11px] bg-emerald-50 border border-emerald-300 text-emerald-900 px-2 py-1 rounded-md font-semibold flex items-center gap-1">
|
||||
{k}
|
||||
<button onClick={() => konuCikar(k)} className="text-emerald-700 hover:text-red-600"><X className="w-3 h-3" /></button>
|
||||
<button type="button" onClick={() => konuCikar(k)} className="text-emerald-700 hover:text-red-600"><X className="w-3 h-3" /></button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
@@ -196,14 +196,14 @@ const SubscriptionForm = () => {
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') { konuEkle(ozelKonu); setOzelKonu(''); } }}
|
||||
placeholder="Kendi konunuzu yazın ve Enter'a basın"
|
||||
className="flex-1 bg-slate-50 border border-slate-200 rounded-lg px-2.5 py-2 text-xs" />
|
||||
<button onClick={() => { konuEkle(ozelKonu); setOzelKonu(''); }}
|
||||
<button type="button" onClick={() => { konuEkle(ozelKonu); setOzelKonu(''); }}
|
||||
className="bg-slate-800 hover:bg-slate-700 text-white font-bold text-xs px-3 rounded-lg">Ekle</button>
|
||||
</div>
|
||||
|
||||
<p className="text-[10px] text-slate-400 pt-1">Hazır sektörler:</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{sektorListesi.map((s) => (
|
||||
<button key={s} onClick={() => konuEkle(s)}
|
||||
<button type="button" key={s} onClick={() => konuEkle(s)}
|
||||
className="text-[11px] bg-slate-100 hover:bg-slate-200 text-slate-700 px-2 py-1 rounded-md">
|
||||
+ {s}
|
||||
</button>
|
||||
@@ -213,7 +213,7 @@ const SubscriptionForm = () => {
|
||||
|
||||
{/* Gelişmiş daraltma (isteğe bağlı) */}
|
||||
<div>
|
||||
<button onClick={() => setGelismis(!gelismis)} className="text-[11px] font-bold text-slate-500 hover:text-slate-800 flex items-center gap-1">
|
||||
<button type="button" onClick={() => setGelismis(!gelismis)} className="text-[11px] font-bold text-slate-500 hover:text-slate-800 flex items-center gap-1">
|
||||
{gelismis ? <ChevronUp className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
|
||||
Daha da daralt (isteğe bağlı) — varsayılan: tüm Türkiye
|
||||
</button>
|
||||
@@ -235,7 +235,7 @@ const SubscriptionForm = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button onClick={kaydet} disabled={busy || abonelikler===null || !konular.length}
|
||||
<button type="button" onClick={kaydet} disabled={busy || abonelikler===null || !konular.length}
|
||||
className="w-full bg-emerald-600 hover:bg-emerald-500 disabled:opacity-40 text-white font-bold text-xs py-2.5 rounded-xl flex items-center justify-center gap-2">
|
||||
{busy ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Plus className="w-3.5 h-3.5" />}
|
||||
Bildirimi Kaydet
|
||||
@@ -244,7 +244,7 @@ const SubscriptionForm = () => {
|
||||
{error && (
|
||||
<div role="alert" className="bg-red-50 border border-red-200 rounded-lg p-2.5 flex gap-2">
|
||||
<AlertTriangle className="w-3.5 h-3.5 text-red-600 shrink-0 mt-0.5" />
|
||||
<p className="text-[11px] text-red-800">{error} <button disabled={busy} onClick={yukle} className="underline">Abonelikleri yeniden yükle</button></p>
|
||||
<p className="text-[11px] text-red-800">{error} <button type="button" disabled={busy} onClick={yukle} className="underline">Abonelikleri yeniden yükle</button></p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -62,10 +62,10 @@ export const Pricing = () => {
|
||||
{/* Aylık / Yıllık */}
|
||||
<div className="flex justify-center">
|
||||
<div className="bg-white border border-slate-200 rounded-xl p-1 inline-flex text-xs font-bold shadow-xs">
|
||||
<button onClick={() => setYillik(true)} className={`px-4 py-2 rounded-lg transition-colors flex items-center gap-1.5 ${yillik ? 'bg-blue-600 text-white' : 'text-slate-600 hover:bg-slate-50'}`}>
|
||||
<button type="button" onClick={() => setYillik(true)} className={`px-4 py-2 rounded-lg transition-colors flex items-center gap-1.5 ${yillik ? 'bg-blue-600 text-white' : 'text-slate-600 hover:bg-slate-50'}`}>
|
||||
Yıllık <span className={`text-[10px] px-1.5 py-0.5 rounded-full ${yillik ? 'bg-amber-400 text-slate-900' : 'bg-emerald-100 text-emerald-800'}`}>%17 indirim</span>
|
||||
</button>
|
||||
<button onClick={() => setYillik(false)} className={`px-4 py-2 rounded-lg transition-colors ${!yillik ? 'bg-blue-600 text-white' : 'text-slate-600 hover:bg-slate-50'}`}>
|
||||
<button type="button" onClick={() => setYillik(false)} className={`px-4 py-2 rounded-lg transition-colors ${!yillik ? 'bg-blue-600 text-white' : 'text-slate-600 hover:bg-slate-50'}`}>
|
||||
Aylık
|
||||
</button>
|
||||
</div>
|
||||
@@ -107,7 +107,7 @@ export const Pricing = () => {
|
||||
</div>
|
||||
</div>
|
||||
{plan.iptalEdilebilir && (
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setIptalAcik(true)}
|
||||
className="text-xs font-bold text-red-600 hover:text-red-700 hover:bg-red-50 border border-red-200 px-3 py-2 rounded-xl flex items-center gap-1.5 transition-colors"
|
||||
>
|
||||
@@ -183,7 +183,7 @@ export const Pricing = () => {
|
||||
Mevcut planınız
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => upgrade(p.id)}
|
||||
disabled={!(yillik ? p.satinAlinabilirYillik : p.satinAlinabilir) || busy === p.id || !authUser}
|
||||
title={!authUser ? 'Önce giriş yapın' : !(yillik ? p.satinAlinabilirYillik : p.satinAlinabilir) ? 'Ödeme altyapısı bağlanmadı' : ''}
|
||||
|
||||
@@ -162,7 +162,7 @@ const PwaPanel = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={refreshCacheStatus} disabled={cacheLoading}
|
||||
className="w-full bg-slate-100 hover:bg-slate-200 text-slate-800 font-bold text-xs py-2.5 rounded-xl border border-slate-200 transition-colors"
|
||||
>
|
||||
@@ -192,17 +192,17 @@ const PwaPanel = () => {
|
||||
{permission === 'denied' ? (
|
||||
<div className="space-y-2 text-xs text-slate-700">
|
||||
<p>Bu site için bildirimler tarayıcı tarafından engellenmiş. Adres çubuğundaki site bilgileri veya tarayıcının site ayarlarından bildirim iznini değiştirin, ardından buraya dönün.</p>
|
||||
<button onClick={refreshPermission} className="border rounded px-3 py-2">Bildirim iznini yeniden kontrol et</button>
|
||||
<button type="button" onClick={refreshPermission} className="border rounded px-3 py-2">Bildirim iznini yeniden kontrol et</button>
|
||||
</div>
|
||||
) : permission === 'unsupported' ? (
|
||||
<p className="text-xs text-slate-700">Bu tarayıcıda bildirim özelliği kullanılamıyor. Uygulama içindeki bildirim listesini kullanabilirsiniz.</p>
|
||||
) : permission !== 'granted' ? (
|
||||
<button onClick={handleEnablePush} disabled={reminderSaving || permissionBusy}
|
||||
<button type="button" onClick={handleEnablePush} disabled={reminderSaving || permissionBusy}
|
||||
className="w-full bg-amber-500 hover:bg-amber-400 disabled:opacity-40 text-white font-bold text-xs py-2.5 rounded-xl">
|
||||
{permissionBusy?'İzin yanıtı bekleniyor…':'Bildirimlere İzin Ver'}
|
||||
</button>
|
||||
) : (
|
||||
<button onClick={() => notify('Test bildirimi', isQuietNow() || !reminderSettings.pushEnabled ? 'Tarayıcı bildirimi kapalı veya sessiz saatte — listeye eklendi.' : 'Tarayıcı bildirimi gösterim denemesi.')}
|
||||
<button type="button" onClick={() => notify('Test bildirimi', isQuietNow() || !reminderSettings.pushEnabled ? 'Tarayıcı bildirimi kapalı veya sessiz saatte — listeye eklendi.' : 'Tarayıcı bildirimi gösterim denemesi.')}
|
||||
className="w-full bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs py-2.5 rounded-xl">
|
||||
Test Bildirimi Gönder
|
||||
</button>
|
||||
@@ -268,7 +268,7 @@ const PwaPanel = () => {
|
||||
<button type="button" disabled={emailSending} onClick={()=>setEmailConfigAttempt(n=>n+1)} className="text-xs underline">E-posta durumunu yenile</button>
|
||||
<p className="text-xs text-slate-600">E-posta gönderimlerini aşağıdaki bildirim aboneliklerinden yönetin. Tarayıcı bildirim seçimi ve sessiz saatler e-posta aboneliklerini kapatmaz.</p>
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={sendTestEmail}
|
||||
disabled={emailSending || emailCfg !== true || !authUser}
|
||||
className="w-full bg-blue-600 hover:bg-blue-500 disabled:opacity-40 text-white font-bold text-xs py-2.5 rounded-xl flex items-center justify-center gap-2"
|
||||
@@ -300,7 +300,7 @@ const PwaPanel = () => {
|
||||
İhaleist'i iOS, Android, Windows veya Mac cihazınıza bir tıklama ile uygulama olarak kurun.
|
||||
</p>
|
||||
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={handleInstallPwa}
|
||||
disabled={installed}
|
||||
className="w-full bg-cyan-500 hover:bg-cyan-400 disabled:bg-slate-700 text-slate-950 font-black text-xs py-3 rounded-xl flex items-center justify-center gap-2 shadow-md transition-colors cursor-pointer"
|
||||
|
||||
@@ -102,7 +102,7 @@ export const RedFlags = () => {
|
||||
</div>
|
||||
<p className="text-xs text-slate-700 mt-1.5 leading-relaxed">{f.aciklama}</p>
|
||||
{f.tender ? (
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setActiveTenderDetail(f.tender)}
|
||||
className="text-[11px] font-bold text-blue-700 hover:underline mt-1.5 text-left"
|
||||
>
|
||||
|
||||
@@ -65,7 +65,7 @@ export const TedTenders = () => {
|
||||
{/* Mod seçici */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{MODLAR.map((m) => (
|
||||
<button
|
||||
<button type="button"
|
||||
key={m.id}
|
||||
onClick={() => setMod(m.id)}
|
||||
className={`px-4 py-2 rounded-xl text-xs font-bold border transition-colors ${
|
||||
@@ -75,7 +75,7 @@ export const TedTenders = () => {
|
||||
{m.label}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => yukle(mod)}
|
||||
disabled={loading}
|
||||
className="px-3 py-2 rounded-xl text-xs font-bold border border-slate-200 bg-white text-slate-600 hover:bg-slate-50 flex items-center gap-1.5 disabled:opacity-50"
|
||||
|
||||
@@ -61,7 +61,7 @@ export const TurkeyHeatMap = () => {
|
||||
const c = density.get(normProvince(il)) || 0;
|
||||
const { bg, fg } = heat(c, max);
|
||||
return (
|
||||
<button
|
||||
<button type="button"
|
||||
key={il}
|
||||
onClick={() => c && openCity(il)}
|
||||
disabled={!c}
|
||||
|
||||
@@ -58,7 +58,7 @@ export const VatCalculator = () => {
|
||||
|
||||
{/* Mode Selector */}
|
||||
<div className="grid grid-cols-2 gap-3 p-1 bg-slate-100 rounded-xl">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setMode('haric')}
|
||||
className={`py-2.5 rounded-lg text-xs font-extrabold transition-all ${
|
||||
mode === 'haric' ? 'bg-white text-blue-700 shadow-2xs' : 'text-slate-600 hover:text-slate-900'
|
||||
@@ -66,7 +66,7 @@ export const VatCalculator = () => {
|
||||
>
|
||||
KDV Hariç ➔ KDV Dahil Hesapla
|
||||
</button>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setMode('dahil')}
|
||||
className={`py-2.5 rounded-lg text-xs font-extrabold transition-all ${
|
||||
mode === 'dahil' ? 'bg-white text-blue-700 shadow-2xs' : 'text-slate-600 hover:text-slate-900'
|
||||
@@ -130,7 +130,7 @@ export const VatCalculator = () => {
|
||||
<div className="bg-slate-900 text-white p-6 rounded-xl space-y-4 shadow-lg">
|
||||
<div className="flex items-center justify-between border-b border-slate-800 pb-3">
|
||||
<span className="font-bold text-xs uppercase tracking-wider text-slate-300">Hesaplama Sonucu</span>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={handleCopy}
|
||||
className="bg-slate-800 hover:bg-slate-700 text-blue-400 font-semibold px-3 py-1.5 rounded-lg text-xs flex items-center gap-1.5 transition-colors border border-slate-700"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user