diff --git a/docs/quality/evidence/qa-browser-acceptance-runtime.json b/docs/quality/evidence/qa-browser-acceptance-runtime.json index 8e5c079..56260b5 100644 --- a/docs/quality/evidence/qa-browser-acceptance-runtime.json +++ b/docs/quality/evidence/qa-browser-acceptance-runtime.json @@ -25,6 +25,7 @@ } ], "icsImported": true, + "icsStorageScoped": true, "icsPersistsAfterTabChange": true, "readyPreparationStatus": 200, "readyOutputButtons": 4, diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 608bb0a..dddcded 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -2164,3 +2164,9 @@ Doğrulama: 293 otomatik test ve üretim derlemesi başarılı. Gerçek iki hesa Teklif dosyası hazırlama ekranındaki firma bilgileri artık ham e-posta yerine normalize edilip URL kodlanmış hesap anahtarında tutuluyor. Büyük/küçük harf farkı aynı hesabı farklı yerel kayda bölmüyor; özel karakterler anahtar yapısını bozamıyor. Eski ortak/normalize edilmemiş anahtarlardan otomatik veri alınmıyor. Doğrulama: 293 otomatik test ve üretim derlemesi başarılı. Gerçek iki hesaplı tarayıcı kabulü ve canlı deployment ayrıca yapılmalı. + +### 2026-09-06 · ICS takvim verisinin hesap izolasyonu + +Harici ICS etkinlikleri daha önce ortak `sessionStorage` anahtarında tutuluyordu. Kayıtlar artık normalize edilmiş hesap e-postasına bağlı anahtarda saklanıyor; hesap değişiminde takvim temizlenip yeni hesabın etkinlikleri yükleniyor. Browser QA, anahtarın hesap kapsamlı olduğunu ve sekme değişimi sonrası kalıcılığı doğruluyor. + +Doğrulama: browser QA başarılı; 293 otomatik test ve üretim derlemesi başarılı. Gerçek iki hesaplı tarayıcı kabulü ve OAuth takvim senkronizasyonu açık. diff --git a/src/components/tools/TenderCalendar.jsx b/src/components/tools/TenderCalendar.jsx index 8ea80dd..c320f9c 100644 --- a/src/components/tools/TenderCalendar.jsx +++ b/src/components/tools/TenderCalendar.jsx @@ -6,15 +6,24 @@ import { CalendarDays } from 'lucide-react'; export const TenderCalendar = () => { const now = useDeadlineClock(); - const { tenders, setActiveTenderDetail } = useApp(); + const { tenders, setActiveTenderDetail, authUser } = useApp(); const today = istanbulDateKey(now); const [month,setMonth] = useState(today.slice(0,7)); const [selectedDay, setSelectedDay] = useState(today); - 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 calendarStorageKey = authUser?.email ? `ihaleist_imported_calendar:${encodeURIComponent(authUser.email.toLowerCase())}` : null; + const [imported,setImported] = useState([]), [importError,setImportError] = useState(''); + const [loadedCalendarKey, setLoadedCalendarKey] = useState(null); + React.useEffect(() => { + if (!calendarStorageKey) { setImported([]); setLoadedCalendarKey(null); return; } + let saved = null; + try { saved = JSON.parse(sessionStorage.getItem(calendarStorageKey) || '[]'); } catch { saved = []; } + setImported(Array.isArray(saved) && saved.length <= 100 ? saved : []); + setLoadedCalendarKey(calendarStorageKey); + }, [calendarStorageKey]); + React.useEffect(() => { + if (!loadedCalendarKey || loadedCalendarKey !== calendarStorageKey) return; + try { sessionStorage.setItem(loadedCalendarKey, JSON.stringify(imported)); } catch { /* private browsing can deny storage */ } + }, [imported, loadedCalendarKey, calendarStorageKey]); const allTenders = useMemo(() => [...tenders, ...imported], [tenders, imported]); const agenda=useMemo(()=>calendarAgenda(allTenders,now),[allTenders,now]); const daysList=calendarMonthDays(month).map(day=>({...day,count:agenda.days.get(day.key)?.length || 0})); diff --git a/tests/browser-acceptance.mjs b/tests/browser-acceptance.mjs index 05f91e5..015b06e 100644 --- a/tests/browser-acceptance.mjs +++ b/tests/browser-acceptance.mjs @@ -36,6 +36,8 @@ try { await page.locator('input[aria-label="ICS takvim dosyası seç"]').setInputFiles(icsPath); await page.getByText('1 harici etkinlik içe aktarıldı.', { exact: true }).waitFor(); evidence.checks.icsImported = true; + evidence.checks.icsStorageScoped = await page.evaluate(() => Object.keys(sessionStorage).some(key => key.startsWith('ihaleist_imported_calendar:'))); + if (!evidence.checks.icsStorageScoped) throw new Error('ICS kaydı hesap kapsamlı sessionStorage anahtarı kullanmıyor.'); await page.getByRole('button', { name: 'Keşfet' }).click(); await page.getByRole('button', { name: 'İhale Takvimi' }).click(); evidence.checks.icsPersistsAfterTabChange = await page.getByText('1 harici etkinlik içe aktarıldı.', { exact: true }).count() === 1;