PageSourceSearch

https://frontier365.ai/assets/analysis-VSD4kTl_.js

js frontier365.ai collected 2026-10-05 14:31:00 UTC 70,748 bytes, 131 lines download raw bytes

1import{r as f,j as t}from"./react-vendor-QlMg5G0i.js";import{c as Ds,L as Ee}from"./router-vendor-CiP11j9Q.js";import{G as $s,d as Fs,e as Es,p as Ts,Q as Ut,U as Vt,L as Wt,H as Ps,a0 as Ms,a1 as _s,a5 as qt,T as Ht,i as Jt,j as Yt,X as Kt,a3 as Bs,s as bt,R as kt,C as Ls,kZ as zs,I as Qt,w as Gs}from"./app-entry-C64IZ4Vk.js";import{j as Os}from"./fortifySecureScoreTiers-DlgAw-ZI.js";import{f as Te}from"./filter-ai-text-Aboc_gem.js";import{f as Xt,e as Zt}from"./report-events-DNsMVQm2.js";import{c as Us}from"./pending-report-DDSb8Jpz.js";import{h as Vs,L as Ge,B as J,X as es,F as Oe,i as le,r as Ws}from"./index-D8uw7Gn1.js";import{S as qs,a as Hs,b as Js,c as Ys,d as Ks}from"./select-Db12z4QW.js";import{F as Qs}from"./file-viewer-C2gp5w2d.js";import{M as Xs}from"./markdown-message-DjRBZUdv.js";import{B as jt}from"./bot-5PDaxMrS.js";import{C as ts}from"./circle-check-D05XmDDB.js";import{T as Zs}from"./terminal-Cgynrm4f.js";import{P as St}from"./plus-DJEuodrN.js";import{F as Ue}from"./file-spreadsheet-CnXsWk7v.js";import"./index-WpFs0KKI.js";import"./helmet-vendor-BkSo4FTL.js";import"./query-vendor-7bKRow_J.js";import"./WhiteLabelContext-7faOb9VJ.js";import"./chevron-up-DNpMLRkb.js";import"./tabs-tOO1_yFg.js";import"./index-D-iX-9aC.js";import"./sanitize-D4KUcQGw.js";import"./purify.es-BPuvlvQ_.js";const er=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]],tr=Vs("square",er),Nt=2,Pe="license_analysis_session",sr=`Hi! I'm your M365 License Analyst. Here's how I work:
2
31. **Chat with me** — Ask any M365 licensing question and I'll respond right here in the chat
42. **Attach files** — Use the paperclip to attach CSV, Excel, PDF, or Word files for me to analyze
53. **Generate reports** — When you attach a CSV/Excel file, check **"Generate M365 Report"** to run a full analysis and create downloadable Word and Excel reports
6
7To get started, select or create a project, then ask a question or attach your license data.`;function ss(){return{id:"1",role:"assistant",content:sr,timestamp:new Date}}function rs(M,ie,te){if(!M||typeof M!="object"||Array.isArray(M))return!1;const F=M;if(F.version!==Nt||F.userId!==ie||F.accountId!==te||!Array.isArray(F.messages))return!1;const ce=V=>F[V]===void 0||F[V]===null||typeof F[V]=="string";return!ce("sessionId")||!ce("selectedCustomerId")||!ce("savedConversationId")||!ce("conversationCustomerId")||!ce("conversationClientId")||F.awaitingUserResponse!==void 0&&typeof F.awaitingUserResponse!="boolean"||F.savedAt!==void 0&&(typeof F.savedAt!="string"||!Number.isFinite(Date.parse(F.savedAt)))||F.completedReport!==void 0&&F.completedReport!==null&&(typeof F.completedReport!="object"||Array.isArray(F.completedReport)||typeof F.completedReport.reportId!="string"||typeof F.completedReport.customerName!="string"||!Array.isArray(F.completedReport.files)||!F.completedReport.files.every(_=>!!_&&typeof _=="object"&&!Array.isArray(_)&&typeof _.type=="string"&&typeof _.filename=="string"))?!1:F.messages.every(V=>{if(!V||typeof V!="object"||Array.isArray(V))return!1;const _=V;return typeof _.id=="string"&&(_.role==="user"||_.role==="assistant")&&t
7ypeof _.content=="string"&&typeof _.timestamp=="string"&&Number.isFinite(Date.parse(_.timestamp))})}async function rt(M,ie){let te=null;try{te=await M.clone().json()}catch{}if(M.status===429){if(te?.error==="daily_ai_token_cap_exceeded")return"This account has reached today's AI usage limit. It resets at midnight UTC (00:00) — please try again after the reset.";const F=M.headers.get("Retry-After")||M.headers.get("X-RateLimit-Reset");return`Rate limit reached. Please wait ${F?Math.max(1,Math.ceil(Number(F))):60} seconds before trying again.`}return te?.message||te?.error||ie}function rr(M){const ie=M.toLowerCase();return/upload|creating|preparing|starting/.test(ie)?"Preparing":/customer|lookup|source data|retriev|fetch/.test(ie)?"Reviewing data":/code|execut|analysis/.test(ie)?"Analyzing":/writing|processing|response/.test(ie)?"Writing response":"Working"}function Fr(){const{user:M,isLoading:ie}=$s(),{currentAccount:te,isLoading:F}=Fs(),{toast:ce}=Es(),{isReadOnly:V}=Ts(),[_,d]=f.useState([ss()]),[at,nt]=f.useState(""),[je,Ve]=f.useState(!1),[z,ne]=f.useState(null),[E,ge]=f.useState([]),[Me,ot]=f.useState(""),[Q,W]=f.useState(!1),[It,y]=f.useState(""),[ar,_e]=f.useState(null),[as,Be]=f.useState(0),[me,Rt]=f.useState([]),[m,de]=f.useState(""),[oe,Se]=f.useState(""),[We,Ne]=f.useState(!1),[qe,lt]=f.useState(!0),[q,Y]=f.useState(null),[ns,He]=f.useState(!1),[Je,X]=f.useState(null),[nr,H]=f.useState([]),[it,Ie]=f.useState(!1),[os,D]=f.useState([]),[Ct,At]=f.useState(!0),[Dt,ct]=f.useState([]),[re,Re]=f.useState(!1),[xe,dt]=f.useState(0),[ut,pt]=f.useState({projectFiles:[],sourceFiles:[],reportFiles:[]}),[ls,ft]=f.useState(!1),[$t,mt]=f.useState(null),[is,ht]=f.useState(null),Ye=f.useRef(null),we=f.useRef(null),Ce=f.useRef(null),Ke=f.useRef(null),Ae=f.useRef(Date.now()),ue=f.useRef(null),Qe=f.useRef(null),G=f.useRef(null),O=f.useRef(null),De=f.useRef(!1),ye=f.useRef(!1),Xe=f.useRef(!1),se=f.useRef(null),ve=!ie&&!F&&M?.id&&te?.id?`${M.id}:${te.id}`:null,Ft=f.useRef(ve);Ft.current=ve;const gt=f.useRef(ve),Ze=f.useRef(0),Et=(e,s)=>{const r=M?.id,n=te?.id;if(!(!r||!n))try{const o=localStorage.getItem(Pe);if(o)try{const a=JSON.parse(o);if(rs(a,r,n)){a.conversationClientId=e,a.conversationCustomerId=s,localStorage.setItem(Pe,JSON.stringify(a));return}}catch{}localStorage.setItem(Pe,JSON.stringify({version:Nt,userId:r,accountId:n,messages:[],conversationClientId:e,conversationCustomerId:s,savedAt:new Date().toISOString()}))}catch(o){console.error("Failed to persist clientId:",o)}},cs=async(e,s)=>{try{await navigator.clipboard.writeText(e),ht(s),setTimeout(()=>ht(null),2e3)}catch(r){console.error("Failed to copy:",r)}},Tt=()=>{const e=M?.email||void 0,s=M?.firstName&&M?.lastName?`${M.firstName} ${M.lastName}`:M?.email||void 0;return{userEmail:e,userName:s}},Le=e=>typeof e=="string"?e:Array.isArray(e)?e.filter(s=>s?.type==="text"&&typeof s?.text=="string").map(s=>s.text).join(""):e&&typeof e=="object"&&e.text?e.text:"",ds=e=>{const s=M?.id,r=te?.id;if(!(!s||!r))try{localStorage.setItem(Pe,JSON.stringify({...e,version:Nt,userId:s,accountId:r,messages:e.messages.map(n=>({...n,timestamp:n.timestamp.toISOString()})),savedAt:new Date().toISOString()}))}catch(n){console.error("Failed to save session:",n)}},us=()=>{const e=M?.id,s=te?.id;if(!e||!s)return!1;try{const r=localStorage.getItem(Pe);if(r){const n=JSON.parse(r);return rs(n,e,s)?(n.savedConversationId&&pe(n.savedConversationId),n.conversationCustomerId&&fe(n.conversationCustomerId),n.conversationClientId&&he(n.conversationClientId),n.selectedCustomerId&&de(n.selectedCustomerId),n.messages?.length>1?(d(n.messages.filter(o=>o.id!=="reconnect-info").map(o=>({...o,timestamp:new Date(o.timestamp),content:Le(o.content)}))),ne(n.sessionId),Y(n.completedReport),Ie(n.awaitingUserResponse||!1),!0):!!(n.conversationClientId||n.savedConversationId)):($e(),!1)}}catch(r){console.error("Failed to restore session:",r),$e()}return!1},$e=()=>{try{localStorage.removeItem(Pe)}catch(e){console.error("Failed to clear session:",e)}},[or,xt]=f.useState(!1),[be,pe]=f.useState(null),[Z,fe]=f.useState(null),[ke,he]=f.useState(null),ps=()=>{se.current?.abort(),se.current=null,G.current&&clearInterval(G.current),O.current&&clearInterval(O.current),B.current&&clearInterval(B.current),ae.current&&clearInterval(ae.current),K.current&&clearTimeout(K.current),G.current=null,O.current=null,B.current=null,ae.current=null,K.current=null,Ce.current=null,Ke.current=null,Ae.current=Date.now(),ue.current=null,Qe.current=null,De.current=!1,ye.current=!1,Xe.current=!1,Fe.current=0,ze.current=null,we.current&&(we.current.value=""),d([ss()]),nt(""),Ve(!1),ne(null),de(""),Se(""),Ne(!1),lt(!0),Y(null),He(!1),Ie(!1),xt(!1),pe(null),fe(null),he(null),ge([]),ot(""),ct([]),Rt([]),At(!0),Re(!1),dt(0),pt({projectFiles:[],sourceFiles:[],reportFiles:[]}),ft(!1),mt(null),X(null),W(!1),y(""),_e(null),Be(0),D([]),H([]),ht(null)},fs=async()=>
7{if(!(!m||_.length<=1)&&!(Z&&Z!==m)){if(!ke){console.error("handleSaveConversation: conversationClientId is missing - this should never happen");return}xt(!0);try{const e=_.find(r=>r.role==="user")?.content.slice(0,100)||"Untitled Conversation",s=_.map(r=>({role:r.role,content:r.content,timestamp:r.timestamp.toISOString(),attachments:r.attachments,userEmail:r.userEmail,userName:r.userName}));if(be){if(!(await fetch(`/api/conversations/${be}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:s}),credentials:"include"})).ok)throw new Error("Failed to update conversation")}else{const r=await fetch("/api/conversations",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({customerId:m,title:e,messages:s,clientId:ke}),credentials:"include"});if(!r.ok)throw new Error("Failed to save conversation");const n=await r.json();pe(n.conversation.id),fe(m);try{Ut(Vt.AiProjectCreated,{hasCustomer:!!m})}catch{}}}catch(e){console.error("Failed to save conversation:",e)}finally{xt(!1)}}},ms=Ds(),vt=new URLSearchParams(ms),et=vt.get("customerId"),tt=vt.get("conversationId"),st=vt.get("resumeSession"),hs=async(e,s=()=>!0)=>{try{const r=await fetch(`/api/conversations/${e}`,{credentials:"include"});if(!s())return!1;if(r.ok){const n=await r.json();if(!s())return!1;const o=n.conversation;if(o&&o.messages){if(d(o.messages.map(a=>({...a,id:a.id||Date.now().toString()+Math.random(),timestamp:new Date(a.timestamp),content:Le(a.content)}))),pe(e),o.customerId){fe(o.customerId),de(o.customerId);try{const a=await fetch(`/api/customers/${o.customerId}/reports`,{credentials:"include"});if(!s())return!1;if(a.ok){const p=await a.json();if(!s())return!1;if(p.reports&&p.reports.length>0){const l=p.reports[0],u=[];l.wordDocPath&&u.push({type:"word",filename:l.wordDocPath.split("/").pop()||"report.docx"}),l.excelPath&&u.push({type:"excel",filename:l.excelPath.split("/").pop()||"report.xlsx"}),u.length>0&&Y({reportId:l.id,customerName:l.clientName,files:u,message:""})}}}catch(a){console.error("Failed to load customer reports:",a)}}return!0}}}catch(r){console.error("Failed to load conversation:",r)}return!1},gs=async(e,s=()=>!0)=>{try{const r=await fetch(`/api/analysis-sessions/${e}`,{credentials:"include"});if(!s())return!1;if(r.ok){const n=await r.json();if(!s())return!1;const o=n.session;if(o){if(o.accountId){const a=await fetch(`/api/accounts/${o.accountId}/select`,{method:"POST",credentials:"include"});if(!s())return!1;a.ok||console.error("Failed to select account for session")}if(ne(o.id),o.customerId&&(de(o.customerId),fe(o.customerId)),o.messages&&o.messages.length>0){const a=o.messages.map((v,g)=>({id:v.id||`restored-${g}-${Date.now()}`,role:v.role,content:Le(v.content),timestamp:new Date(v.timestamp||Date.now()),isStreaming:!1})),p=o.customerName?` for **${o.customerName}**`:"";let l="";o.sourceFiles&&Array.isArray(o.sourceFiles)?l=o.sourceFiles.map(v=>v.originalFilename||v.filename).join(", "):o.csvFilename&&(l=o.csvFilename);const u=l?`
8
9📎 **Source data available:** ${l}`:"",c=o.reportId?`
10
11✅ **Reports loaded** — I can see your completed reports and answer questions about them. You can also ask me to make modifications.`:"",b=o.reportId&&o.customerId?`
12
13📁 [View Report Files](/files?customer=${o.customerId}) or preview: <span class="purple-badge">Reports Ready ▸</span>`:"",x={id:`continue-${Date.now()}`,role:"assistant",content:`📊 **Continuing Previous Analysis${p}**
14
15I've loaded your previous conversation and all project files.${u}${c}${b}
16
17Feel free to ask questions, request report changes, or upload new files.`,timestamp:new Date,isStreaming:!1};d([...a,x])}if(o.reportId){const a=await fetch(`/api/reports/${o.reportId}`,{credentials:"include"});if(!s())return!1;if(a.ok){const p=await a.json();if(!s())return!1;if(p.report){const l=p.report,u=[];l.wordDocPath&&u.push({type:"word",filename:l.wordDocPath.split("/").pop()||"report.docx"}),l.excelPath&&u.push({type:"excel",filename:l.excelPath.split("/").pop()||"report.xlsx"}),Y({reportId:l.id,customerName:l.clientName,files:u,message:""})}}}return mt(`Resuming project${o.customerName?` for ${o.customerName}`:""}`),setTimeout(()=>mt(null),6e3),!0}}}
17catch(r){console.error("Failed to load analysis session:",r)}return!1},xs=async(e=()=>!0)=>{try{const s=await fetch("/api/active-session",{credentials:"include"});if(!e())return!1;if(s.ok){const r=await s.json();if(!e())return!1;if(r.active){ne(r.sessionId),de(r.customerId||"");const n=r.status==="processing";if(n&&r.reportId)try{const a=await fetch(`/api/reports/${r.reportId}/events`,{credentials:"include"});if(!e())return!1;if(a.ok){const p=await a.json();if(!e())return!1;Fe.current=0;const l=`reconnected-${Date.now()}`,u={id:"reconnect-info",role:"assistant",content:`📊 **Reconnected to active analysis**
18
19**Project:** ${r.customerName}
20**Files:** ${r.fileName}`,timestamp:new Date,isStreaming:!1};d([u,{id:l,role:"assistant",content:"",timestamp:new Date,isStreaming:!0,codeBlocks:[],statusLog:[]}]),W(!0),_e(new Date(p.createdAt||Date.now()));const b=p.createdAt?new Date(p.createdAt).getTime():Date.now();return Ce.current=b,Ke.current="reconnecting",Be(Math.floor((Date.now()-b)/1e3)),X(l),p.events&&p.events.length>0?Pt(p.events,l):y("Analysis in progress... (reconnected)"),Mt(r.reportId,l),!0}}catch(a){console.error("Failed to fetch events, falling back to legacy polling:",a)}const o=r.messages?.map((a,p)=>({id:a.id||`restored-${p}-${Date.now()}`,role:a.role,content:a.role==="assistant"?Te(Le(a.content)):Le(a.content),timestamp:new Date(a.timestamp||Date.now()),isStreaming:!1}))||[];if(o.length>0){if(n){W(!0),y("Analysis in progress... (reconnected)");const a={id:Date.now().toString(),role:"assistant",content:`📊 **Reconnected to active analysis**
21
22**Project:** ${r.customerName}
23**Files:** ${r.fileName}
24
25The analysis is still processing. Please wait for it to complete.`,timestamp:new Date,isStreaming:!1};d([...o,a]),_t(r.reportId)}else if(d(o),r.status==="completed"){const a=await fetch(`/api/reports/${r.reportId}`,{credentials:"include"});if(!e())return!1;if(a.ok){const p=await a.json();if(!e())return!1;const l=p.report,u=[];l.wordDocPath&&u.push({type:"word",filename:l.wordDocPath.split("/").pop()||"report.docx"}),l.excelPath&&u.push({type:"excel",filename:l.excelPath.split("/").pop()||"report.xlsx"}),u.length>0&&Y({reportId:l.id,customerName:l.clientName,files:u,message:""})}}}else{const a={id:"1",role:"assistant",content:`Hi! I'm your M365 License Analyst. Here's how I work:
26
271. **Chat with me** — Ask any M365 licensing question and I'll respond right here in the chat
282. **Attach files** — Use the paperclip to attach CSV, Excel, PDF, or Word files for me to analyze
293. **Generate reports** — When you attach a CSV/Excel file, check **"Generate M365 Report"** to run a full analysis and create downloadable Word and Excel reports
30
31To get started, select or create a project, then ask a question or attach your license data.`,timestamp:new Date};if(n){W(!0),y("Analysis in progress... (reconnected)");const p={id:Date.now().toString(),role:"assistant",content:`📊 **Reconnected to active analysis**
32
33**Project:** ${r.customerName}
34**Files:** ${r.fileName}
35
36The analysis is still processing. Please wait for it to complete.`,timestamp:new Date,isStreaming:!1};d([a,p]),_t(r.reportId)}else d([a])}return!0}}}catch(s){console.error("Failed to check active session:",s)}return!1},Fe=f.useRef(0),B=f.useRef(null),Pt=(e,s)=>{let r="",n=[],o=[],a="",p="",l=null;for(const c of e){const b=c.data,x=c.eventType;switch(c.id>Fe.current&&(Fe.current=c.id),x){case"status":p=b.message,o=[...o,{message:b.message,timestamp:new Date(c.createdAt||Date.now())}],y(b.message),b.sessionId&&ne(b.sessionId);break;case"text":r+=b.text;break;case"text_start":r.length>0&&(r+=`
37
38`);break;case"stream_reset":r="",n=[],a="";break;case"code_start":a=b.id||`code_${Date.now()}`;const v={id:a,code:"",output:"",result:"",isExecuting:!1,isComplete:!1};n=[...n,v],y("Writing code...");break;case"code_delta":a&&b.code&&(n=n.map(g=>g.id===a?{...g,code:g.code+b.code}:g));break;case"code_complete":b.code&&a&&(n=n.map(g=>g.id===a?{...g,code:b.code,isExecuting:!0}:g),y("Running code..."));break;case"execution_output":b.output&&a&&(n=n.map(g=>g.id===a?{...g,output:g.output+b.output}:g),y("Running code..."));break;case"execution_result":b.result&&a&&(n=n.map(g=>g.id===a?{...g,result:b.result}:g));break;case"execution_end":a&&(n=n.map(g=>g.id===a?{...g,isExecuting:!1,isComplete:!0}:g),y("Processing response..."));break;case"file_saved":break;case"complete":l=b;break;case"error":y(`Error: ${b.message}`);break}}const u=Te(r);return d(c=>c.map(b=>{if(b.id!==s)return b;const x=r?b.content?b.content+u:u:b.content;return{...b,content:x,codeBlocks:[...n],statusLog:[...b.statusLog||[],...o],isStreaming:l===null}})),D([...n]),l&&vs(l,s),{accumulatedText:r,codeBlocks:n,lastStatus:p,completedData:l}},vs=(e,s)=>{W(!1),X(n=>n===s?null:n),y("Analysis complete!");const r=e.files||[];(r.length>0||e.reportId)&&Y({reportId:e.reportId,customerName:e.customerName||"",files:r,message:e.message||""}),Zt({reportId:e.reportId}),ue.current=null,d(n=>n.map(o=>o.id===s?{...o,isStreaming:!1}:o)),B.current&&(clearInterval(B.current),B.current=null)},Mt=async(e,s)=>
38{B.current&&clearInterval(B.current),ue.current=e,B.current=setInterval(async()=>{try{const r=Fe.current>0?`?afterId=${Fe.current}`:"",n=await fetch(`/api/reports/${e}/events${r}`,{credentials:"include"});if(n.ok){Ae.current=Date.now();const o=await n.json();if(o.events&&o.events.length>0&&Pt(o.events,s),(o.status==="completed"||o.status==="failed")&&(B.current&&(clearInterval(B.current),B.current=null),o.status==="failed")){if(W(!1),X(p=>p===s?null:p),Xt(o.events)){y("Report timed out");const p=m?`/analysis?customerId=${m}`:"/analysis";ce({title:"Report timed out — please try again",description:"We didn't see any progress for a while, so the analysis was automatically cancelled. You can safely start it again.",variant:"destructive",duration:1e4,action:t.jsx(Ee,{href:p,children:t.jsxs(J,{variant:"outline",size:"sm","data-testid":"toast-action-retry-report",children:[t.jsx(kt,{className:"h-3.5 w-3.5 mr-1.5"}),"Generate again"]})})})}else y("Analysis failed");d(p=>p.map(l=>l.id===s?{...l,isStreaming:!1}:l))}}}catch(r){console.error("Error polling for session events:",r)}},2e3),setTimeout(()=>{B.current&&(clearInterval(B.current),B.current=null)},9e5)},ae=f.useRef(null),K=f.useRef(null),_t=async e=>{ue.current=e,ae.current&&clearInterval(ae.current),K.current&&clearTimeout(K.current);const s=setInterval(async()=>{try{const r=await fetch(`/api/reports/${e}`,{credentials:"include"});if(r.ok){const n=await r.json();if(n.report?.status==="completed"){clearInterval(s),ae.current=null,K.current&&(clearTimeout(K.current),K.current=null),W(!1);const o=n.report,a=[];o.wordDocPath&&a.push({type:"word",filename:o.wordDocPath.split("/").pop()||"report.docx"}),o.excelPath&&a.push({type:"excel",filename:o.excelPath.split("/").pop()||"report.xlsx"}),a.length>0&&Y({reportId:o.id,customerName:o.clientName,files:a,message:""}),y("Analysis complete!");const p={id:`completion-${Date.now()}`,role:"assistant",content:`✅ **Analysis Complete!**
39
40Your reports are ready for download below.
41
42📁 [View Report Files](/files${m?`?customer=${m}`:""}) or preview: <span class="purple-badge">Reports Ready ▸</span>`,timestamp:new Date,isStreaming:!1};d(l=>[...l,p])}else if(n.report?.status==="failed"){clearInterval(s),ae.current=null,K.current&&(clearTimeout(K.current),K.current=null),W(!1);let o=!1;try{const a=await fetch(`/api/reports/${e}/events`,{credentials:"include"});if(a.ok){const p=await a.json();o=Xt(p.events)!==null}}catch{}if(o){y("Report timed out");const a=m?`/analysis?customerId=${m}`:"/analysis";ce({title:"Report timed out — please try again",description:"We didn't see any progress for a while, so the analysis was automatically cancelled. You can safely start it again.",variant:"destructive",duration:1e4,action:t.jsx(Ee,{href:a,children:t.jsxs(J,{variant:"outline",size:"sm","data-testid":"toast-action-retry-report",children:[t.jsx(kt,{className:"h-3.5 w-3.5 mr-1.5"}),"Generate again"]})})})}else y("Analysis failed")}}}catch(r){console.error("Error polling for session completion:",r)}},5e3);ae.current=s,K.current=setTimeout(()=>{clearInterval(s),ae.current=null,K.current=null},6e5)};f.useEffect(()=>{if(!ve||!M?.id||!te?.id)return;const e=++Ze.current,s=ve,r=()=>Ze.current===e&&Ft.current===s;return gt.current!==s&&(gt.current=s,ps(),lt(!0)),(async()=>{const o=!st&&!tt?Us():null;if(await wt(r),!!r()){if(et&&de(et),o&&o.files.length>0){Xe.current=!0,de(o.customerId),ge(o.files),$e(),ne(null),pe(null),fe(null),he(null),Y(null),d([{id:"1",role:"assistant",content:`Starting license analysis for **${o.customerName}**...
43
44The AI analysis will begin automatically.`,timestamp:new Date}]);return}if(st)await gs(st,r);else if(tt)await hs(tt,r);else{const a=await xs(r);if(!r())return;a||(et?($e(),ne(null),pe(null),fe(null),he(null),Y(null),d([{id:"1",role:"assistant",content:`Hi! I'm your M365 License Analyst. Here's how I work:
45
461. **Chat with me** — Ask any M365 licensing question and I'll respond right here in the chat
472. **Attach files** — Use the paperclip to attach CSV, Excel, PDF, or Word files for me to analyze
483. **Generate reports** — When you attach a CSV/Excel file, check **"Generate M365 Report"** to run a full analysis and create downloadable Word and Excel reports
49
50To get started, select or create a project, then ask a question or attach your license data.`,timestamp:new Date}])):us())}}})(),()=>{Ze.current===e&&(Ze.current+=1),B.current&&(clearInterval(B.current),B.current=null),G.current&&(clearInterval(G.current),G.current=null),O.current&&(clearInterval(O.current),O.current=null),ae.current&&(clearInterval(ae.current),ae.current=null),K.current&&(clearTimeout(K.current),K.current=null)}},[et,tt,st,ve]),f.useEffect(()=>{Xe.current&&E.length>0&&m&&!Q&&!qe&&(Xe.current=!1,setTimeout(()=>yt(),100))},[E,m,qe]),f.useEffect(()=>{(_.length>
501||z)&&ds({messages:_,sessionId:z,selectedCustomerId:m,completedReport:q,awaitingUserResponse:it,savedConversationId:be,conversationCustomerId:Z,conversationClientId:ke})},[_,z,m,q,it,be,Z,ke]),f.useEffect(()=>{if(!m||_.length<=1||z||Z&&Z!==m||_.some(r=>r.isStreaming))return;const s=setTimeout(()=>{fs()},2e3);return()=>clearTimeout(s)},[_,m,Z,be,ke,z]);const ze=f.useRef(null);f.useEffect(()=>{if(ze.current===null){ze.current=m;return}m&&ze.current!==m&&Z&&Z!==m&&(d([{id:"1",role:"assistant",content:`Hi! I'm your M365 License Analyst. Here's how I work:
51
521. **Chat with me** — Ask any M365 licensing question and I'll respond right here in the chat
532. **Attach files** — Use the paperclip to attach CSV, Excel, PDF, or Word files for me to analyze
543. **Generate reports** — When you attach a CSV/Excel file, check **"Generate M365 Report"** to run a full analysis and create downloadable Word and Excel reports
55
56To get started, select or create a project, then ask a question or attach your license data.`,timestamp:new Date}]),pe(null),fe(null),he(null),ne(null),Y(null)),ze.current=m},[m,Z]),f.useEffect(()=>{Ye.current&&(Ye.current.scrollTop=Ye.current.scrollHeight)},[_,os]),f.useEffect(()=>{let e=null;return Je?(Ke.current===null&&(Ce.current=Date.now(),Be(0)),e=setInterval(()=>{if(Ce.current){const s=Math.floor((Date.now()-Ce.current)/1e3);Be(s)}},1e3)):(Ce.current=null,Be(0)),Ke.current=Je,()=>{e&&clearInterval(e)}},[Je]),f.useEffect(()=>{Qe.current=z},[z]);const Bt=async e=>{if(De.current||ye.current)return;const s=ue.current,r=Qe.current;if(!(!s&&!r))try{let n=null,o=null;if(r){const a=await fetch(`/api/session/${r}/status`,{credentials:"include"});a.ok&&(o=await a.json(),n=o.status)}else if(s){const a=await fetch(`/api/reports/${s}`,{credentials:"include"});if(a.ok){const p=await a.json();o=p.report,n=p.report?.status}}if(!n)return;if(n==="completed"||n==="failed"){if(De.current)return;if(De.current=!0,`${e}${n}`,B.current&&(clearInterval(B.current),B.current=null),G.current&&(clearInterval(G.current),G.current=null),O.current&&(clearInterval(O.current),O.current=null),W(!1),X(null),y(n==="completed"?"Analysis complete!":"Analysis failed"),n==="completed"&&o){const a=o.files||[];o.wordDocPath&&a.push({type:"word",filename:o.wordDocPath.split("/").pop()||"report.docx"}),o.excelPath&&a.push({type:"excel",filename:o.excelPath.split("/").pop()||"report.xlsx"});const p=o.reportId||s;a.length>0&&p&&(Y({reportId:p,customerName:o.customerName||o.clientName||"",files:a,message:"Reports generated successfully!"}),Zt({reportId:p})),d(l=>{const u=[...l].reverse().find(c=>c.role==="assistant");return u?u.content.includes("Reports generated successfully")?l.map(c=>c.id===u.id?{...c,isStreaming:!1}:c):l.map(c=>c.id===u.id?{...c,isStreaming:!1,content:`${c.content}
57
58---
59
60Reports generated successfully! Click "View Reports" below to preview or download.
61
62📁 [View Report Files](/files${m?`?customer=${m}`:""}) or preview: <span class="purple-badge">Reports Ready ▸</span>`}:c):[...l,{id:`sync-complete-${Date.now()}`,role:"assistant",content:`Analysis complete! Your reports are ready for download.
63
64📁 [View Report Files](/files${m?`?customer=${m}`:""}) or preview: <span class="purple-badge">Reports Ready ▸</span>`,timestamp:new Date,isStreaming:!1}]})}else n==="failed"&&d(a=>{const p=[...a].reverse().find(l=>l.role==="assistant");return p?a.map(l=>l.id===p.id?{...l,isStreaming:!1,content:`${l.content}
65
66---
67
68The analysis has ended. Please try again or check your reports list.`}:l):a});ue.current=null}}catch(n){console.error(`[${e}] Status check failed:`,n)}};f.useEffect(()=>{if(Q)return Ae.current=Date.now(),De.current=!1,G.current&&clearInterval(G.current),G.current=setInterval(()=>{Date.now()-Ae.current>3e4&&Bt("Heartbeat timeout")},1e4),()=>{G.current&&(clearInterval(G.current),G.current=null)};G.current&&(clearInterval(G.current),G.current=null)},[Q,z]),f.useEffect(()=>{if(Q)return O.current&&clearInterval(O.current),O.current=setInterval(()=>{Bt("Periodic status poll")},15e3),()=>{O.current&&(clearInterval(O.current),O.current=null)};O.current&&(clearInterval(O.current),O.current=null)},[Q,z]);const ws=e=>{const s=Math.floor(e/60),r=e%60;return`${s}:${r.toString().padStart(2,"0")}`},wt=async(e=()=>!0)=>{try{const s=await fetch("/api/customers",{credentials:"include"});if(!e())return;if(s.ok){const r=await s.json();if(!e())return;Rt(r.customers||[])}}catch(s){console.error("Failed to fetch customers:",s)}finally{e()&&lt(!1)}},Lt=async()=>
68{if(oe.trim())try{const e=await fetch("/api/customers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:oe.trim(),checkDuplicate:!0}),credentials:"include"});if(e.status===409){const r=(await e.json()).existingCustomer;ce({title:"Project already exists",description:`A project named "${r.name}" already exists. Click the link below to open it.`,action:t.jsx(Ee,{href:`/customers/${r.id}`,children:t.jsx(J,{variant:"outline",size:"sm",children:"Open Project"})}),duration:8e3});return}if(e.ok){const s=await e.json();await wt(),de(s.customer.id),Se(""),Ne(!1)}else console.error("Failed to create customer")}catch(e){console.error("Failed to create customer:",e)}},ys=e=>{const s=e.target.files;if(s&&s.length>0){const r=Array.from(s);ge(n=>[...n,...r]),Re(!0)}we.current&&(we.current.value="")},bs=e=>{const s=E.filter((r,n)=>n!==e);ge(s),s.length===0?Re(!1):s.some(n=>{const o=n.name.toLowerCase().split(".").pop();return o==="csv"||o==="xls"||o==="xlsx"})||Re(!1)},ks=()=>{se.current&&(se.current.abort(),se.current=null),G.current&&(clearInterval(G.current),G.current=null),O.current&&(clearInterval(O.current),O.current=null),B.current&&(clearInterval(B.current),B.current=null),ye.current=!1,d(e=>e.map(s=>s.isStreaming?{...s,isStreaming:!1,content:s.content?`${s.content}
69
70---
71
72⛔ Generation was stopped by user.`:"⛔ Generation was stopped by user."}:s)),Ve(!1),W(!1),y(""),_e(null),X(null),H([]),D([])},yt=async()=>{if(E.length===0)return;try{Ut(Vt.ReportGenerated,{files:E.length,hasCustomer:!!m,hasPrompt:!!Me.trim()})}catch{}if(!m&&!oe.trim()){const x={id:Date.now().toString(),role:"assistant",content:"Please select an existing customer or enter a new customer name before uploading.",timestamp:new Date};d(v=>[...v,x]);return}const e=We||me.length===0?oe:me.find(x=>x.id===m)?.name||"",s=Me.trim()?`Generate license analysis reports for ${e}
73
74Additional instructions: ${Me.trim()}`:`Generate license analysis reports for ${e}`,r={id:Date.now().toString(),role:"user",content:s,timestamp:new Date,attachments:E.map(x=>({name:x.name,size:x.size})),...Tt()};if(d(x=>[...x,r]),m&&(Z&&Z!==m&&(pe(null),he(null)),fe(m),!ke&&!be)){const x=`${m}-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;he(x),Et(x,m)}W(!0),_e(new Date),y(`Uploading ${E.length} file${E.length>1?"s":""}...`),ue.current=null,Qe.current=null,De.current=!1;const n=[...E],o=Me.trim();if(n.length===0){W(!1);return}ge([]),ot("");const a=(Date.now()+1).toString(),p={id:a,role:"assistant",content:"",timestamp:new Date,isStreaming:!0,toolActivity:[],statusLog:[]};d(x=>[...x,p]),X(a),H([]);let l=!1,u=null,c="",b=!1;try{const x=new FormData;n.forEach((I,U)=>{x.append("files",I)}),(We||me.length===0)&&oe.trim()?x.append("newCustomerName",oe.trim()):m&&x.append("customerId",m),o&&x.append("prompt",o);const v=new AbortController;se.current=v;const g=await fetch("/api/upload-stream",{method:"POST",body:x,credentials:"include",signal:v.signal});if(!g.ok)throw new Error(await rt(g,"Failed to start report generation"));const L=g.body?.getReader(),N=new TextDecoder;if(!L)throw new Error("No response stream available");let T="",$=[],k=[],h=[],P="",ee="",C=null;for(;;){const{done:I,value:U}=await L.read();if(I)break;const w=N.decode(U,{stream:!0});ee+=w;const i=ee.split(`
75`);ee=i.pop()||"";for(const A of i){if(A.startsWith("data: "))try{const R=JSON.parse(A.slice(6));switch(Ae.current=Date.now(),R.type){case"connected":R.message,y(R.message);break;case"ping":break;case"status":y(R.message),h=[...h,{message:R.message,timestamp:new Date}],d(j=>j.map(S=>S.id===a?{...S,statusLog:[...h]}:S)),R.sessionId&&(ne(R.sessionId),u=R.sessionId),R.reportId&&(ue.current=R.reportId);break;case"text":T+=R.text,c=T;const Is=Te(T);d(j=>j.map(S=>S.id===a?{...S,content:Is,codeBlocks:[...k]}:S));break;case"text_start":T.length>0&&(T+=`
76
77`);break;case"stream_reset":T="",k=[],P="",D([]),d(j=>j.map(S=>S.id===a?{...S,content:"",codeBlocks:[]}:S));break;case"code_start":P=R.id||`code_${Date.now()}`;const Rs={id:P,code:"",output:"",result:"",isExecuting:!1,isComplete:!1};k=[...k,Rs],D([...k]),d(j=>j.map(S=>S.id===a?{...S,codeBlocks:[...k]}:S)),y("Writing code...");break;case"code_delta":P&&R.code&&(k=k.map(j=>
77j.id===P?{...j,code:j.code+R.code}:j),D([...k]),d(j=>j.map(S=>S.id===a?{...S,codeBlocks:[...k]}:S)));break;case"code_complete":R.code&&P&&(k=k.map(j=>j.id===P?{...j,code:R.code,isExecuting:!0}:j),D([...k]),d(j=>j.map(S=>S.id===a?{...S,codeBlocks:[...k]}:S)),y("Running code..."));break;case"execution_start":P&&(k=k.map(j=>j.id===P?{...j,isExecuting:!0}:j),D([...k]),y("Running code..."));break;case"execution_output":R.output&&P&&(k=k.map(j=>j.id===P?{...j,output:j.output+R.output}:j),D([...k]),d(j=>j.map(S=>S.id===a?{...S,codeBlocks:[...k]}:S)),y("Running code..."));break;case"execution_result":R.result&&P&&(k=k.map(j=>j.id===P?{...j,result:R.result}:j),D([...k]),d(j=>j.map(S=>S.id===a?{...S,codeBlocks:[...k]}:S)));break;case"execution_end":P&&(k=k.map(j=>j.id===P?{...j,isExecuting:!1,isComplete:!0}:j),D([...k]),d(j=>j.map(S=>S.id===a?{...S,codeBlocks:[...k]}:S)),y("Processing response..."));break;case"tool_start":const Cs=R.name||"Processing";$=[...$,`${Cs}...`],H($);break;case"tool_end":break;case"file_found":$=[...$,`Found: ${R.filename||R.fileId?.slice(-8)}`],H($);break;case"file_saved":$=[...$,`Saved: ${R.filename}`],H($);break;case"complete":l=!0,R.awaitingInput&&Ie(!0),R.files?.length>0&&Y({reportId:R.reportId,customerName:R.customerName,files:R.files,message:R.message});const As=R.files?.length>0?`${T}
78
79---
80
81Reports generated successfully! Click "View Reports" below to preview or download.
82
83📁 [View Report Files](/files${m?`?customer=${m}`:""}) or preview: <span class="purple-badge">Reports Ready ▸</span>`:T||"Processing complete.";d(j=>j.map(S=>S.id===a?{...S,content:As,isStreaming:!1,toolActivity:$,codeBlocks:[...k]}:S)),We&&R.customerId&&(await wt(),Se(""),Ne(!1),de(R.customerId));break;case"error":h=[...h,{message:`Error: ${R.message}`,timestamp:new Date}],d(j=>j.map(S=>S.id===a?{...S,statusLog:[...h]}:S)),C=new Error(R.message);break}}catch{}if(C)throw C}}}catch(x){if(x.name==="AbortError"){b=!0;return}d(v=>v.map(g=>{if(g.id!==a)return g;const L=g.content||"",N=`
84
85${x.message}`;return{...g,content:L+N,isStreaming:!1}}))}finally{if(se.current=null,b){W(!1),y(""),X(null),H([]);return}const x=a;let v=!0;if(!l&&u){const L=[1e3,2e3,4e3];let N=!1,T=null;for(let $=0;$<3&&!N;$++)try{$+1,`${L[$]}`,await new Promise(h=>setTimeout(h,L[$]));const k=await fetch(`/api/session/${u}/status`,{credentials:"include"});if(k.ok){const h=await k.json();if(T=h.status,h.status==="completed"&&h.files?.length>0)Y({reportId:h.reportId,customerName:h.customerName||me.find(P=>P.id===m)?.name||"",files:h.files,message:"Reports generated successfully!"}),d(P=>P.map(ee=>ee.id===x?{...ee,content:`${c||""}
86
87---
88
89Reports generated successfully! Click "View Reports" below to preview or download.
90
91📁 [View Report Files](/files${m?`?customer=${m}`:""}) or preview: <span class="purple-badge">Reports Ready ▸</span>`,isStreaming:!1}:ee)),N=!0;else if(h.status!=="processing"){if(h.status==="failed"){N=!1,T="failed";break}}}}catch(k){console.error(`Fallback status check failed (attempt ${$+1}):`,k),T="error"}if(N)v=!0;else if(T==="failed")v=!0,d($=>$.map(k=>k.id===x?{...k,content:`${Te(c||"")}
92
93---
94
95The analysis has ended. Please try again or check your reports list.`,isStreaming:!1}:k));else if(T==="processing"||T==="error"){const $=ue.current;$?(y("Reconnecting to analysis..."),Mt($,x),v=!1):u?(y("Reconnecting..."),v=!1):(d(k=>k.map(h=>h.id===x?{...h,content:`${Te(c||"")}
96
97---
98
99⏳ Report generation is still in progress. Please check your reports list in a few moments.`,isStreaming:!1}:h)),v=!0)}}v&&(W(!1),y(""),X(g=>g===a?null:g),H([]))}},zt=async e=>{const s=(e??at).trim();if(!s||je)return;if(!m&&!oe.trim()){const l={id:Date.now().toString(),role:"assistant",content:"Please select an existing customer or enter a new customer name before asking questions. This helps keep your conversations organized by customer.",timestamp:new Date};d(u=>[...u,l]);return}const r={id:Date.now().toString(),role:"user",content:s,timestamp:new Date,...Tt()};if(d(l=>[...l,r]),m&&(Z&&Z!==m&&(pe(null),he(null)),fe(m),!ke&&!be)){const l=`${m}-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;he(l),Et(l,m)}const n=s;e||nt("");const o=[...Dt,...!re&&E.length>0?E:[]];if(!re&&E.length>0&&(ge([]),Re(!1)),o.length>0&&ct([]),z){Ie(!1),o.length>0?await js(n,o):await Ss(n);return}if(o.length>0&&!z){const l={id:Date.now().toString(),role:"assistant",content:`To attach files for chat, you'll need an existing project first. Either:
100
1011. **Generate a report** — Check **"Generate M365 Report"** and click Generate Reports to start a project
1022. **Chat first** — Send your question without files, and you can attach files in follow-up messages after a project is created`,timestamp:new Date};d(u=>[...u,{id:(Date.now()-1).toString(),role:"user",content:n,timestamp:new Date,attachments:o.map(c=>({name:c.name,size:c.size}))},l]),ge(o);return}Ve(!0),y("Preparing response...");const a=(Date.now()+1).toString(),p={id:a,role:"assistant",content:"",timestamp:new Date,isStreaming:!0,codeBlocks:[]};d(l=>[...l,p]),X(a);try{const l=new AbortController;se.current=l;const u=await fetch("/api/general-chat",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:n}),credentials:"include",signal:l.signal});if(!u.ok)throw new Error(await rt(u,"Failed to get response"));y("Writing response...");const c=u.body?.getReader(),b=new TextDecoder;if(!c)throw new Error("No response stream available");let x="",v=[],g="",L="",N=null;for(;;){const{done:T,value:$}=await c.read();if(T)break;const k=b.decode($,{stream:!0});L+=k;const h=L.split(`
103
104`);L=h.pop()||"";for(const P of h){const ee=P.split(`
105`);for(const C of ee){if(C.startsWith("data: "))try{const I=JSON.parse(C.slice(6));switch(I.type){case"text":x+=I.text;const U=Te(x);d(i=>i.map(A=>
105A.id===a?{...A,content:U,codeBlocks:[...v]}:A));break;case"text_start":x.length>0&&(x+=`
106
107`);break;case"stream_reset":x="",v=[],g="",D([]),d(i=>i.map(A=>A.id===a?{...A,content:"",codeBlocks:[]}:A));break;case"code_start":g=I.id||`code_${Date.now()}`;const w={id:g,code:"",output:"",result:"",isExecuting:!1,isComplete:!1};v=[...v,w],D([...v]),d(i=>i.map(A=>A.id===a?{...A,codeBlocks:[...v]}:A)),y("Writing code...");break;case"code_delta":g&&I.code&&(v=v.map(i=>i.id===g?{...i,code:I.code}:i),D([...v]),d(i=>i.map(A=>A.id===a?{...A,codeBlocks:[...v]}:A)));break;case"code_complete":I.code&&g&&(v=v.map(i=>i.id===g?{...i,code:I.code,isExecuting:!0}:i),D([...v]),d(i=>i.map(A=>A.id===a?{...A,codeBlocks:[...v]}:A)),y("Running code..."));break;case"execution_start":g&&(v=v.map(i=>i.id===g?{...i,isExecuting:!0}:i),D([...v]),y("Running code..."));break;case"execution_output":I.output&&g&&(v=v.map(i=>i.id===g?{...i,output:i.output+I.output}:i),D([...v]),d(i=>i.map(A=>A.id===a?{...A,codeBlocks:[...v]}:A)),y("Running code..."));break;case"execution_end":g&&(v=v.map(i=>i.id===g?{...i,isExecuting:!1,isComplete:!0}:i),D([...v]),d(i=>i.map(A=>A.id===a?{...A,codeBlocks:[...v]}:A)),y("Processing response..."));break;case"complete":d(i=>i.map(A=>A.id===a?{...A,isStreaming:!1,incomplete:!1,errorMessage:void 0,codeBlocks:[...v]}:A)),y("Response complete");break;case"error":N=new Error(I.message);break}}catch{}if(N)throw N}}}}catch(l){if(l.name==="AbortError")return;d(u=>u.map(c=>{if(c.id!==a)return c;const b=c.content||"";return{...c,content:b||"I could not complete this response.",isStreaming:!1,incomplete:!0,errorMessage:l.message||"The response ended unexpectedly.",retryPrompt:n}})),y("Response incomplete")}finally{se.current=null,X(l=>l===a?null:l),Ve(!1)}},js=async(e,s)=>{if(!z)return;const r=s||Dt;if(r.length===0)return;W(!0),y("Uploading files..."),ye.current=!0;const n=r.map(u=>({name:u.name,size:u.size})),a={id:Date.now().toString(),role:"user",content:e||`Uploaded ${r.length} file${r.length!==1?"s":""}`,timestamp:new Date,attachments:n},p=(Date.now()+1).toString(),l={id:p,role:"assistant",content:"",timestamp:new Date,isStreaming:!0,toolActivity:[],statusLog:[]};d(u=>[...u,a,l]),X(p),H([]);try{const u=new FormData;u.append("message",e||"");for(const $ of r)u.append("files",$);ct([]);const c=new AbortController;se.current=c;const b=await fetch(`/api/conversation/${z}/upload`,{method:"POST",body:u,credentials:"include",signal:c.signal});if(!b.ok)throw new Error(await rt(b,"Failed to upload files"));const x=b.body?.getReader(),v=new TextDecoder;if(!x)throw new Error("No response stream available");let g="",L=[],N=[],T=[];for(;;){const{done:$,value:k}=await x.read();if($)break;const P=v.decode(k,{stream:!0}).split(`
108`);for(const ee of P)if(ee.startsWith("data: "))try{const C=JSON.parse(ee.slice(6));if(Ae.current=Date.now(),C.type==="text")g+=C.text,d(I=>I.map(U=>U.id===p?{...U,content:g}:U));else if(C.type==="status")y(C.message),T=[...T,{timestamp:new Date,message:C.message}],d(I=>I.map(U=>U.id===p?{...U,statusLog:T}:U));else if(C.type==="code_start"){const I={id:C.id||`code_${Date.now()}`,code:"",output:"",result:"",isExecuting:!0,isComplete:!1};N=[...N,I],D(N),L=[...L,"Writing code..."],H(L),y("Writing code...")}else if(C.type==="code_delta"){if(N.length>0){const I=N[N.length-1];I.code+=C.code,D([...N])}}else if(C.type==="execution_output"){if(N.length>0){const I=N[N.length-1];I.output+=C.output,D([...N]),y("Running code...")}}else if(C.type==="execution_end")N.length>0&&(N[N.length-1].isExecuting=!1,D([...N])),y("Processing response...");else if(C.type==="file_found")L=[...L,`File generated: ${C.filename}`],H(L);else if(C.type==="complete"){const I=C.message&&C.message.length>=g.length?C.message:g||C.message;d(U=>U.map(w=>w.id===p?{...w,content:I||"Processing complete.",isStreaming:!1,toolActivity:L,codeBlocks:N,statusLog:T,files:C.files||[]}:w)),C.files&&C.files.length>0&&Y({reportId:ue.current||"",customerName:"",files:C.files,message:I}),Gt()}else C.type==="error"&&d(I=>I.map(U=>U.id===p?{...U,content:C.message||"An error occurred.",isStreaming:!1}:U))}catch{}}
108d($=>$.map(k=>k.id===p&&k.isStreaming?{...k,isStreaming:!1,content:g,codeBlocks:N,statusLog:T}:k))}catch(u){if(u.name==="AbortError")return;d(c=>c.map(b=>b.id===p?{...b,content:"Failed to process uploaded files. Please try again.",isStreaming:!1}:b))}finally{se.current=null,ye.current=!1,W(!1),X(u=>u===p?null:u),y(""),H([]),D([])}},Gt=async()=>{if(z)try{const e=await fetch(`/api/session/${z}/files`,{credentials:"include"});if(e.ok){const s=await e.json();dt(s.totalFiles||0),pt({projectFiles:s.projectFiles||[],sourceFiles:s.sourceFiles||[],reportFiles:s.reportFiles||[]})}else e.status===404&&(dt(0),pt({projectFiles:[],sourceFiles:[],reportFiles:[]}),ne(null),$e())}catch{}};f.useEffect(()=>{z&&Gt()},[z]);const Ss=async e=>{if(!z)return;W(!0),y("Processing your response..."),ye.current=!0;const s=(Date.now()+1).toString(),r={id:s,role:"assistant",content:"",timestamp:new Date,isStreaming:!0,toolActivity:[],statusLog:[]};d(n=>[...n,r]),X(s),H([]);try{const n=new AbortController;se.current=n;const o=await fetch(`/api/conversation/${z}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:e,includeSourceData:Ct}),credentials:"include",signal:n.signal});if(!o.ok)throw new Error(await rt(o,"Failed to continue conversation"));const a=o.body?.getReader(),p=new TextDecoder;if(!a)throw new Error("No response stream available");let l="",u=[],c=[],b=[],x="",v="",g=null;for(;;){const{done:L,value:N}=await a.read();if(L)break;const T=p.decode(N,{stream:!0});v+=T;const $=v.split(`
109`);v=$.pop()||"";for(const k of $){if(k.startsWith("data: "))try{const h=JSON.parse(k.slice(6));switch(h.type,h.type==="status"&&h.message,h.type){case"status":y(h.message),b=[...b,{message:h.message,timestamp:new Date}],d(w=>w.map(i=>i.id===s?{...i,statusLog:[...b]}:i));break;case"text":l+=h.text,d(w=>w.map(i=>i.id===s?{...i,content:l,codeBlocks:[...c]}:i));break;case"text_start":l.length>0&&(l+=`
110
111`);break;case"stream_reset":l="",c=[],x="",D([]),d(w=>w.map(i=>i.id===s?{...i,content:"",codeBlocks:[]}:i));break;case"code_start":x=h.id||`code_${Date.now()}`;const P={id:x,code:"",output:"",result:"",isExecuting:!1,isComplete:!1};c=[...c,P],D([...c]),d(w=>w.map(i=>i.id===s?{...i,codeBlocks:[...c]}:i)),y("Writing code...");break;case"code_delta":x&&h.code&&(c=c.map(w=>w.id===x?{...w,code:w.code+h.code}:w),D([...c]),d(w=>w.map(i=>i.id===s?{...i,codeBlocks:[...c]}:i)));break;case"code_complete":h.code&&x&&(c=c.map(w=>w.id===x?{...w,code:h.code,isExecuting:!0}:w),D([...c]),d(w=>w.map(i=>i.id===s?{...i,codeBlocks:[...c]}:i)),y("Running code..."));break;case"execution_start":x&&(c=c.map(w=>w.id===x?{...w,isExecuting:!0}:w),D([...c]),y("Running code..."));break;case"execution_output":h.output&&x&&(c=c.map(w=>w.id===x?{...w,output:w.output+h.output}:w),D([...c]),d(w=>w.map(i=>i.id===s?{...i,codeBlocks:[...c]}:i)),y("Running code..."));break;case"execution_result":h.result&&x&&(c=c.map(w=>w.id===x?{...w,result:h.result}:w),D([...c]),d(w=>w.map(i=>i.id===s?{...i,codeBlocks:[...c]}:i)));break;case"execution_end":x&&(c=c.map(w=>w.id===x?{...w,isExecuting:!1,isComplete:!0}:w),D([...c]),d(w=>w.map(i=>i.id===s?{...i,codeBlocks:[...c]}:i)),y("Processing response..."));break;case"tool_start":const ee=h.name||"Processing";u=[...u,`${ee}...`],H(u);break;case"tool_end":break;case"file_found":u=[...u,`Found file: ${h.fileId?.slice(-8)||"file"}`],H(u);break;case"file_saved":u=[...u,`Saved: ${h.filename}`],H(u);break;case"complete":h.awaitingInput&&Ie(!0),h.reportCompleted&&h.files?.length>0&&Y({reportId:q?.reportId||"",customerName:q?.customerName||"",files:h.files,message:h.message});let I=(h.message&&h.message.length>=l.length?h.message:l)||"Processing complete.";const U=h.reportId||q?.reportId||"";h.reportCompleted&&h.files?.length>0&&I.length<100?I=`${I}
112
113---
114
115Reports generated successfully! Click "View Reports" below to preview or download.${U?`
116
117📁 [View Report Files](/files${m?`?customer=${m}`:""}) or preview: <span class="purple-badge">Reports Ready ▸</span>`:""}`:h.reportCompleted&&h.files?.length>0&&(I=`${I}
118
119---
120
121New files generated and saved to your project.${U?`
122
123📁 [View Report Files](/files${m?`?customer=${m}`:""}) or preview: <span class="purple-badge">Reports Ready ▸</span>`:""}`),d(w=>w.map(i=>i.id===s?{...i,content:I,isStreaming:!1,toolActivity:u,codeBlocks:[...c]}:i));break;case"error":b=[...b,{message:`Error: ${h.message}`,timestamp:new Date}],d(w=>w.map(i=>i.id===s?{...i,statusLog:[...b]}:i)),g=new Error(h.message);break}}catch{}if(g)throw g}}d(L=>L.map(N=>N.id===s&&N.isStreaming?{...N,isStreaming:!1,content:l||N.content,toolActivity:u,codeBlocks:[...c],statusLog:b}:N))}catch(n){if(n.name==="AbortError")return;d(o=>o.map(a=>{if(a.id!==s)return a;const p=a.content||"",l=`
124
125${n.message}`;return{...a,content:p+l,isStreaming:!1}}))}finally{se.current=null,ye.current=!1,W(!1),_e(null),y(""),X(n=>n===s?null:n),H([]),D([])}},Ns=async()=>{try{await Os("POST","/api/active-session/cancel")}catch(e){console.error("Failed to cancel active session:",e)}d([{id:"1",role:"assistant",content:`Hi! I'm your M365 License Analyst. Here's how I work:
126
1271. **Chat with me** — Ask any M365 licensing question and I'll respond right here in the chat
1282. **Attach files** — Use the paperclip to attach CSV, Excel, PDF, or Word files for me to analyze
1293. **Generate reports** — When you attach a CSV/Excel file, check **"Generate M365 Report"** to run a full analysis and create downloadable Word and Excel reports
130
131To get started, select or create a project, then ask a question or attach your license data.`,timestamp:new Date}]),ne(null),ge([]),Y(null),Ne(!1),Se(""),Ie(!1),D([]),pe(null),$e()},Ot=me.find(e=>e.id===m);return!ve||gt.current!==ve?t.jsx(Wt,{children:t.jsx("div",{className:"flex min-h-[50vh] items-center justify-center","aria-label":"Loading analysis",children:t.jsx(Ge,{className:"h-8 w-8 animate-spin text-muted-foreground"})})}):t.jsxs(Wt,{children:[t.jsxs("div",{className:"h-[calc(100vh-64px)] flex flex-col -mt-2 -mb-8 bg-[var(--clr-pearl-100)] -mx-4 px-4",children:[V&&t.jsx("div",{className:"px-1 pt-1",children:t.jsx(Ps,{})}),m&&Ot&&t.jsxs("div",{className:"flex items-center gap-2 text-[11px] uppercase tracking-widest py-1.5 mb-1",style:{fontFamily:"var(--font-mono)"},children:[t.jsx(Ee,{href:"/dashboard",className:"text-[var(--clr-ink-3)] hover:text-[var(--clr-navy)] transition-colors",children:"Dashboard"}),t.jsx("span",{className:"text-[var(--clr-shell)]",children:"›"}),t.jsx(Ee,{href:`/customer/${m}`,className:"text-[var(--clr-ink-3)] hover:text-[var(--clr-navy)] transition-colors",children:Ot.name}),t.jsx("span",{className:"text-[var(--clr-shell)]",children:"›"}),t.jsx("span",{className:"text-[var(--clr-navy)]",children:"Chat"})]}),t.jsxs("div",{className:"flex items-center justify-between py-2.5 px-4 bg-[var(--clr-navy)] rounded-lg mb-2 flex-wrap gap-2 shadow-sm",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[m&&t.jsx(Ee,{href:`/customer/${m}`,children:t.jsx(J,{variant:"ghost",size:"icon",className:"h-8 w-8 transition-all duration-200 hover:bg-white/10 text-white active:scale-95",title:"Back to customer","aria-label":"Back to customer",children:t.jsx(Ms,{className:"h-4 w-4"})})}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx(jt,{className:"h-5 w-5 text-white/80"}),t.jsx("span",{className:"font-semibold text-sm text-white",children:"AI Analysis"})]})]}),t.jsxs("div",{className:"flex items-center gap-2",children:[q&&q.files?.length>0&&t.jsxs(J,{variant:"default",size:"sm",className:"h-8 text-xs font-medium bg-[var(--clr-moss-700)] text-white hover:bg-[var(--clr-moss)] hover:text-white border-none rounded-full transition-all duration-200 shadow-sm",onClick:()=>He(!0),title:"Click to preview reports","data-testid":"button-reports-ready",children:[t.jsx(ts,{className:"h-3.5 w-3.5 mr-1.5"}),"Reports Ready",t.jsx(_s,{className:"h-3.5 w-3.5 ml-1.5"})]}),t.jsx(qt,{children:t.jsxs(Ht,{children:[t.jsx(Jt,{asChild:!0,children:t.jsxs(J,{variant:"outline",size:"sm",className:"h-8 text-xs font-medium bg-[var(--clr-sky-050)] text-[var(--clr-navy)] hover:bg-[var(--clr-sky)] hover:text-[var(--clr-navy)] border-[var(--clr-sky-300)] rounded-full",onClick:async()=>{try{const e=await fetch("/api/download/powershell-script",{credentials:"include"});if(!e.ok)throw new Error("Failed to download script");const s=await e.blob(),r=window.URL.createObjectURL(s),n=document.createElement("a");n.href=r,n.download="Get-M365LicensedUsers.ps1",document.body.appendChild(n),n.click(),document.body.removeChild(n),window.URL.revokeObjectURL(r)}catch(e){console.error("Download failed:",e)}},"data-testid":"button-download-powershell",children:[t.jsx(Zs,{className:"h-3.5 w-3.5 mr-1.5"}),"Get Your M365 LICENSE data"]})}),t.jsxs(Yt,{side:"bottom",className:"max-w-xs p-3 bg-[var(--clr-navy)] border-none",children:[t.jsx("p",{className:"font-medium mb-1 text-white",children:"M365 License Export Script"}),t.jsx("p",{className:"text-xs text-white",children:"Download this PowerShell script to extract Microsoft 365 user licensing data from your tenant. Run it with Global Admin permissions to generate a CSV file, then upload that CSV here for analysis."})]})]})}),t.jsxs(J,{variant:"outline",size:"sm",className:"h-8 text-xs font-medium border-transparent bg-white text-[var(--clr-navy)] rounded-full transition-all duration-200 hover:bg-[var(--clr-sky-050)] hover:text-[var(--clr-navy)] hover:shadow-md active:scale-95",onClick:Ns,title:"Start a new chat session",children:[t.jsx(St,{className:"h-3.5 w-3.5 mr-1.5"}),"New Chat"]})]})]}),$t&&t.jsxs("div",{className:"flex items-center gap-2 px-4 py-2.5 bg-[var(--clr-sky-050)] border border-[var(--clr-sky-300)] rounded-lg text-sm text-[var(--clr-navy)] shadow-sm animate-in fade-in slide-in-from-top-2","data-testid":"banner-session-resume",children:[t.jsx(Kt,{className:"h-4 w-4 shrink-0"}),t.jsxs("span",{children:[$t,xe>0?` — ${xe} file${xe!==1?"s":""} available`:""]})]}),ls&&xe>0&&t.jsxs("div",{className:"rounded-lg bg-white overflow-hidden shadow-md border-0","data-testid":"panel-project-files",children:[t.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-[var(--clr-sky)] border-b border-[var(--clr-rule)]",children:[t.jsxs("span",{className:"text-sm font-semibold text-[var(--clr-navy)] uppercase tracking-widest font-mono",style:{fontFamily:"var(--font-mono)"},children:["Project Files (",xe,")"]}),t.jsx("button",{onClick:()=>ft(!1),className:"text-[var(--clr-navy)] hover:text-[var(--clr-shell)]","data-testid":"button-close-project-files",children:t.jsx(es,{className:"h-4 w-4"})})]}),t.jsxs("div",{className:"max-h-48 overflow-y-auto divide-y divide-[var(--clr-rule)]",children:[ut.reportFiles.map((e,s)=>t.jsxs("div",{className:"flex items-center gap-2 px-3 py-2 text-sm","data-testid":`file-report-${s}`,children:[t.jsx(Oe,{className:"h-4 w-4 text-[var(--clr-sky-600)] shrink-0"}),t.jsx("span",{className:"flex-1 truncate text-[var(--clr-navy)]",children:e.originalFilename}),t.jsx("span",{className:"text-[10px] font-mono uppercase tracking-wide px-2 py-0.5 rounded-full border bg-[var(--clr-sky-050)] text-[var(--clr-navy)] border-[var(--clr-sky-300)]",children:"Report"}),e.fileSize&&t.jsxs("span",{className:"text-xs text-[var(--clr-ink-3)]",children:[(e.fileSize/1024).toFixed(0)," KB"]}
131),e.uploadedAt&&t.jsx("span",{className:"text-xs text-[var(--clr-ink-3)]",children:new Date(e.uploadedAt).toLocaleDateString()})]},`report-${s}`)),ut.sourceFiles.map((e,s)=>t.jsxs("div",{className:"flex items-center gap-2 px-3 py-2 text-sm","data-testid":`file-source-${s}`,children:[t.jsx(Ue,{className:"h-4 w-4 text-[var(--clr-moss-700)] shrink-0"}),t.jsx("span",{className:"flex-1 truncate text-[var(--clr-navy)]",children:e.originalFilename}),t.jsx("span",{className:"text-[10px] font-mono uppercase tracking-wide px-2 py-0.5 rounded-full border bg-[var(--clr-moss-100)] text-[var(--clr-moss-700)] border-[var(--clr-moss-300)]",children:"Source"}),e.fileSize&&t.jsxs("span",{className:"text-xs text-[var(--clr-ink-3)]",children:[(e.fileSize/1024).toFixed(0)," KB"]}),e.uploadedAt&&t.jsx("span",{className:"text-xs text-[var(--clr-ink-3)]",children:new Date(e.uploadedAt).toLocaleDateString()})]},`source-${s}`)),ut.projectFiles.map((e,s)=>t.jsxs("div",{className:"flex items-center gap-2 px-3 py-2 text-sm","data-testid":`file-upload-${s}`,children:[e.mimeType?.includes("pdf")?t.jsx(Oe,{className:"h-4 w-4 text-[var(--clr-shell)] shrink-0"}):t.jsx(Oe,{className:"h-4 w-4 text-[var(--clr-navy)] shrink-0"}),t.jsx("span",{className:"flex-1 truncate text-[var(--clr-navy)]",children:e.originalFilename}),t.jsx("span",{className:"text-[10px] font-mono uppercase tracking-wide px-2 py-0.5 rounded-full border bg-[var(--clr-pearl-100)] text-[var(--clr-ink-2)] border-[var(--clr-rule)]",children:"Upload"}),e.fileSize&&t.jsxs("span",{className:"text-xs text-[var(--clr-ink-3)]",children:[(e.fileSize/1024).toFixed(0)," KB"]}),e.uploadedAt&&t.jsx("span",{className:"text-xs text-[var(--clr-ink-3)]",children:new Date(e.uploadedAt).toLocaleDateString()})]},`upload-${s}`))]})]}),t.jsxs("div",{className:"flex-1 overflow-y-auto rounded-lg bg-white border-0 shadow-md p-4 space-y-4",ref:Ye,children:[_.map((e,s)=>t.jsxs("div",{className:le("flex gap-3 max-w-[85%]",e.role==="user"?"ml-auto flex-row-reverse":"",s>0&&"pt-4 border-t border-[var(--clr-rule)]"),"data-testid":`message-${e.role}-${e.id}`,children:[t.jsx("div",{className:le("h-9 w-9 rounded-full flex items-center justify-center flex-shrink-0 shadow-sm",e.role==="user"?"bg-[var(--clr-navy)] text-white":"bg-white border border-[var(--clr-rule)] text-[var(--clr-navy)]"),children:e.role==="user"?t.jsx(Bs,{className:"h-4 w-4"}):t.jsx(jt,{className:"h-4 w-4"})}),t.jsxs("div",{className:"space-y-2 flex-1 min-w-0",children:[e.role==="user"&&(e.userEmail||e.userName)&&t.jsx("div",{className:"text-xs text-[var(--clr-ink-3)] font-medium mb-1",children:e.userName||e.userEmail}),e.attachments&&e.attachments.length>0&&t.jsx("div",{className:"flex flex-wrap gap-2","data-testid":`attachments-${e.id}`,children:e.attachments.map((r,n)=>{const o=r.name.split(".").pop()?.toLowerCase()||"",a=o==="pdf",p=o==="docx"||o==="doc",l=["csv","xls","xlsx"].includes(o);return t.jsxs("div",{className:le("flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs font-medium border",a?"bg-[var(--clr-shell-100)] text-[var(--clr-shell-700)] border-[var(--clr-shell-300)]":p?"bg-[var(--clr-sky-050)] text-[var(--clr-navy)] border-[var(--clr-sky-300)]":l?"bg-[var(--clr-moss-100)] text-[var(--clr-moss-700)] border-[var(--clr-moss-300)]":"bg-[var(--clr-pearl-100)] text-[var(--clr-ink-2)] border-[var(--clr-rule)]"),"data-testid":`attachment-${e.id}-${n}`,children:[l?t.jsx(Ue,{className:"h-3.5 w-3.5"}):t.jsx(Oe,{className:"h-3.5 w-3.5"}),t.jsx("span",{children:r.name}),r.size>0&&t.jsxs("span",{className:"opacity-60",children:["(",(r.size/1024).toFixed(1)," KB)"]})]},n)})}),e.isStreaming&&e.toolActivity&&e.toolActivity.length>0&&t.jsxs("div",{className:"bg-[var(--clr-pearl-100)] border border-[var(--clr-rule)] rounded-lg p-2 text-xs font-mono",children:[t.jsxs("div",{className:"flex items-center gap-2 text-[var(--clr-ink-3)] mb-2",children:[t.jsx(Ge,{className:"h-3 w-3 animate-spin"}),t.jsx("span",{className:"font-medium",children:"Agent Activity"})]}),t.jsx("div",{className:"space-y-1 max-h-32 overflow-y-auto",children:e.toolActivity.map((r,n)=>t.jsx("div",{className:"text-[var(--clr-ink-2)] truncate",children:r},n))})]}),t.jsxs("div",{className:le("rounded-2xl px-3 py-2 text-xs shadow-sm relative group pr-8",e.role==="user"?"bg-[var(--clr-navy)] text-white rounded-tr-none whitespace-pre-wrap":"bg-white border border-[var(--clr-rule)] text-[var(--clr-navy)] rounded-tl-none"),children:[e.role==="assistant"&&e.content&&!e.isStreaming?t.jsxs(t.Fragment,{children:[t.jsx(Xs,{content:e.content,onPurpleClick:()=>He(!0)}),e.incomplete&&t.jsxs("div",{className:"mt-3 rounded-md border border-[var(--clr-shell-300)] bg-[var(--clr-shell-100)] px-2.5 py-2 text-xs text-[var(--clr-shell-700)]",role:"alert",children:[t.jsxs("div",{className:"flex items-start gap-1.5",children:[t.jsx(bt,{className:"mt-0.5 h-3.5 w-3.5 shrink-0"}),t.jsxs("span",{children:[t.jsx("strong",{children:"Incomplete response."})," ",e.errorMessage||"The response ended unexpectedly."]})]}),e.retryPrompt&&t.jsxs(J,{type:"button",variant:"outline",size:"sm",className:"mt-2 h-7 border-[var(--clr-shell-300)] bg-white text-xs text-[var(--clr-shell-700)] hover:bg-[var(--clr-shell-100)]",onClick:()=>{zt(e.retryPrompt)},disabled:je||Q||V,children:[t.jsx(kt,{className:"mr-1.5 h-3 w-3"}),"Retry response"]})]}),t.jsx("span",{className:"absolute bottom-1 right-1",children:t.jsx(J,{variant:"ghost",size:"icon",className:"h-[18px] w-[18px] opacity-100 transition-all duration-200 hover:bg-[var(--clr-shell)] hover:text-white flex items-center justify-center bg-white border border-[var(--clr-rule)] rounded-md",onClick:()=>cs(e.content,e.id),"data-tip":"Copy to clipboard",children:is===e.id?t.jsx(Ws,{className:"h-2 w-2 text-[var(--clr-moss-700)]"}):t.jsx(Ls,{className:"h-2 w-2 text-[var(--clr-ink-3)]"})})})]}):e.content&&!e.isStreaming?e.content:null,e.isStreaming&&(e.statusLog?.length||e.content)&&t.jsx("div",{className:"bg-[var(--clr-pearl-100)] border border-[var(--clr-rule)] rounded-lg p-2 max-h-48 overflow-y-auto",children:t.jsxs("div",{className:"space-y-0.5",children:[e.content&&t.jsx("div",{className:"space-y-1",children:t.jsxs("div",{className:"flex items-start gap-2 text-xs py-0.5 text-[var(--clr-sky-600)] font-medium",children:[t.jsx(Ge,{className:"h-3 w-3 animate-spin flex-shrink-0 mt-0.5"}),t.jsx("div",{className:"space-y-0.5",children:e.content.split(new RegExp("(?<=\\.)\\s+")).filter(
131Boolean).map((r,n)=>t.jsx("div",{children:r.trim()},n))})]})}),e.statusLog&&[...e.statusLog].reverse().map((r,n)=>t.jsxs("div",{className:le("flex items-center gap-2 text-xs py-0.5",!e.content&&n===0?"text-[var(--clr-sky-600)] font-medium":"text-[var(--clr-ink-3)]"),children:[!e.content&&n===0?t.jsx(Ge,{className:"h-3 w-3 animate-spin flex-shrink-0"}):t.jsx(ts,{className:"h-3 w-3 flex-shrink-0"}),t.jsx("span",{className:"truncate",children:r.message})]},n))]})}),e.isStreaming&&t.jsxs("div",{className:le("flex items-center gap-3 text-[var(--clr-ink-3)] flex-wrap",(e.content||e.statusLog&&e.statusLog.length>0)&&"mt-2 pt-2 border-t border-[var(--clr-rule)]"),children:[t.jsxs("div",{className:"flex items-center gap-1.5 bg-[var(--clr-sky-050)] border border-[var(--clr-sky-300)] px-2 py-0.5 rounded-full",children:[t.jsx("div",{className:"w-1.5 h-1.5 bg-[var(--clr-sky-600)] rounded-full animate-pulse"}),t.jsx("span",{className:"text-xs font-mono font-medium text-[var(--clr-navy)]",children:ws(as)})]}),t.jsxs("div",{className:"flex items-center gap-1.5 text-[var(--clr-ink-3)]",children:[t.jsx(Ge,{className:"h-3 w-3 animate-spin"}),t.jsx("span",{className:"text-xs font-medium",children:It||"Thinking..."})]}),t.jsxs("div",{className:"flex items-center gap-1.5 text-[var(--clr-ink-3)]",children:[t.jsx("span",{className:"text-[10px] font-mono uppercase tracking-wide",children:"Stage"}),t.jsx("span",{className:"text-xs font-medium text-[var(--clr-navy)]",children:rr(It||"Working")})]}),t.jsxs("div",{className:"flex items-center gap-1.5 text-[var(--clr-shell)]",children:[t.jsx(bt,{className:"h-3.5 w-3.5"}),t.jsx("span",{className:"text-xs font-medium",children:"You can navigate away - your report will keep processing. Stay for real-time updates."})]})]}),e.isStreaming&&e.content&&t.jsx("span",{className:"inline-block w-2 h-4 bg-[var(--clr-shell)]/50 animate-pulse ml-1"})]})]})]},e.id)),je&&!Je&&t.jsxs("div",{className:"flex gap-3 max-w-[85%]",children:[t.jsx("div",{className:"h-9 w-9 rounded-full bg-white border border-[var(--clr-rule)] text-[var(--clr-navy)] flex items-center justify-center flex-shrink-0 shadow-sm",children:t.jsx(jt,{className:"h-4 w-4"})}),t.jsxs("div",{className:"bg-white border border-[var(--clr-rule)] rounded-2xl rounded-tl-none px-4 py-3 flex items-center gap-1.5 shadow-sm",children:[t.jsx("span",{className:"w-2 h-2 bg-[var(--clr-shell)]/40 rounded-full animate-bounce [animation-delay:-0.3s]"}),t.jsx("span",{className:"w-2 h-2 bg-[var(--clr-shell)]/40 rounded-full animate-bounce [animation-delay:-0.15s]"}),t.jsx("span",{className:"w-2 h-2 bg-[var(--clr-shell)]/40 rounded-full animate-bounce"})]})]})]}),E.length>0&&!m&&!oe.trim()&&t.jsxs("div",{className:"mb-3 p-3 bg-[var(--clr-shell-100)] border border-[var(--clr-shell-300)] rounded-lg flex items-center gap-2 shadow-sm",children:[t.jsx(bt,{className:"h-5 w-5 text-[var(--clr-shell)] flex-shrink-0"}),t.jsxs("p",{className:"text-sm text-[var(--clr-shell-700)]",children:[t.jsxs("strong",{children:["Please ",me.length>0?"select a project":"enter a project name"," above"]})," ","before generating reports."]})]}),t.jsxs("div",{className:"mt-3 border-0 rounded-lg bg-white shadow-md p-3",children:[E.length>0&&!Q&&t.jsxs("div",{className:"mb-3 space-y-3",children:[t.jsx("div",{className:"flex flex-wrap gap-2",children:E.map((e,s)=>{const r=e.name.toLowerCase().split(".").pop(),n=r==="csv"||r==="xls"||r==="xlsx";return t.jsxs("div",{className:le("flex items-center gap-2 px-3 py-2 rounded-lg text-sm border",re?"bg-[var(--clr-navy)]/10 text-[var(--clr-navy)] border-[var(--clr-navy)]/20":"bg-[var(--clr-sky-050)] border-[var(--clr-sky-300)] text-[var(--clr-navy)]"),children:[n?t.jsx(Ue,{className:"h-4 w-4"}):t.jsx(Oe,{className:"h-4 w-4"}),t.jsx("span",{children:e.name}),t.jsxs("span",{className:"text-xs text-[var(--clr-ink-3)]",children:["(",(e.size/1024).toFixed(1)," KB)"]}),t.jsx("button",{onClick:()=>bs(s),className:"hover:bg-[var(--clr-shell)]/20 rounded p-0.5 transition-all duration-200 hover:brightness-110 hover:shadow-md active:scale-95","data-testid":`button-remove-file-${s}`,title:"Remove file",children:t.jsx(es,{className:"h-3 w-3"})})]},s)})}),E.some(e=>{const s=e.name.toLowerCase().split(".").pop();return s==="csv"||s==="xls"||s==="xlsx"})&&t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsxs("label",{htmlFor:"newReportMode",className:le("flex items-center gap-2 px-3 py-1.5 rounded-md cursor-pointer transition-colors shrink-0 border",re?"bg-[var(--clr-navy)]/10 border-[var(--clr-navy)]/30":"bg-[var(--clr-pearl-050)] border-[var(--clr-rule)] hover:bg-[var(--clr-pearl-100)]"),children:[t.jsx("input",{type:"checkbox",id:"newReportMode",checked:re,onChange:e=>Re(e.target.checked),className:"h-3.5 w-3.5 rounded border-[var(--clr-rule)] text-[var(--clr-navy)]","data-testid":"checkbox-new-report"}),t.jsx(Ue,{className:le("h-3.5 w-3.5",re?"text-[var(--clr-navy)]":"text-[var(--clr-ink-3)]")}),t.jsx("span",{className:le("text-xs font-medium",re?"text-[var(--clr-navy)]":"text-[var(--clr-ink-3)]"),children:"Generate M365 Report"})]}),!re&&t.jsx("span",{className:"text-xs text-[var(--clr-ink-3)]",children:"Files will be sent as chat context only"})]}),re&&t.jsxs(t.Fragment,{children:[t.jsx("textarea",{value:Me,onChange:e=>ot(e.target.value),placeholder:"Optional: Add specific instructions for the analysis (e.g., 'Focus on Teams licenses' or 'Compare with previous month')",className:"w-full px-3 py-2 text-sm bg-[var(--clr-pearl-050)] border border-[var(--clr-rule)] rounded-[10px] resize-none text-[var(--clr-navy)] placeholder:text-[var(--clr-ink-3)] focus:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--clr-shell)] focu
131s-visible:ring-offset-0",rows:2,"data-testid":"input-upload-prompt"}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsxs(J,{type:"button",variant:"outline",size:"sm",onClick:()=>we.current?.click(),"data-testid":"button-add-more-files",title:"Add more files to analyze",className:"rounded-full border-[var(--clr-navy)] text-[var(--clr-navy)] hover:bg-[var(--clr-navy)] hover:text-white transition-all duration-200 active:scale-95",children:[t.jsx(St,{className:"h-4 w-4 mr-1"}),"Add More Files"]}),t.jsxs(J,{onClick:yt,disabled:!m&&!oe.trim(),"data-testid":"button-generate-reports",title:"Generate analysis reports from uploaded files",className:"rounded-full bg-[var(--clr-shell)] hover:bg-[var(--clr-shell-700)] text-white transition-all duration-200 active:scale-95",children:["Generate Reports (",E.length," file",E.length>1?"s":"",")"]})]})]})]}),t.jsxs("form",{className:"flex items-center gap-2 pt-2",onSubmit:e=>{e.preventDefault(),E.length>0&&re?yt():zt()},children:[t.jsx("input",{ref:we,type:"file",accept:".csv,.xls,.xlsx,.txt,.pdf,.docx",onChange:ys,className:"hidden",multiple:!0,"data-testid":"file-input"}),t.jsx(qt,{children:t.jsxs(Ht,{children:[t.jsx(Jt,{asChild:!0,children:t.jsx(J,{type:"button",variant:E.length>0?"default":"ghost",size:"icon",onClick:()=>we.current?.click(),disabled:je||Q||V,className:le("shrink-0 h-9 w-9 rounded-full transition-all duration-200 hover:brightness-110 hover:shadow-md active:scale-95","bg-[var(--clr-shell)] text-white hover:bg-[var(--clr-shell-700)] shadow-sm"),"data-testid":"button-attach-file",children:t.jsx(zs,{className:"h-4 w-4"})})}),t.jsx(Yt,{side:"top",children:t.jsx("p",{children:V?"Subscribe to upload files":z&&q?"Attach files to chat (CSV, Excel, PDF, Word)":"Attach files (CSV, Excel, PDF, Word)"})})]})}),z&&q&&t.jsxs(t.Fragment,{children:[t.jsxs("label",{htmlFor:"includeSourceData",className:"flex items-center gap-1.5 px-2 py-1 rounded-md bg-[var(--clr-sky-050)] border border-[var(--clr-sky-300)] cursor-pointer hover:bg-[var(--clr-sky-050)]/80 shrink-0",title:"Include original source CSV/Excel data files for raw data analysis",children:[t.jsx("input",{type:"checkbox",id:"includeSourceData",checked:Ct,onChange:e=>At(e.target.checked),className:"h-3.5 w-3.5 rounded border-[var(--clr-sky-300)] text-[var(--clr-navy)]","data-testid":"checkbox-include-source-data"}),t.jsx(Ue,{className:"h-3.5 w-3.5 text-[var(--clr-navy)]"}),t.jsx("span",{className:"text-xs text-[var(--clr-navy)]",children:"+Source Data"})]}),xe>0&&t.jsxs("button",{type:"button",onClick:()=>ft(e=>!e),className:"flex items-center gap-1 px-2 py-1 rounded-md bg-[var(--clr-moss-100)] border border-[var(--clr-moss-300)] shrink-0 hover:bg-[var(--clr-moss-100)]/80 transition-colors",title:`${xe} file(s) available in this project — click to view`,"data-testid":"button-toggle-project-files",children:[t.jsx(Kt,{className:"h-3.5 w-3.5 text-[var(--clr-moss-700)]"}),t.jsxs("span",{className:"text-xs text-[var(--clr-moss-700)]",children:[xe," files"]})]})]}),t.jsx(Qt,{placeholder:it?"Type your response...":E.length>0&&re?`${E.length} file(s) ready — click Generate Reports`:E.length>0?`Type a message about your ${E.length} file(s)...`:"Ask a question...",value:at,onChange:e=>nt(e.target.value),className:"flex-1 h-9 bg-[var(--clr-pearl-050)] border-[var(--clr-rule)] rounded-[10px] text-[var(--clr-navy)] placeholder:text-[var(--clr-ink-3)] focus-visible:ring-[3px] focus-visible:ring-[var(--clr-shell)] focus-visible:ring-offset-0",disabled:je||Q||E.length>0&&re||V,"data-testid":"input-message"}),!We&&me.length>0?t.jsxs("div",{className:"flex items-center gap-1 shrink-0",children:[t.jsxs(qs,{value:m,onValueChange:de,disabled:qe||Q,children:[t.jsx(Hs,{className:"h-9 w-[160px] text-xs bg-[var(--clr-pearl-050)] border-[var(--clr-rule)] rounded-[10px] text-[var(--clr-navy)] focus:ring-[3px] focus:ring-[var(--clr-shell)] focus:ring-offset-0",children:t.jsx(Js,{placeholder:qe?"Loading...":"Select project"})}),t.jsx(Ys,{children:me.map(e=>t.jsx(Ks,{value:e.id,className:"text-xs",children:e.name},e.id))})]}),t.jsx(J,{type:"button",variant:"ghost",size:"icon",className:"h-9 w-9 transition-all duration-200 hover:brightness-110 hover:shadow-md active:scale-95",onClick:()=>Ne(!0),disabled:Q,title:"Add new project",children:t.jsx(St,{className:"h-4 w-4"})})]}):t.jsxs("div",{className:"flex items-center gap-1 shrink-0",children:[t.jsx(Qt,{placeholder:"Project name",value:oe,onChange:e=>Se(e.target.value),disabled:Q,className:"h-9 w-[140px] text-xs bg-[var(--clr-pearl-050)] border-[var(--clr-rule)] rounded-[10px] text-[var(--clr-navy)] placeholder:text-[var(--clr-ink-3)] focus-visible:ring-[3px] focus-visible:ring-[var(--clr-shell)] focu
131s-visible:ring-offset-0",onKeyDown:e=>{e.key==="Enter"&&(e.preventDefault(),Lt())}}),t.jsx(J,{type:"button",size:"sm",className:"h-9 rounded-full bg-[var(--clr-navy)] text-white hover:bg-[var(--clr-navy-600)] transition-all duration-200 active:scale-95",onClick:Lt,disabled:Q||!oe.trim(),title:"Save new project and start analysis",children:"Save"}),me.length>0&&t.jsx(J,{type:"button",variant:"ghost",size:"sm",className:"h-9 transition-all duration-200 hover:brightness-110 hover:shadow-md active:scale-95",onClick:()=>{Ne(!1),Se("")},disabled:Q,title:"Cancel adding new project",children:"Cancel"})]}),je||Q?t.jsx(J,{type:"button",size:"icon",onClick:ks,className:"shrink-0 h-9 w-9 rounded-full bg-[var(--clr-shell)] hover:bg-[var(--clr-shell-700)] text-white transition-all duration-200 hover:shadow-md active:scale-95","data-testid":"button-stop",title:"Stop generation","aria-label":"Stop generation",children:t.jsx(tr,{className:"h-4 w-4 fill-current"})}):t.jsx(J,{type:"submit",size:"icon",disabled:!at.trim()&&E.length===0||V,className:"shrink-0 h-9 w-9 rounded-full bg-[var(--clr-navy)] hover:bg-[var(--clr-navy-600)] text-white transition-all duration-200 active:scale-95","data-testid":"button-send",title:V?"Subscribe to use the AI assistant":"Send message","aria-label":V?"Subscribe to use the AI assistant":"Send message",children:t.jsx(Gs,{className:"h-4 w-4"})})]})]})]}),ns&&q&&q.files?.length>0&&t.jsx(Qs,{reportId:q.reportId,wordFilename:q.files.find(e=>e.type==="word")?.filename,excelFilename:q.files.find(e=>e.type==="excel")?.filename,wordAvailable:q.files.some(e=>e.type==="word"),excelAvailable:q.files.some(e=>e.type==="excel"),customerName:q.customerName,onClose:()=>He(!1)})]})}export{Fr as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.