mathpulse-ai / assets /CompetencyRadarChart-DSeHeNCR.js
github-actions[bot]
🚀 Auto-deploy frontend from GitHub (9405693)
58e954f
import{r as y,j as n}from"./vendor-react-B4AQeu28.js";import{g as D,ad as _,u as B,m as U,B as W,R as H,b as K}from"./index-BOwCDUuj.js";import{collection as T,getDocs as P,query as $,orderBy as J,limit as V,Timestamp as G,getDoc as X,doc as Y}from"./vendor-firebase-firestore-CNv3xXal.js";import{g as Z,s as ee}from"./subjects-D7EDkved.js";import{S as te}from"./sparkles-CxtI97fU.js";import{R as se,f as ne,g as oe,h as re,i as ie,j as ae,T as ce}from"./vendor-charts-CAmziV1y.js";import"./vendor-firebase-core-D7GXCeNu.js";import"./vendor-firebase-auth-T_wvTOvf.js";import"./vendor-firebase-storage-Bi7H_66d.js";import"./vendor-firebase-analytics-rHarj5hR.js";import"./vendor-radix-C5nwdvGr.js";import"./vendor-markdown-yqZQ_iQm.js";import"./chart-column-BFl6enfG.js";const F=["#1FA7E1","#9956DE","#75D06A","#FFB356","#7274ED","#FF8B8B","#6ED1CF","#FB96BB"];async function le(o){try{const s=T(D,"users",o,"quizResults"),u=$(s,J("timestamp","desc"),V(500)),c=await P(u);return c.empty?O(o):c.docs.map(t=>{const e=t.data();return{quizId:t.id,moduleId:e.moduleId||"",subjectId:e.subjectId||"",score:typeof e.score=="number"?e.score:0,totalQuestions:e.totalQuestions||0,correctAnswers:e.correctAnswers||0,questionType:e.questionType||"multiple_choice",timestamp:e.timestamp instanceof G||e.timestamp?.toDate?e.timestamp.toDate():new Date,timeSpent:e.timeSpent||0}})}catch(s){return console.error("[competencyService] fetchQuizResults failed, falling back:",s),O(o)}}async function O(o){const s=await X(Y(D,"progress",o));return s.exists()?(s.data().quizAttempts||[]).map((t,e)=>({quizId:String(t.quizId||`attempt-${e}`),moduleId:String(t.moduleId||t.quizId||""),subjectId:String(t.subjectId||""),score:typeof t.score=="number"?t.score:0,totalQuestions:t.totalQuestions||0,correctAnswers:typeof t.correctAnswers=="number"?t.correctAnswers:0,questionType:t.questionType||"multiple_choice",timestamp:t.completedAt?new Date(t.completedAt):new Date,timeSpent:t.timeSpent||0})):[]}async function ue(o){try{const s=T(D,"users",o,"moduleProgress"),u=await P(s);if(u.empty)return{};const c={};return u.forEach(t=>{const e=t.data();c[t.id]={moduleId:t.id,subjectId:e.subjectId||"",sessionsCompleted:e.sessionsCompleted||0,lastActive:e.lastActive?e.lastActive instanceof G?e.lastActive.toDate():new Date(e.lastActive):new Date,moduleTitle:e.moduleTitle||t.id,lessonsCompleted:e.lessonsCompleted||[],quizzesCompleted:e.quizzesCompleted||[]}}),c}catch(s){return console.error("[competencyService] fetchModuleProgress failed:",s),{}}}function Q(o,s,u,c){const t=s.filter(d=>d.moduleId===o),e=t.length>0?t.reduce((d,b)=>d+b.score,0)/t.length:0,p=["identification","multiple_choice"],i=t.filter(d=>p.includes(d.questionType)),a=i.length>0?i.reduce((d,b)=>d+b.score,0)/i.length:e*.9,l=["word_problem","equation_based","enumeration"],r=t.filter(d=>l.includes(d.questionType)),m=r.length>0?r.reduce((d,b)=>d+b.score,0)/r.length:e*.85,f=u,h=f?.lessonsCompleted?.length||0,z=f?.quizzesCompleted?.length||0,E=f?.sessionsCompleted||0,g=c.find(d=>d.id===o),x=g?.lessons?.length||1,C=g?.quizzes?.length||1,A=Math.min(100,h/x*100),v=Math.min(100,z/C*100),j=Math.min(100,(A+v+E*5)/2.5);if(t.length<2){const d=j>50?60+j*.3:j*.8;return{overallMastery:e,conceptGrasp:a,application:m,engagement:j,consistency:Math.min(100,d)}}const w=t.map(d=>d.score),M=w.reduce((d,b)=>d+b,0)/w.length,k=w.reduce((d,b)=>d+Math.pow(b-M,2),0)/w.length,q=Math.sqrt(k)/Math.max(M,1),R=Math.max(0,Math.min(100,(1-q)*100));return{overallMastery:Math.round(e),conceptGrasp:Math.round(a),application:Math.round(m),engagement:Math.round(j),consistency:Math.round(R)}}async function de(o){try{const s=T(D,"users",o,"competencyMatrix"),u=await P(s);if(u.empty)return{};const c={};return u.forEach(t=>{const e=t.data();c[t.id]={moduleId:t.id,moduleName:e.moduleName||t.id,overallMastery:e.overallMastery??0,conceptGrasp:e.conceptGrasp??0,application:e.application??0,engagement:e.engagement??0,consistency:e.consistency??0,computedAt:e.computedAt?.toDate?.()||new Date}}),c}catch(s){return console.error("[competencyService] fetchCachedCompetencyMatrix failed:",s),{}}}const L=[{key:"progress",label:"Overall Mastery"},{key:"concept",label:"Concept Grasp"},{key:"application",label:"Application"},{key:"engagement",label:"Engagement"},{key:"consistency",label:"Consistency"}];function me(o){const[s,u]=y.useState([]),[c,t]=y.useState([]),[e,p]=y.useState("N/A"),[i,a]=y.useState(!0),[l,r]=y.useState(null),[m,f]=y.useState(!1),h=y.useRef(0),z=y.useCallback(async(g,x)=>{if(x!==h.current)return;const A=Z(null)[0]??"gen-math",v=ee.find(d=>d.id===A)?.modules||[],j=v.map((d,b)=>({id:d.id,name:d.title.length>15?d.title.substring(0,15).trim()+"...":d.title,color:F[b%F.length]}));t(j);const w=await de(o);if(x!==h.current)return;if(Object.keys(w).length>0){pe(w,j,u,p),f(!1),a(!1);return}const M=await le(o),k=await ue(o);if(x!==h.current)return;let N=M,q=k;M.length===0&&g&&(N=he(g,v)),Object.keys(k).length===0&&g&&(q=ge(g));const R=N.length>0||Object.keys(q).length>0;f(!R),fe(N,q,j,v,u,p),a(!1)},[o]),E=y.useCallback(async()=>{if(!o){a(!1);return}const g=++h.current;a(!0),r(null);try{const x=await new Promise(C=>{const A=_(o,v=>{A(),C(v)})});if(g!==h.current)return;await z(x,g)}catch(x){console.error("[useCompetencyMatrix] Failed to load:",x),g===h.current&&(r("Failed to load competency data. Please try again."),a(!1))}},[o,z]);return y.useEffect(()=>{if(!o)return;const g=_(o,x=>{const C=++h.current;z(x,C)});return()=>g()},[o,z]),{data:s,modulesList:c,topModule:e,loading:i,error:l,isEmpty:m,refresh:E}}function pe(o,s,u,c){let t=-1,e="N/A";const p=L.map(i=>{const a={metric:i.label,fullMark:100};return s.forEach(l=>{const r=o[l.id];r&&(i.key==="progress"?a[l.id]=r.overallMastery:i.key==="concept"?a[l.id]=r.conceptGrasp:i.key==="application"?a[l.id]=r.application:i.key==="engagement"?a[l.id]=r.engagement:i.key==="consistency"&&(a[l.id]=r.consistency))}),a});s.forEach(i=>{const a=o[i.id];if(a){const l=(a.overallMastery+a.conceptGrasp+a.application+a.engagement+a.consistency)/5;l>t&&(t=l,e=i.name)}}),c(e),u(p)}function fe(o,s,u,c,t,e){let p=-1,i="N/A";const a=L.map(l=>{const r={metric:l.label,fullMark:100};return c.forEach(m=>{const f=s[m.id],h=Q(m.id,o,f??{moduleId:m.id,sessionsCompleted:0,moduleTitle:m.title,lessonsCompleted:[],quizzesCompleted:[]},c);l.key==="progress"?r[m.id]=h.overallMastery:l.key==="concept"?r[m.id]=h.conceptGrasp:l.key==="application"?r[m.id]=h.application:l.key==="engagement"?r[m.id]=h.engagement:l.key==="consistency"&&(r[m.id]=h.consistency)}),r});c.forEach(l=>{const r=s[l.id],m=Q(l.id,o,r??{moduleId:l.id,sessionsCompleted:0,moduleTitle:l.title,lessonsCompleted:[],quizzesCompleted:[]},c),f=(m.overallMastery+m.conceptGrasp+m.application+m.engagement+m.consistency)/5;f>p&&(p=f,i=u.find(h=>h.id===l.id)?.name||"N/A")}),e(i),t(a)}const S={"gm-q1-functions-graphs":"gm-1","gm-q1-patterns-sequences-series":"gm-1","gm-q1-business-finance":"gm-2","gm-q1-financial-application-sequences-series":"gm-2","gm-q1-bf":"gm-2","gm-q1-logic":"gm-3"};function I(o){if(S[o])return S[o];for(const[u,c]of Object.entries(S))if(o.startsWith(u))return c;const s=o.toLowerCase();return s.includes("function")||s.includes("graph")||s.includes("pattern")||s.includes("sequence")?"gm-1":s.includes("business")||s.includes("finance")||s.includes("interest")||s.includes("annuit")?"gm-2":s.includes("logic")||s.includes("proposition")||s.includes("truth")?"gm-3":o}function he(o,s){const u=o.quizAttempts||[];if(u.length===0)return[];const c={};if(o.subjects){for(const e of Object.values(o.subjects))if(e.modulesProgress)for(const[p,i]of Object.entries(e.modulesProgress)){const a=I(p);for(const l of i.quizzesCompleted||[])c[l]=a}}const t=Object.keys(c).length>0;return u.map((e,p)=>{let i=c[e.quizId]||"";return i||(i=I(e.quizId),i===e.quizId&&(i=!t&&s.length>0?s[p%s.length].id:"")),{quizId:e.quizId,moduleId:i,subjectId:"",score:e.score,totalQuestions:e.answers?.length||0,correctAnswers:e.answers?.filter(a=>a.isCorrect).length||0,questionType:"multiple_choice",timestamp:e.completedAt instanceof Date?e.completedAt:new Date(e.completedAt),timeSpent:e.timeSpent||0}})}function ge(o){const s={};if(!o.subjects)return s;for(const[u,c]of Object.entries(o.subjects))if(c.modulesProgress)for(const[t,e]of Object.entries(c.modulesProgress)){const p=I(t);s[p]||(s[p]={moduleId:p,subjectId:u,sessionsCompleted:0,lastActive:new Date,moduleTitle:p,lessonsCompleted:[],quizzesCompleted:[]});const i=s[p];i.sessionsCompleted+=(e.lessonsCompleted?.length||0)+(e.quizzesCompleted?.length||0),i.lessonsCompleted.push(...e.lessonsCompleted||[]),i.quizzesCompleted.push(...e.quizzesCompleted||[]);const a=e.lastAccessedAt instanceof Date?e.lastAccessedAt:new Date(e.lastAccessedAt);a>i.lastActive&&(i.lastActive=a)}return s}const De=()=>{const{userProfile:o}=B(),{data:s,modulesList:u,topModule:c,loading:t,error:e,isEmpty:p,refresh:i}=me(o?.uid??""),[a,l]=y.useState(!1);return n.jsxs(U.div,{initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{duration:.5,ease:[.22,1,.36,1]},className:"relative overflow-hidden bg-white rounded-[16px] border border-slate-200 shadow-sm p-6 lg:p-8 group",onMouseEnter:()=>l(!0),onMouseLeave:()=>l(!1),children:[n.jsx("div",{className:"absolute -right-12 -bottom-12 w-32 h-32 bg-[#9956DE]/5 rounded-full transition-transform duration-500 group-hover:scale-[1.8] group-hover:-translate-y-4 group-hover:-translate-x-4"}),n.jsxs("div",{className:"flex flex-col sm:flex-row items-start sm:items-center justify-between mb-6 relative z-10 gap-4",children:[n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx("div",{className:"w-10 h-10 rounded-[12px] bg-gradient-to-br from-[#a855f7] to-[#9333ea] flex items-center justify-center shadow-[0_4px_12px_rgba(168,85,247,0.2)]",children:n.jsx(W,{size:20,className:"text-white"})}),n.jsxs("div",{children:[n.jsx("h3",{className:"text-[15px] font-bold text-slate-900",children:"Competency Matrix"}),n.jsx("p",{className:"text-[12px] text-slate-500",children:"Realtime performance across modules"})]})]}),n.jsxs("div",{className:"flex items-center gap-3",children:[!t&&!p&&!e&&n.jsxs("div",{className:"flex items-center gap-2 bg-[#f5f3ff] border border-[#e0e7ff] px-3 py-1.5 rounded-full",children:[n.jsx(te,{size:12,className:"text-amber-500"}),n.jsx("span",{className:"text-[11px] font-bold text-[#9956DE] uppercase tracking-wider",children:c})]}),!t&&n.jsx("button",{onClick:i,className:"w-8 h-8 rounded-full bg-white border border-slate-200 flex items-center justify-center hover:bg-[#f5f3ff] hover:border-[#9956DE]/30 transition-colors shadow-sm",title:"Refresh",children:n.jsx(H,{size:13,className:"text-slate-400"})})]})]}),n.jsx("div",{className:"h-[300px] sm:h-[360px] w-full relative z-10",children:t?n.jsx("div",{className:"w-full h-full flex items-center justify-center",children:n.jsxs("div",{className:"flex flex-col items-center gap-3",children:[n.jsx("div",{className:"w-8 h-8 border-4 border-[#9956DE]/20 border-t-[#9956DE] rounded-full animate-spin"}),n.jsx("p",{className:"text-[13px] text-slate-400 font-medium",children:"Analyzing skill vectors..."})]})}):p||e?n.jsxs("div",{className:"w-full h-full flex flex-col items-center justify-center gap-4",children:[n.jsx("div",{className:"w-14 h-14 rounded-[16px] bg-gradient-to-br from-slate-100 to-slate-50 flex items-center justify-center border border-slate-200",children:n.jsx(K,{size:24,className:"text-slate-400"})}),n.jsxs("div",{className:"text-center",children:[n.jsx("p",{className:"text-[13px] font-semibold text-slate-600 mb-1",children:e?"Unable to load competency data":"No activity yet"}),n.jsx("p",{className:"text-[12px] text-slate-400 max-w-[260px]",children:e||"Complete quizzes and lessons to see your competency matrix here."})]}),e&&n.jsx("button",{onClick:i,className:"px-4 py-2 text-[12px] font-semibold bg-[#9956DE] text-white rounded-full hover:bg-[#8b45d1] transition-colors shadow-md",children:"Try Again"})]}):n.jsx(se,{width:"100%",height:"100%",children:n.jsxs(ne,{cx:"50%",cy:"50%",outerRadius:a?"75%":"70%",data:s,children:[n.jsx(oe,{stroke:"#e2e8f0",strokeDasharray:"4 4",polarRadius:[20,40,60,80,100]}),n.jsx(re,{dataKey:"metric",tick:{fill:"#475569",fontSize:12,fontWeight:600}}),n.jsx(ie,{angle:90,domain:[0,100],tick:{fill:"#94a3b8",fontSize:10},tickCount:6,axisLine:!1}),u.map(r=>n.jsx(ae,{name:r.name,dataKey:r.id,stroke:r.color,strokeWidth:2,fill:r.color,fillOpacity:.3,dot:{r:3,fill:"#fff",stroke:r.color,strokeWidth:2},activeDot:{r:5,fill:r.color,stroke:"#fff",strokeWidth:2},isAnimationActive:!0,animationDuration:1500,animationEasing:"ease-out"},r.id)),n.jsx(ce,{cursor:!1,content:({active:r,payload:m})=>r&&m&&m.length?n.jsxs("div",{className:"bg-white/95 backdrop-blur-[12px] shadow-[0_8px_24px_rgba(0,0,0,0.12)] border border-slate-100 rounded-[12px] p-4",children:[n.jsx("p",{className:"font-bold text-slate-800 text-[13px] mb-2",children:m[0].payload.metric}),n.jsx("div",{className:"flex flex-col gap-1.5",children:m.map(f=>n.jsxs("div",{className:"flex items-center justify-between gap-4",children:[n.jsxs("div",{className:"flex items-center gap-2",children:[n.jsx("div",{className:"w-2 h-2 rounded-full",style:{backgroundColor:f.stroke}}),n.jsx("span",{className:"text-[11px] font-semibold text-slate-600 truncate max-w-[120px]",children:String(f.name)})]}),n.jsxs("span",{className:"text-[13px] font-bold",style:{color:f.stroke},children:[Math.round(f.value),"%"]})]},f.name))})]}):null})]})})}),!t&&!p&&!e&&u.length>0&&n.jsx("div",{className:"flex flex-wrap justify-center items-center gap-3 mt-4 relative z-10",children:u.map(r=>n.jsxs("div",{className:"flex items-center gap-1.5 bg-slate-50 px-2.5 py-1 rounded-full border border-slate-100",children:[n.jsx("div",{className:"w-2 h-2 rounded-full",style:{backgroundColor:r.color}}),n.jsx("span",{className:"text-[10px] font-bold text-slate-600 uppercase tracking-wider",children:r.name.length>15?r.name.substring(0,12)+"...":r.name})]},r.id))})]})};export{De as CompetencyRadarChart};