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:
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user