persist imported calendar events per session

This commit is contained in:
2026-09-06 18:43:44 +03:00
parent 580bc6dd9f
commit bdf67e58e4
2 changed files with 10 additions and 2 deletions
+4
View File
@@ -2100,3 +2100,7 @@ Doğrulama: HTTP readiness testi geçti; 290/290 otomatik test ve üretim derlem
Takvim ekranı artık Google/Outlook’tan dışa aktarılan `.ics` dosyalarını yerel oturumda içe alıyor. Ayrıştırıcı 200 KB ve 100 etkinlik sınırı uyguluyor, geçersiz tarihleri reddediyor ve harici etkinlikleri kaynak ihale kayıtlarından ayrı, salt okunur takvim girdileri olarak gösteriyor. OAuth ile otomatik senkronizasyon bu özelliğin kapsamı dışında tutuldu. Takvim ekranı artık Google/Outlook’tan dışa aktarılan `.ics` dosyalarını yerel oturumda içe alıyor. Ayrıştırıcı 200 KB ve 100 etkinlik sınırı uyguluyor, geçersiz tarihleri reddediyor ve harici etkinlikleri kaynak ihale kayıtlarından ayrı, salt okunur takvim girdileri olarak gösteriyor. OAuth ile otomatik senkronizasyon bu özelliğin kapsamı dışında tutuldu.
Doğrulama: ICS round-trip, İstanbul saat dönüşümü, kaçış karakterleri, bozuk tarih, boyut ve etkinlik sayısı sınırları test edildi; 292/292 test ve üretim derlemesi başarılı. Doğrulama: ICS round-trip, İstanbul saat dönüşümü, kaçış karakterleri, bozuk tarih, boyut ve etkinlik sayısı sınırları test edildi; 292/292 test ve üretim derlemesi başarılı.
İçe aktarılan harici etkinlikler artık `sessionStorage` içinde tutuluyor; kullanıcı takvim sekmesinden ayrılıp döndüğünde aynı tarayıcı oturumunda kayıtlar korunuyor. Temizleme düğmesi tüm harici etkinlikleri kaldırıyor; özel tarama modunda storage yazımı başarısız olursa uygulama akışı devam ediyor.
Doğrulama: 292/292 otomatik test ve üretim derlemesi başarılı.
+6 -2
View File
@@ -10,7 +10,11 @@ export const TenderCalendar = () => {
const today = istanbulDateKey(now); const today = istanbulDateKey(now);
const [month,setMonth] = useState(today.slice(0,7)); const [month,setMonth] = useState(today.slice(0,7));
const [selectedDay, setSelectedDay] = useState(today); const [selectedDay, setSelectedDay] = useState(today);
const [imported,setImported] = useState([]), [importError,setImportError] = useState(''); const [imported,setImported] = useState(() => {
try { const saved = JSON.parse(sessionStorage.getItem('ihaleist_imported_calendar') || '[]'); return Array.isArray(saved) && saved.length <= 100 ? saved : []; }
catch { return []; }
}), [importError,setImportError] = useState('');
React.useEffect(() => { try { sessionStorage.setItem('ihaleist_imported_calendar', JSON.stringify(imported)); } catch { /* private browsing can deny storage */ } }, [imported]);
const allTenders = useMemo(() => [...tenders, ...imported], [tenders, imported]); const allTenders = useMemo(() => [...tenders, ...imported], [tenders, imported]);
const agenda=useMemo(()=>calendarAgenda(allTenders,now),[allTenders,now]); const agenda=useMemo(()=>calendarAgenda(allTenders,now),[allTenders,now]);
const daysList=calendarMonthDays(month).map(day=>({...day,count:agenda.days.get(day.key)?.length || 0})); const daysList=calendarMonthDays(month).map(day=>({...day,count:agenda.days.get(day.key)?.length || 0}));
@@ -59,7 +63,7 @@ export const TenderCalendar = () => {
catch(error) { setImported([]); setImportError(error.message); } catch(error) { setImported([]); setImportError(error.message); }
event.target.value=''; event.target.value='';
}} className="block text-xs" /> }} className="block text-xs" />
{imported.length>0&&<p role="status" className="text-xs text-emerald-700">{imported.length} harici etkinlik içe aktarıldı.</p>} {imported.length>0&&<div className="flex flex-wrap items-center gap-3"><p role="status" className="text-xs text-emerald-700">{imported.length} harici etkinlik içe aktarıldı.</p><button type="button" className="text-xs underline text-slate-700" onClick={()=>setImported([])}>İçe aktarılanları temizle</button></div>}
{importError&&<p role="alert" className="text-xs text-red-700">Takvim içe aktarılamadı: {importError}</p>} {importError&&<p role="alert" className="text-xs text-red-700">Takvim içe aktarılamadı: {importError}</p>}
</section> </section>
<div role="group" className="flex flex-wrap gap-2" aria-label="Takvim ayı gezinme"> <div role="group" className="flex flex-wrap gap-2" aria-label="Takvim ayı gezinme">