diff --git a/docs/quality/qa-preparation-navigation.json b/docs/quality/qa-preparation-navigation.json new file mode 100644 index 0000000..276664d --- /dev/null +++ b/docs/quality/qa-preparation-navigation.json @@ -0,0 +1,18 @@ +{ + "date": "2026-09-06", + "environment": "localhost:5182 synthetic fixture, real browser UI", + "checks": { + "firmFocus": true, + "completedFieldRemoved": true, + "priceFocus": true, + "validPriceClearsError": true, + "decimalTotal": true, + "checksFocus": true, + "incompleteOutputDisabled": true + }, + "limits": [ + "No real tender submission or official document acceptance", + "Only synthetic Giresun preparation form tested", + "No actual PDF/Excel download tested in this run" + ] +} diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 9d735cd..4326ea5 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -839,3 +839,10 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö - Ayrı kurtarma sürecinde commit öncesi kayıt aynı UUID/byte-identical payload ile yeniden çağrıldı; sonradan değişen kaynak B e-postayı değiştirmedi. Commit sonrası kayıtta sağlayıcı yeniden çağrılmadı. Görülmüş ihale listesi, provider id ve SQLite integrity_check doğrulandı. - Yerel deterministik sağlayıcı simülatöründe iki senaryoda da tek teslim; commit öncesi iki istek, sonrası tek istek gözlendi. Bu gerçek Resend tesliminin kanıtı değildir. Lease saati testte 121 saniye ilerletilir; gerçek zaman bekleme yapılmaz. - 191 otomatik test geçti. Üretim kodu değişmediği için önceki derleme tekrarlanmadı. Dış teslim, platform volume kurtarma ve Telegram kalıcı kuyruk kabulü açık. + +### D03 — Eksik bilgiden düzeltilecek alana geçiş — 6 Eylül 2026 +- Teklif dosyasına çıktı öncesi eksik listesi eklendi. Firma, kaynak, teklif türü/geçerliliği, kısım, fiyat ve belge/uygunluk maddeleri ilgili kontrol alanına kaydırır ve klavye odağını taşır. Doldurulan eksikler listeden kalkar; eksik taslak kaydı mümkün, mevcut çıktı engelleri korunur. +- Her fiyat alanının kalem numarası/adı içeren erişilebilir adı, hata açıklaması ve aria-invalid durumu var. Kaynak kalem eksikleri ile fiyat hataları satırda ayrılır. Belge eksikleri belge/ürün kontrol bölümüne gider; her alt belge alanına ayrı odaklama bu değişikliğin kapsamı değildir. +- Kaşe üretmeyen dosya hazırlama panelinin eski PDF & Kaşe adı Teklif Dosyası Hazırla olarak düzeltildi; dosya bağlantısı PDF / Excel çıktılarını belirtir. +- Yerel 5182 sentetik Giresun fixture tarayıcısında 7 kontrol geçti: firma odağı, tamamlanan alanın listeden kalkması, fiyat odağı/erişilebilir hata bağlantısı, fiyat düzeltmesinin hatayı kaldırması, 125,50 × 2 = 251,00 TL, belge bölümüne odak ve eksik dosyada çıktı engeli. Kanıt: qa-preparation-navigation.json. +- Derleme geçti. Hesaplama veya sunucu kodu değişmediğinden 191 testlik önceki otomatik paket tekrar çalıştırılmadı. Gerçek çok sayfalı PDF, resmi beş dosya ve planın diğer açık kabulleri tamamlanmış sayılmaz. diff --git a/src/components/BidPackageBuilder.jsx b/src/components/BidPackageBuilder.jsx index 17a0366..5d8c383 100644 --- a/src/components/BidPackageBuilder.jsx +++ b/src/components/BidPackageBuilder.jsx @@ -5,7 +5,7 @@ import { createBidWorkbook, downloadBidWorkbook } from '../services/bidWorkbook' import { getToken } from '../services/auth'; import { listDocuments, downloadDocument, validateDocumentTextReview } from '../services/account'; import { tenderDateKey } from '../services/tenderCalendar'; -import React, { useState, useMemo, useEffect, useRef } from 'react'; +import React, { useState, useMemo, useEffect, useRef, useId } from 'react'; import { bidRowTotal, validateBidRows, roundMoney, parseBidPrice } from '../services/bidValidation'; import { sourceRows, bidSourceSnapshot, restoreBidPrices, bidSourceOptions } from '../services/bidSource'; import { rowLot, selectBidLots } from '../services/bidLots'; @@ -58,6 +58,15 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => { .catch(error=>{if(!cancelled)setVault({loading:false,error:error.message,documents:[]});}); return ()=>{cancelled=true;}; },[authUser?.email,vaultRefresh]); + const fieldPrefix = useId(); + const preparationTargets = useRef({}); + const targetRef = key => node => { preparationTargets.current[key] = node; }; + const goToMissing = key => { + const target = preparationTargets.current[key]; + if (!target) return; + target.scrollIntoView({block:'center'}); + target.focus({preventScroll:true}); + }; const [dirty, setDirty] = useState(false); const [acik, setAcik] = useState(true); const [hata, setHata] = useState(null); @@ -136,6 +145,22 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => { const analizEksik = effectiveResults.sourceReview?.status === 'partial'; const hazir = !deadlineProblem && eksikler.length === 0 && toplam > 0 && !kalemEksik && !turCeliskisi && !sureEksik && !kaynakKontrolGerekli && kontrolEksikleri.length === 0 && lotSelection.errors.length === 0 && !analizEksik && !sourceOptions.needsSelection; + const missingActions = [ + ...(deadlineProblem ? [{target:'source',text:deadlineProblem}] : []), + ...(kaynakKontrolGerekli ? [{target:'source',text:'Resmi dokümanla kaynak karşılaştırmasını tamamlayın.'}] : []), + ...(analizEksik ? [{target:'analysis',text:'Kısmi analizi tam ve doğrulanabilir kaynakla tamamlayın.'}] : []), + ...(sourceOptions.needsSelection ? [{target:'itemSource',text:'Resmi cetvele uygun kalem kaynağını seçin.'}] : []), + ...eksikler.map(a=>({target:`firm:${a.k}`,text:`${a.l} bilgisini doldurun.`})), + ...(turCeliskisi ? [{target:'bidType',text:'Teklif türünü kaynak dokümana göre doğrulayın.'}] : []), + ...(sureEksik ? [{target:'validity',text:'Teklif geçerlilik süresini doğrulayın.'}] : []), + ...lotSelection.errors.map(text=>({target:'lots',text})), + ...(teklifTuru === 'birim' ? (satirlar.length ? satirlar.flatMap((row,i)=> + validateBidRows([row]).length ? [{target:`price:${row.priceKey}`,text:`${i+1}. kalem (${row.name || 'Adı eksik'}): ad, miktar, birim ve fiyatı kontrol edin.`}] : []) + : [{target:'pricing',text:'Resmi teklif cetvelindeki kalemleri yükleyin veya kısım seçimini tamamlayın.'}]) : []), + ...(teklifTuru === 'goturu' && !(toplam > 0) ? [{target:'lumpSum',text:'Geçerli, sıfırdan büyük toplam teklif bedeli girin.'}] : []), + ...kontrolEksikleri.map(text=>({target:'checks',text})), + ]; + const formVerisi = () => ({ ...firma, teklifTuru, @@ -221,7 +246,17 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => { {dirty ? 'Kaydedilmemiş değişiklikler var' : storedDraft.firma ? 'Sunucuda kayıtlı taslak' : 'Henüz kaydedilmedi'} -
İlgili alana gitmek için eksikliğe tıklayın. Taslağı eksikleriyle kaydedebilirsiniz.
+{reviewedSource ? 'İlan, kalem veya belge bilgisi değişti. Önceki kontrol bu sürüm için geçerli değil.' : 'Kalemler ve teklif koşulları resmi dokümanla henüz karşılaştırılmadı.'} {' '}Kaynağı kontrol etmeden teklif çıktısı oluşturulamaz. @@ -248,7 +283,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
İhale havuzundaki liste ile analiz listesi aynı değil. Resmi cetvelle karşılaştırıp kullanılacak listeyi seçin. Seçim uygunluk onayı değildir; ardından kalem, fiyat ve belge kontrollerini tamamlayın.