Files
ihaleist/src/components/PreparationChecks.jsx
T

75 lines
7.1 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, {useState} from 'react';
import {HEALTH_CHECKS, documentKey, documentRevision, documentScopeProblem} from '../services/preparationChecks';
const fieldClass = 'w-full border border-slate-300 rounded-lg p-2 text-xs bg-white';
const Status = ({value, onChange, optional = true}) => <select aria-label="Kontrol durumu" className={fieldClass} value={value || 'pending'} onChange={e => onChange(e.target.value)}>
<option value="pending">Kontrol gerekli</option><option value="verified">Doğrulandı</option>
{optional && <option value="notApplicable">Uygulanmıyor — gerekçe gerekli</option>}
</select>;
export function PreparationChecks({rows, documents, manualDocuments, setManualDocuments, checks, setChecks, vault, refreshVault, downloadFile}) {
const eligibleFiles = vault.documents.filter(file => !documentScopeProblem(file,vault.tenderIkn));
const [name,setName] = useState('');
const [clause,setClause] = useState('');
const updateDocument = (key, patch) => setChecks(old => ({...old,documents:{...old.documents,[key]:{...old.documents?.[key],...patch}}}));
const updateProduct = (key, patch) => setChecks(old => ({...old,products:{...old.products,[key]:{...old.products?.[key],...patch}}}));
return <section className="space-y-4" aria-label="Belge ve ürün uygunluğu">
<h5 className="font-extrabold text-sm">Belge ve ürün uygunluğu</h5>
<p className="text-xs text-slate-600">Her kontrol için belge adı/sürümü ve kaynak maddeyi yazın. ÜTS, numune ve servis koşulları her ihalede zorunlu değildir; uygulanmıyorsa gerekçesini belirtin. Bu kayıtlar kullanıcı kontrolüdür; resmi uygunluk onayı değildir.</p>
<div className="space-y-2">
<p className="text-xs">Firma belgelerini veya bu İKN için yüklenmiş ihale dokümanlarını ilgili gereksinime bağlayın. Başka ihale dokümanları seçilemez.</p>
<button type="button" onClick={refreshVault} disabled={vault.loading} className="text-xs font-bold text-blue-700">Belge deposunu yenile</button>
{vault.loading && <p role="status" className="text-xs">Belge deposu yükleniyor…</p>}
{vault.error && <p role="alert" className="text-xs text-red-700">Belge deposu yüklenemedi: {vault.error}</p>}
{documents.map(d => {
const key = documentKey(d), c = checks.documents?.[key] || {};
return <fieldset key={key} className="border rounded-xl p-3 space-y-2">
<legend className="text-xs font-bold">{d.document} {d.mandatory === false ? '(isteğe bağlı)' : '(zorunlu)'}</legend>
<p className="text-xs text-slate-600">{d.note}</p>
{manualDocuments.some(item => documentKey(item) === key) && <button type="button" className="text-xs text-red-700"
onClick={() => setManualDocuments(manualDocuments.filter(item => documentKey(item) !== key))}>Elle eklenen gereksinimi kaldır</button>}
<label className="block text-xs">Bağlı dosya
<select className={fieldClass} value={c.documentId || ''} disabled={vault.loading || Boolean(vault.error)} onChange={e=>{
const file=eligibleFiles.find(f=>f.id===e.target.value);
updateDocument(key,{documentId:file?.id || null,documentRevision:file ? documentRevision(file) : null,status:'pending'});
}}>
<option value="">Dosya seçin</option>
{c.documentId && !eligibleFiles.some(f=>f.id===c.documentId) && <option value={c.documentId}>Önceki dosya bulunamadı veya bu ihale için kullanılamıyor</option>}
{eligibleFiles.map(file=><option key={file.id} value={file.id}>{file.ad} · {file.son_kullanma || 'Süre bilgisi yok'}{file.erisilebilir === false ? ' · erişilemiyor' : ''}</option>)}
</select>
</label>
{vault.documents.some(f=>f.id===c.documentId) && <button type="button" className="text-xs text-blue-700" onClick={()=>downloadFile(vault.documents.find(f=>f.id===c.documentId))}>Bağlı dosyayı indir</button>}
<Status value={c.status} optional={d.mandatory === false} onChange={status => updateDocument(key,{status})}/>
<label className="block text-xs">Belge adı/sürümü, geçerlilik kontrolü ve kaynak madde
<input className={fieldClass} value={c.evidence || ''} onChange={e => updateDocument(key,{evidence:e.target.value})}/>
</label>
</fieldset>;
})}
<div className="border border-dashed rounded-xl p-3 space-y-2">
<p className="text-xs font-semibold">Resmi dokümandan eksik belge gereksinimi ekle</p>
<label className="block text-xs">Belge adı<input className={fieldClass} value={name} onChange={e=>setName(e.target.value)}/></label>
<label className="block text-xs">Kaynak doküman ve madde<input className={fieldClass} value={clause} onChange={e=>setClause(e.target.value)}/></label>
<button type="button" className="text-xs font-bold text-blue-700 disabled:opacity-40" disabled={!name.trim() || !clause.trim()}
onClick={()=>{const d={document:name.trim(),note:clause.trim(),mandatory:true};if(!documents.some(x=>documentKey(x)===documentKey(d)))setManualDocuments([...manualDocuments,d]);setName('');setClause('');}}>Belge gereksinimi ekle</button>
</div>
</div>
{rows.map(row => {
const c=checks.products?.[row.priceKey] || {};
return <details key={row.priceKey} className="border rounded-xl p-3">
<summary className="cursor-pointer text-xs font-bold">{row.name} — ürün/hizmet ve yükümlülük kontrolü</summary>
<div className="space-y-2 mt-3">
<label className="block text-xs">Teklif edilen marka/model veya hizmet kapsamı<input className={fieldClass} value={c.product || ''} onChange={e=>updateProduct(row.priceKey,{product:e.target.value})}/></label>
<label className="block text-xs">Teknik doküman/madde ve uygunluk dayanağı<input className={fieldClass} value={c.source || ''} onChange={e=>updateProduct(row.priceKey,{source:e.target.value})}/></label>
<label className="block text-xs">Ürün/hizmet uygunluğu<select className={fieldClass} value={c.status || 'pending'} onChange={e=>updateProduct(row.priceKey,{status:e.target.value})}>
<option value="pending">Kontrol gerekli</option><option value="suitable">Uygun</option><option value="unsuitable">Uygun değil</option>
</select></label>
{HEALTH_CHECKS.map(([key,label])=><fieldset key={key} className="bg-slate-50 rounded-lg p-2 space-y-1">
<legend className="text-xs font-semibold">{label}</legend>
<Status value={c[key]?.status} onChange={status=>updateProduct(row.priceKey,{[key]:{...c[key],status}})}/>
<label className="block text-xs">Kaynak, karşılanan koşul veya uygulanmama gerekçesi<input className={fieldClass} value={c[key]?.evidence || ''} onChange={e=>updateProduct(row.priceKey,{[key]:{...c[key],evidence:e.target.value}})}/></label>
</fieldset>)}
<label className="block text-xs">Sorumlu<input className={fieldClass} value={c.owner || ''} onChange={e=>updateProduct(row.priceKey,{owner:e.target.value})}/></label>
</div>
</details>;
})}
</section>;
}