arff-instructor / state.js
Superboss1976's picture
Build AM Risk academy with Prof, persistent progress and instructor review desk
8896856 verified
Raw History Blame Contribute Delete
2.77 kB
import { lessons } from './lessons.js';
export const STORAGE_KEY='am-risk-academy-v2';
export const freshSession=()=>({started:false,index:0,answers:[],ended:false});
const empty=()=>({version:2,learner:'Learner',sessions:lessons.map(freshSession),history:[],reviewNotes:'',updated:null});
let storageOK=true;
function cleanSession(value,lesson){
if(!value||typeof value!=='object')return freshSession();
const answers=[];
for(let i=0;i<Math.min(value.answers?.length||0,lesson.steps.length);i++){
const choice=value.answers[i]?.choice;
if(!Number.isInteger(choice)||choice<0||choice>=lesson.steps[i].options.length)break;
answers.push({choice,correct:lesson.steps[i].options[choice].correct});
}
const ended=value.ended===true&&answers.length===lesson.steps.length;
return {started:!!value.started||answers.length>0,index:Math.min(Math.max(Number.isInteger(value.index)?value.index:0,0),answers.length,lesson.steps.length-1),answers,ended};
}
export function readState(){
const state=empty();
try{const raw=JSON.parse(localStorage.getItem(STORAGE_KEY)||'null');if(!raw||raw.version!==2)return state;
state.learner=typeof raw.learner==='string'?raw.learner.slice(0,50):'Learner';
state.sessions=lessons.map((l,i)=>cleanSession(raw.sessions?.[i],l));
state.history=Array.isArray(raw.history)?raw.history.filter(x=>Number.isInteger(x.lesson)&&x.lesson>=0&&x.lesson<lessons.length&&Number.isInteger(x.score)&&x.score>=0&&x.score<=3&&typeof x.date==='string').slice(-40):[];
state.reviewNotes=typeof raw.reviewNotes==='string'?raw.reviewNotes.slice(0,6000):'';
state.updated=typeof raw.updated==='string'?raw.updated:null;
}catch{storageOK=false;}return state;
}
export function saveState(state){try{state.updated=new Date().toISOString();localStorage.setItem(STORAGE_KEY,JSON.stringify(state));storageOK=true;window.dispatchEvent(new CustomEvent('academy-progress'));return true;}catch{storageOK=false;return false;}}
export function storageAvailable(){return storageOK;}
export function saveSessions(sessions,completedLesson=null){const state=readState();state.sessions=sessions.map((s,i)=>cleanSession(s,lessons[i]));if(completedLesson!==null){const s=state.sessions[completedLesson];state.history.push({lesson:completedLesson,score:s.answers.filter(a=>a.correct).length,date:new Date().toISOString()});state.history=state.history.slice(-40);}saveState(state);return state;}
export function summary(state=readState()){const complete=state.sessions.filter(s=>s.ended).length,decisions=state.sessions.reduce((n,s)=>n+s.answers.length,0),correct=state.sessions.reduce((n,s)=>n+s.answers.filter(a=>a.correct).length,0);return{complete,decisions,correct,percent:Math.round(complete/lessons.length*100),next:Math.max(0,state.sessions.findIndex(s=>!s.ended))};}