Kullanıcı testi hataları: sabit tarihler, kişisel varsayılanlar, kılavuz düğmesi, mobil başlık, AI taslak çökmesi

- Günlük Bülten ve İhale Takvimi sabit "10.08.2026" yerine bugünden hesaplar;
  takvim 7 günü gerçek ihale tarihleriyle sayar
- Ekip/üye/firma varsayılanları oturumdaki kullanıcıdan; sahte "Ekip Notları"
  ve sabit "%94 Uyum" rozeti kaldırıldı (gerçek uyum skoru varsa gösterilir)
- Kılavuz düğmesi document.body'ye portal (ekran dışına düşüyordu)
- Detay modalı havuzdaki güncel kaydı okur: Takip Et / Görev aç etiketleri güncellenir
- Mobil: başlıkta ikincil düğmeler md altında gizli, arama formu küçülebilir
- PWA sayfası: sahte önbellek sayıları yerine Cache API ve bellek sayımı
- Sözleşme sorgula pazar payı bir ondalık (hep %0 görünüyordu)
- Rakip Radar "SMS" vaadi düzeltildi; sektör=CPV etiketi tekrarı gizlendi;
  Stripe kurulum uyarısı yalnız yöneticiye
- Açılışta 7 gereksiz PUT /api/me/data: ilk yükleme yazımı atlanır
- server: teklif taslağı çökmesi (kalemOku nesne döndürür), denetim yanıtından
  model adı kaldırıldı, gereklilikler satır bazlı; derin analiz 12k token +
  kesik JSON onarımı

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 09:43:41 +03:00
co-authored by Claude Fable 5.1
parent 6d7d938711
commit 08c0a0af7e
14 changed files with 130 additions and 60 deletions
+1 -1
View File
@@ -66,7 +66,7 @@ export const CompetitorRadar = () => {
Proaktif Takip
</span>
</div>
<p className="text-xs text-indigo-200 mt-0.5">İzlemeye aldığınız rakip firmalar ihaleye teklif verdiğinde veya kazandığında anında SMS ve Push bildirimi alın.</p>
<p className="text-xs text-indigo-200 mt-0.5">İzlemeye aldığınız rakip firmalar ihaleye teklif verdiğinde veya kazandığında uygulama içi, e-posta ve Telegram bildirimi alın.</p>
</div>
</div>
+2 -2
View File
@@ -75,10 +75,10 @@ export const ContractSearch = () => {
<div>
<div className="flex justify-between text-xs font-semibold text-slate-700 mb-1">
<span>Pazar Payı (hacim)</span>
<span className="font-bold text-indigo-700">%{comp.share}</span>
<span className="font-bold text-indigo-700">%{Number(comp.share).toLocaleString('tr-TR', { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</span>
</div>
<div className="w-full bg-slate-100 rounded-full h-2 overflow-hidden">
<div className="bg-indigo-600 h-2 rounded-full" style={{ width: `${Math.max(comp.share, 2)}%` }}></div>
<div className="bg-indigo-600 h-2 rounded-full" style={{ width: `${Math.min(100, Math.max(comp.share * 4, 2))}%` }}></div>
</div>
</div>
</div>
+8 -2
View File
@@ -5,8 +5,14 @@ import { Newspaper, Calendar, Building2, Package, ArrowRight } from 'lucide-reac
export const DailyBulletin = () => {
const { tenders, setActiveTenderDetail } = useApp();
const todayStr = '10.08.2026';
const todayTenders = tenders.filter(t => t.tarih === todayStr || (t.eklenmeZamani || '').includes('az') || (t.eklenmeZamani || '').includes('önce'));
// Bugün: yayımlanma (ilan) tarihi bugün olanlar; ilan tarihi yoksa havuza az önce eklenenler.
const bugun = new Date();
const todayStr = `${String(bugun.getDate()).padStart(2, '0')}.${String(bugun.getMonth() + 1).padStart(2, '0')}.${bugun.getFullYear()}`;
const isoBugun = `${bugun.getFullYear()}-${String(bugun.getMonth() + 1).padStart(2, '0')}-${String(bugun.getDate()).padStart(2, '0')}`;
const bugunMu = (v) => !!v && (String(v).startsWith(isoBugun) || String(v).slice(0, 10) === todayStr);
const todayTenders = tenders.filter(t =>
bugunMu(t.ilanTarihi) || (!t.ilanTarihi && ((t.eklenmeZamani || '').includes('az') || (t.eklenmeZamani || '').includes('saat')))
);
return (
<div className="space-y-4 my-4 animate-fade-in">
+2 -2
View File
@@ -5,7 +5,7 @@ import { Check, Sparkles, CreditCard, Loader2, Info, ShieldCheck, XCircle } from
import { CancelSubscriptionModal } from '../CancelSubscriptionModal';
export const Pricing = () => {
const { authUser, plan, planYenile } = useApp();
const { authUser, plan, planYenile, isAdmin } = useApp();
const [data, setData] = useState(null);
const [busy, setBusy] = useState(null);
const [error, setError] = useState(null);
@@ -75,7 +75,7 @@ export const Pricing = () => {
<div className="bg-red-50 border border-red-200 rounded-xl p-3 text-xs text-red-800 text-center">{error}</div>
)}
{data && !data.configured && (
{data && !data.configured && isAdmin && (
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4 flex gap-3">
<Info className="w-4 h-4 text-amber-700 shrink-0 mt-0.5" />
<p className="text-xs text-amber-900 leading-relaxed">
+20 -6
View File
@@ -7,6 +7,20 @@ import { NotifySubscriptions } from './NotifySubscriptions';
export const PwaSettings = () => {
const { userProfile, authUser, tenders, reminderSettings, setReminderSettings, requestPushPermission, notify, isQuietNow } = useApp();
// Gerçek Cache API sayımı — sahte "102 ihale önbellekte" yerine
const [onbellekSayisi, setOnbellekSayisi] = useState(null);
const [onbellekAdi, setOnbellekAdi] = useState('');
useEffect(() => {
if (!('caches' in window)) return;
(async () => {
try {
const adlar = await caches.keys();
let toplam = 0;
for (const ad of adlar) toplam += (await (await caches.open(ad)).keys()).length;
setOnbellekSayisi(toplam); setOnbellekAdi(adlar[adlar.length - 1] || '');
} catch { setOnbellekSayisi(null); }
})();
}, []);
const [offlineMode, setOfflineMode] = useState(true);
const [installed, setInstalled] = useState(false);
const [permission, setPermission] = useState(typeof Notification !== 'undefined' ? Notification.permission : 'unsupported');
@@ -113,16 +127,16 @@ export const PwaSettings = () => {
<div className="space-y-2 text-xs text-slate-700">
<p className="flex justify-between">
<span>İhale Kayıtları:</span>
<strong className="text-slate-900">102 İhale Önbellekte</strong>
<span>İhale Kayıtları (bellekte):</span>
<strong className="text-slate-900">{tenders.length.toLocaleString('tr-TR')} ihale</strong>
</p>
<p className="flex justify-between">
<span>Şartname PDF'leri:</span>
<strong className="text-slate-900">18 Belge Çevrimdışı</strong>
<span>Önbelleğe alınmış istek:</span>
<strong className="text-slate-900">{onbellekSayisi == null ? '—' : `${onbellekSayisi} kayıt`}</strong>
</p>
<p className="flex justify-between">
<span>Son Senkronizasyon:</span>
<strong className="text-emerald-700">Bugün 14:45</strong>
<span>Uygulama sürümü önbelleği:</span>
<strong className="text-emerald-700">{onbellekAdi || '—'}</strong>
</p>
</div>
+10 -11
View File
@@ -4,18 +4,17 @@ import { CalendarDays, Clock, MapPin, AlertCircle } from 'lucide-react';
export const TenderCalendar = () => {
const { tenders, setActiveTenderDetail } = useApp();
const [selectedDay, setSelectedDay] = useState('10.08.2026');
const GUNLER = ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'];
const ggaayyyy = (d) => `${String(d.getDate()).padStart(2, '0')}.${String(d.getMonth() + 1).padStart(2, '0')}.${d.getFullYear()}`;
// Bugünden başlayan 7 gün; sayılar havuzdaki gerçek ihale tarihlerinden.
const daysList = Array.from({ length: 7 }, (_, i) => {
const d = new Date(); d.setHours(0, 0, 0, 0); d.setDate(d.getDate() + i);
const date = ggaayyyy(d);
return { date, label: i === 0 ? `Bugün (${GUNLER[d.getDay()]})` : i === 1 ? `Yarın (${GUNLER[d.getDay()]})` : GUNLER[d.getDay()], count: tenders.filter(t => t.tarih === date).length };
});
const [selectedDay, setSelectedDay] = useState(daysList[0].date);
const daysList = [
{ date: '10.08.2026', label: 'Bugün (Pazartesi)', count: 3 },
{ date: '11.08.2026', label: 'Salı', count: 4 },
{ date: '12.08.2026', label: 'Çarşamba', count: 2 },
{ date: '13.08.2026', label: 'Perşembe', count: 5 },
{ date: '14.08.2026', label: 'Cuma', count: 3 },
{ date: '15.08.2026', label: 'Cumartesi', count: 1 }
];
const dayTenders = tenders.filter(t => t.tarih === selectedDay || (selectedDay === '10.08.2026' && (t.tarih || '').includes('10')));
const dayTenders = tenders.filter(t => t.tarih === selectedDay);
return (
<div className="space-y-4 my-4 animate-fade-in">