diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 681620f..e716ab9 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -40,7 +40,14 @@ Tarayıcı kanıtı: [Yenileme sonrası beş kart](evidence/qa-kanban-after-relo - Kalem bazında ürün/model veya hizmet kapsamı, teknik uygunluk kaynağı, sorumlu, ÜTS, numune/demonstrasyon, servis/kurulum ve teslimat kontrolleri bulunur. Uygulanmıyor seçimi gerekçe ister; uygun olmayan ürün çıktıyı engeller. - Eksik kontrollerin listesi fiyatlandırma ekranında görünür. Bu alanlar kullanıcı beyanı/kontrol kaydıdır; belge deposundaki gerçek dosyaya otomatik bağlantı ve resmi yeterlik doğrulaması henüz yoktur. - Kontroller, elle eklenen belgeler ve analizden gelen kalem/belge listesi taslakla saklanır. HTTP testi bu alanların hesap içinde yazılıp geri okunmasını doğrular. Üç kural testi eksik kaynak, uygun olmayan ürün, zorunlu belgenin atlanması ve değişmiş gereksinimleri kapsar. -- Tarayıcıda tüm bu alanları doldurma, yenileme ve çok kalemli kullanılabilirlik kabulü bekliyor. AI raporundaki eski yerel checklist işaretleri henüz bu kalıcı kontrol modeline bağlanmadı. D06/D07 tamamlanmış sayılmaz. +- Tarayıcıda tüm bu alanları doldurma, yenileme ve çok kalemli kullanılabilirlik kabulü bekliyor. AI raporundaki eski yerel belge işaretleri kaldırıldı; rapor kalıcı hazırlık kaydını özetler ve kontrol formuna yönlendirir. D06/D07 tamamlanmış sayılmaz. + +## Ekranlar arası kayıt tutarlılığı + +- Derin rapor belge listesi artık geçici tamamlandı işaretleri tutmaz; kayıtlı kullanıcı kontrollerini gösterir ve kaynaklı hazırlık formuna geçiş sağlar. +- Yol haritası tamamlanma durumu ihale checklist kaydında sunucu onayıyla saklanır. Eylem veya kaynak değişince eski işaret tamamlanmış sayılmaz. Kaydetme hatası görünürdür. +- Derin rapor ve hazırlık formu, açılışta seçilmiş eski nesne yerine ihale havuzundaki güncel kullanıcı kaydını okur. Böylece AI penceresinden tekrar açılan formda yeni kaydedilmiş taslağa erişilir. +- HTTP testinde yol haritası kaynak/durum alanlarının yazma-okuma kontrolü eklendi. Arayüzde iki pencere arasında geçiş ve yeniden giriş senaryosu hâlâ ayrıca doğrulanmalıdır. ## Doğrulama diff --git a/src/components/BidPackageBuilder.jsx b/src/components/BidPackageBuilder.jsx index 794a774..a0f8a50 100644 --- a/src/components/BidPackageBuilder.jsx +++ b/src/components/BidPackageBuilder.jsx @@ -37,8 +37,9 @@ const FIRMA_ANAHTAR = 'ihaleist_firma_bilgi'; const bosFirma = () => Object.fromEntries(ALANLAR.map((a) => [a.k, ''])); -export const BidPackageBuilder = ({ results, tender }) => { - const { userProfile, mutateTender, savingCount, authUser } = useApp(); +export const BidPackageBuilder = ({ results, tender: selectedTender }) => { + const { userProfile, mutateTender, savingCount, authUser, tenders } = useApp(); + const tender = {...selectedTender, ...tenders.find(t => t.id === selectedTender?.id)}; const storedDraft = tender?.bidDraft || {}; const [dirty, setDirty] = useState(false); const [acik, setAcik] = useState(true); diff --git a/src/components/DeepAnalysisReport.jsx b/src/components/DeepAnalysisReport.jsx index 09f32fc..60ed7de 100644 --- a/src/components/DeepAnalysisReport.jsx +++ b/src/components/DeepAnalysisReport.jsx @@ -1,8 +1,11 @@ -import React, { useState } from 'react'; +import React, { useRef } from 'react'; +import { useApp } from '../context/AppContext'; +import { bidSourceSnapshot } from '../services/bidSource'; +import { documentKey } from '../services/preparationChecks'; import { YeterlilikRozeti } from './YeterlilikRozeti'; import { AlertOctagon, AlertTriangle, Eye, CalendarClock, ClipboardList, Route, - HelpCircle, Coins, Target, CheckSquare, Square, Package, + HelpCircle, Coins, Target, Package, } from 'lucide-react'; import { BidPackageBuilder } from './BidPackageBuilder'; @@ -45,8 +48,16 @@ const Baslik = ({ icon: Icon, renk, children, not }) => ( ); -export const DeepAnalysisReport = ({ results, tender }) => { - const [isaretli, setIsaretli] = useState({}); +export const DeepAnalysisReport = ({ results, tender: selectedTender }) => { + const {tenders, mutateTender, savingCount, saveError} = useApp(); + const preparationRef = useRef(null); + const tender = {...selectedTender, ...tenders.find(t => t.id === selectedTender?.id)}; + const sourceSnapshot = bidSourceSnapshot(tender, results); + const savedSteps = Array.isArray(tender.checklist) ? tender.checklist : []; + const stepDone = key => savedSteps.some(s => s.key === key && s.sourceSnapshot === sourceSnapshot && s.done); + const toggleStep = key => mutateTender(tender.id, {checklist:[ + ...savedSteps.filter(s => s.key !== key), {key, sourceSnapshot, done:!stepDone(key)}, + ]}); if (!results) return null; const { @@ -59,7 +70,6 @@ export const DeepAnalysisReport = ({ results, tender }) => { dolu(roadmap) || dolu(questionsForAdministration) || dolu(itemsAndSpecs); if (!varMi) return null; - const degistir = (k) => setIsaretli((o) => ({ ...o, [k]: !o[k] })); return (
@@ -181,41 +191,26 @@ export const DeepAnalysisReport = ({ results, tender }) => { )}
- {/* Belge kontrol listesi */} + {/* Source requirements summarize the single saved preparation checklist. */} {dolu(documentChecklist) && (
- - Belge Kontrol Listesi - + Belge Gereksinimleri +

Kontrolleri aşağıdaki hazırlık formunda kaynaklarıyla kaydedin.

    {documentChecklist.map((b, i) => { - const k = `b${i}`; - const secili = !!isaretli[k]; - return ( -
  • - -
  • - ); + const check = tender.bidDraft?.checks?.documents?.[documentKey(b)]; + return
  • + {b.document || '—'} +

    {b.note}

    + +

    {check?.status === 'verified' && check?.evidence?.trim() + ? 'Kayıtlı kullanıcı kontrolü var; güncel kaynak kontrolünü hazırlık formundan doğrulayın.' + : 'Belge kontrolü bekliyor.'}

    +
  • ; })}
+
)} @@ -262,15 +257,17 @@ export const DeepAnalysisReport = ({ results, tender }) => {
    {roadmap.map((a, i) => { - const k = `y${i}`; - const secili = !!isaretli[k]; + const k = JSON.stringify(a); + const secili = stepDone(k); return (
  1. ); diff --git a/tests/http-state.test.mjs b/tests/http-state.test.mjs index cd3a66c..12d85c0 100644 --- a/tests/http-state.test.mjs +++ b/tests/http-state.test.mjs @@ -27,9 +27,10 @@ after(async()=>{if(child){child.kill();await new Promise(r=>child.once('exit',r) test('HTTP preparation requires auth, persists draft and rejects stale revisions',async()=>{ assert.equal((await request('/api/me/tender-state','GET',null,null)).status,401); const draft={firma:{firmaUnvan:'QA Laboratuvar'},priceSchema:1,pricesBySource:{kit1:'12.50'},gecerlilikGun:'120',checks:{products:{kit1:{product:'Test kit',status:'suitable',source:'Teknik 3.2'}},documents:{doc1:{status:'verified',evidence:'Test v1'}}},manualDocuments:[{document:'Yetki',note:'7.1',mandatory:true}],analysisSource:{itemsAndSpecs:[{name:'Kit',quantity:'2 adet'}],documentChecklist:[]}}; - const data={key:'2026/1582991',patch:{takipte:true,gorev:{kontrolListesi:[]},bidDraft:draft},snapshot:{id:'ilan-qa',ikn:'2026/1582991'},version:0}; + const data={key:'2026/1582991',patch:{takipte:true,gorev:{kontrolListesi:[]},bidDraft:draft,checklist:[{key:'collect-documents',sourceSnapshot:'source-v1',done:true}]},snapshot:{id:'ilan-qa',ikn:'2026/1582991'},version:0}; assert.equal((await request('/api/me/tender-state','PUT',data)).status,200); const read=await request('/api/me/tender-state');assert.deepEqual(read.data.records[data.key].data.bidDraft,draft); + assert.deepEqual(read.data.records[data.key].data.checklist,data.patch.checklist); assert.equal((await request('/api/me/tender-state','PUT',data)).status,409); assert.deepEqual((await request('/api/me/tender-state','GET',null,other)).data.records,{}); });