fix: preserve bid drafts and align tender source data
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { bidRowTotal, validateBidRows, roundMoney } from '../services/bidValidation';
|
||||
import { useApp } from '../context/AppContext';
|
||||
import {
|
||||
FolderCheck, ChevronDown, ChevronUp, FileSignature, Table2, ListChecks,
|
||||
@@ -34,35 +35,38 @@ const FIRMA_ANAHTAR = 'ihaleist_firma_bilgi';
|
||||
const bosFirma = () => Object.fromEntries(ALANLAR.map((a) => [a.k, '']));
|
||||
|
||||
export const BidPackageBuilder = ({ results, tender }) => {
|
||||
const { userProfile } = useApp();
|
||||
const { userProfile, mutateTender, savingCount, authUser } = useApp();
|
||||
const storedDraft = tender?.bidDraft || {};
|
||||
const [dirty, setDirty] = useState(false);
|
||||
const [acik, setAcik] = useState(true);
|
||||
const [hata, setHata] = useState(null);
|
||||
|
||||
const [firma, setFirma] = useState(() => {
|
||||
let kayitli = {};
|
||||
try { kayitli = JSON.parse(localStorage.getItem(FIRMA_ANAHTAR) || '{}'); } catch { /* yoksay */ }
|
||||
try { kayitli = JSON.parse(localStorage.getItem(FIRMA_ANAHTAR + ':' + authUser?.email) || '{}'); } catch { /* yoksay */ }
|
||||
return {
|
||||
...bosFirma(),
|
||||
firmaUnvan: userProfile?.company || '',
|
||||
...kayitli,
|
||||
...(storedDraft.firma || {}),
|
||||
};
|
||||
});
|
||||
|
||||
const [teklifTuru, setTeklifTuru] = useState('birim');
|
||||
const [teklifTuru, setTeklifTuru] = useState(storedDraft.teklifTuru || 'birim');
|
||||
const kaynakGecerlilik = /(?:geçerlilik[^.]{0,100}?|ihale tarihinden itibaren\s*)(\d+)\s*(?:takvim\s*)?gün/i.exec(tender?.aciklama || '');
|
||||
const [gecerlilikGun, setGecerlilikGun] = useState(kaynakGecerlilik?.[1] || '');
|
||||
const [kisim, setKisim] = useState('');
|
||||
const [goturuBedel, setGoturuBedel] = useState('');
|
||||
const [gecerlilikGun, setGecerlilikGun] = useState(storedDraft.gecerlilikGun || kaynakGecerlilik?.[1] || '');
|
||||
const [kisim, setKisim] = useState(storedDraft.kisim || '');
|
||||
const [goturuBedel, setGoturuBedel] = useState(storedDraft.goturuBedel || '');
|
||||
|
||||
const kalemler = tender?.items?.length
|
||||
? tender.items.map(k => ({name:k.ad,quantity:`${k.miktar} ${k.birim || ''}`,keySpecs:[]}))
|
||||
: (Array.isArray(results?.itemsAndSpecs) ? results.itemsAndSpecs : []);
|
||||
const [fiyatlar, setFiyatlar] = useState({});
|
||||
const [fiyatlar, setFiyatlar] = useState(storedDraft.fiyatlar || {});
|
||||
|
||||
const alanYaz = (k, v) => {
|
||||
const yeni = { ...firma, [k]: v };
|
||||
setFirma(yeni);
|
||||
try { localStorage.setItem(FIRMA_ANAHTAR, JSON.stringify(yeni)); } catch { /* kota */ }
|
||||
try { localStorage.setItem(FIRMA_ANAHTAR + ':' + authUser?.email, JSON.stringify(yeni)); } catch { /* kota */ }
|
||||
};
|
||||
|
||||
const satirlar = useMemo(
|
||||
@@ -70,21 +74,17 @@ export const BidPackageBuilder = ({ results, tender }) => {
|
||||
[kalemler, fiyatlar],
|
||||
);
|
||||
|
||||
const kalemToplami = useMemo(
|
||||
() => satirlar.reduce((t, r) => {
|
||||
const { miktar } = miktarAyristir(r.quantity);
|
||||
return t + (miktar ?? 0) * (Number(r.birimFiyat) || 0);
|
||||
}, 0),
|
||||
[satirlar],
|
||||
);
|
||||
const kalemToplami = useMemo(() => roundMoney(satirlar.reduce((t,r) => t + (bidRowTotal(r) ?? 0),0)), [satirlar]);
|
||||
const saveDraft = async () => {
|
||||
const saved = await mutateTender(tender.id, {bidDraft:{firma,teklifTuru,gecerlilikGun,kisim,goturuBedel,fiyatlar}});
|
||||
if (saved) { setDirty(false); setHata(null); }
|
||||
else setHata('Taslak kaydedilemedi. Tekrar deneyin.');
|
||||
};
|
||||
|
||||
const toplam = teklifTuru === 'goturu' ? (Number(goturuBedel) || 0) : kalemToplami;
|
||||
|
||||
const eksikler = ALANLAR.filter((a) => a.zorunlu && !String(firma[a.k] || '').trim());
|
||||
const kalemEksik = teklifTuru === 'birim' && (!satirlar.length || satirlar.some(r => {
|
||||
const {miktar,birim} = miktarAyristir(r.quantity);
|
||||
return !r.name || !(miktar > 0) || !birim || !(Number(r.birimFiyat) > 0);
|
||||
}));
|
||||
const kalemEksik = teklifTuru === 'birim' && validateBidRows(satirlar).length > 0;
|
||||
const turCeliskisi = teklifTuru === 'goturu' && /birim fiyat/i.test(tender?.aciklama || '');
|
||||
const sureEksik = !(Number(gecerlilikGun) > 0) || (kaynakGecerlilik && Number(gecerlilikGun) < Number(kaynakGecerlilik[1]));
|
||||
const hazir = eksikler.length === 0 && toplam > 0 && !kalemEksik && !turCeliskisi && !sureEksik;
|
||||
@@ -135,7 +135,12 @@ export const BidPackageBuilder = ({ results, tender }) => {
|
||||
</button>
|
||||
|
||||
{acik && (
|
||||
<div className="border-t border-emerald-200 p-5 space-y-5">
|
||||
<div className="border-t border-emerald-200 p-5 space-y-5" onChange={() => setDirty(true)}>
|
||||
<div className="flex items-center gap-3">
|
||||
<button type="button" onClick={saveDraft} disabled={savingCount > 0}
|
||||
className="px-4 py-2 rounded-lg bg-blue-700 text-white text-xs disabled:opacity-50">Hazırlık taslağını kaydet</button>
|
||||
<span role="status" className="text-xs">{dirty ? 'Kaydedilmemiş değişiklikler var' : storedDraft.firma ? 'Sunucuda kayıtlı taslak' : 'Henüz kaydedilmedi'}</span>
|
||||
</div>
|
||||
|
||||
{/* Sizden beklenen bilgiler */}
|
||||
{eksikler.length > 0 && (
|
||||
@@ -238,7 +243,7 @@ export const BidPackageBuilder = ({ results, tender }) => {
|
||||
<tbody>
|
||||
{satirlar.map((r, i) => {
|
||||
const { miktar, birim } = miktarAyristir(r.quantity);
|
||||
const tutar = (miktar ?? 0) * (Number(r.birimFiyat) || 0);
|
||||
const tutar = bidRowTotal(r);
|
||||
return (
|
||||
<tr key={i} className="border-b border-slate-100">
|
||||
<td className="py-1.5 pr-2 text-slate-800">{r.name || '—'}</td>
|
||||
|
||||
Reference in New Issue
Block a user