Persist general specification analysis and expose saved results

This commit is contained in:
2026-09-06 12:44:37 +03:00
parent ca0ed73242
commit bfef768974
11 changed files with 122 additions and 42 deletions
+20 -14
View File
@@ -159,6 +159,7 @@ export const AppProvider = ({ children }) => {
// Giriş yapınca kullanıcı verisini SUNUCUDAN yükle (localStorage'ı ezer)
useEffect(() => {
aiRunRef.current=null;setAiAnalyzing(false);
setVeriSenkron(false); setPlan(null); setSaveError(null);
setFavoriteKurumlar([]); setTeamMembers([]); setSavedFilters([]); setCompetitorWatchlist([]);
setReminderSettings(defaultReminder()); setUserProfile(defaultProfile(authUser));
@@ -336,6 +337,7 @@ export const AppProvider = ({ children }) => {
// Analiz modal dışından (ör. doküman panelindeki derin analiz düğmesi) da
// tetiklenebiliyor; modalın beklerken boş görünmemesi için ortak sinyal.
const [aiAnalyzing, setAiAnalyzing] = useState(false);
const aiRunRef=useRef(null);
// Enterprise Module States
// Rakip izleme listesi — kullanıcı gerçek kazanan firma analizinden ekler (hesaba özel sunucu kaydı)
@@ -441,35 +443,38 @@ export const AppProvider = ({ children }) => {
const getModelById = (id) => AI_MODEL_CATALOG.find(m => m.id === id);
const activeAiModel = getModelById(aiModelConfig.chain[0]) || AI_MODEL_CATALOG[0];
// Gerçek AI analizi — backend /api/ai/analyze üzerinden, admin panelde
// Gerçek AI analizi — kalıcı sunucu işi üzerinden, admin panelde
// seçilen model zinciri (fallback) ile.
// mode='derin' → ince detaylar, belge kontrol listesi ve yol haritası da üretilir
const analyzeSpecPdfWithGemini = async ({ text, fileName, tender, mode, extraction, reviewedSource } = {}) => {
const session = getToken();
if(aiRunRef.current?.session===session)throw new Error('Bir analiz zaten sürüyor. Kayıtlı işlerden durumunu kontrol edin.');
const run={session};aiRunRef.current=run;
setAiAnalyzing(true);
setAiAnalysisResults(null);
try {
const { analyzeSpecText } = await import('../services/ai');
const data = await analyzeSpecText({ text, fileName, tender, chain: aiModelConfig.chain, mode, extraction, reviewedSource });
const result = {
...data.result,
tenderKey: tender?.ikn || tender?.id || null,
mode: data.mode || (mode === 'derin' ? 'derin' : 'standart'),
fileName,
model: `${data.model.label} (${data.model.provider})`,
modelId: data.model.model,
fallbackChain: aiModelConfig.chain.map((id) => getModelById(id)?.model).filter(Boolean),
attempts: data.attempts,
analyzedAt: new Date(data.analyzedAt).toLocaleString('tr-TR'),
};
if(getToken() !== session) throw new Error('Oturum değişti; analiz sonucu bu hesaba aktarılmadı.');
const {analysisView}=await import('../services/ai');
const result=analysisView(data,{tender,mode,fileName});
if(getToken() !== session || aiRunRef.current!==run) throw new Error('Oturum değişti; analiz sonucu bu hesaba aktarılmadı.');
setAiAnalysisResults(result);
return result;
} finally {
setAiAnalyzing(false);
if(aiRunRef.current===run){aiRunRef.current=null;setAiAnalyzing(false);}
}
};
const openSavedAnalysis = async job => {
const session=getToken();
if(job?.operation!=='analiz'||job.state!=='completed')throw new Error('Tamamlanmış analiz seçin.');
const {analysisView}=await import('../services/ai');
const result=analysisView(job.result,job.input);
if(getToken()!==session)throw new Error('Oturum değişti.');
setAiAnalysisResults(result);
return result;
};
const [userProfile, setUserProfile] = useState(() => defaultProfile(getStoredUser()));
useEffect(() => {
const veri = {
@@ -777,6 +782,7 @@ export const AppProvider = ({ children }) => {
setUrunAramaBaslangic,
setIsAiSpecAnalyzerOpen,
analyzeSpecPdfWithGemini,
openSavedAnalysis,
aiAnalysisResults,
aiAnalyzing,
competitorWatchlist,