=== FRONTEND FILES CONTAINING MFA === public/assets/bcos-s25-3.js === FRONTEND MFA CODE WITH CONTEXT === public/assets/bcos-s25-3.js-342- public/assets/bcos-s25-3.js-343-async function viewProjects(page){page.innerHTML=`${pageHead('Projects & Engagements','Management consulting, strategic project sourcing, transformation and business development.',has('projects.create')?button('New project','primary','id="new-project"','plus'):'','Delivery')}
${icon('search')}
${loading(4)}
`;$('#project-q').oninput=debounce(loadProjects,350);$('#project-status').onchange=loadProjects;$('#new-project')?.addEventListener('click',newProjectModal);await loadProjects()} public/assets/bcos-s25-3.js-344-async function loadProjects(){const el=$('#project-results');el.innerHTML=loading(4);try{const q=$('#project-q')?.value.trim()||'',status=$('#project-status')?.value||'';const d=await api(`/api/projects?q=${encodeURIComponent(q)}&status=${encodeURIComponent(status)}`);const items=d.projects||[];el.innerHTML=items.length?`
${items.map(p=>`
${esc(p.reference_no)}${statusPill(p.health||p.status)}

${esc(p.name)}

${esc(p.client_name||'Client not shown')}

${fmtNum(p.progress_percent)}%complete${fmtNum(p.overdue_tasks)}overdue${fmtNum(p.high_risks)}high risks
`).join('')}
`:emptyState('No projects found','Create an engagement or adjust your filters.',has('projects.create')?'':'','folder');$$('[data-project-open]').forEach(b=>b.onclick=()=>openProject(b.dataset.projectOpen));$$('[data-finance-project]').forEach(b=>b.onclick=()=>navigate(`/finance?project_id=${b.dataset.financeProject}`));$('#empty-new-project')?.addEventListener('click',newProjectModal)}catch(ex){showError(ex,el)}} public/assets/bcos-s25-3.js-345-async function newProjectModal(){const m=openModal('Create project','Start a controlled consulting or strategic sourcing engagement.',loading(4),true);try{const [clients,admin]=await Promise.all([api('/api/clients?status=ACTIVE&limit=100'),api('/api/administration/dashboard')]);m.querySelector('.modal-body').innerHTML=`
`;m.querySelector('[data-close]').onclick=closeModal;m.querySelector('#project-form').onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,btn=f.querySelector('[type="submit"]');btn.disabled=true;const body=Object.fromEntries(new FormData(f).entries());body.status='DRAFT';body.health='NOT_ASSESSED';try{const r=await api('/api/projects',{method:'POST',body:JSON.stringify(body)});closeModal();toast('Project created',r.reference_no);await loadProjects()}catch(ex){toast('Project creation failed',ex.message,'error')}finally{btn.disabled=false}}}catch(ex){showError(ex,m.querySelector('.modal-body'))}} public/assets/bcos-s25-3.js-346-async function openProject(id){const m=openModal('Project record','Loading the governed project file…',loading(5),true);try{const p=await api(`/api/projects/show?id=${encodeURIComponent(id)}`);m.querySelector('.modal-body').innerHTML=`

${esc(p.reference_no)}

${esc(p.name)}

${esc(p.client_name||'')} · ${esc(p.project_type_code||'Project')}

${metricCard('Milestones',(p.milestones||[]).length,'Project plan','calendar')}${metricCard('Tasks',(p.tasks||[]).length,'Delivery work','task')}${metricCard('Deliverables',(p.deliverables||[]).length,'Outputs','file')}${metricCard('Risks',(p.risks||[]).length,'Risk register','risk')}${metricCard('Decisions',(p.decisions||[]).length,'Decision record','check')}

Project purpose

Approved engagement description

${statusPill(p.status)}

${esc(p.description||'No description has been recorded.')}

Milestones

Planned and completed delivery points

${(p.milestones||[]).length?p.milestones.map(x=>`
${esc(x.name)}${esc(x.reference_no||'')} · Due ${fmtDate(x.due_date)}
${statusPill(x.status)}
`).join(''):emptyState('No milestones recorded','Add milestones to make progress measurable.','','calendar')}
`;m.querySelector('[data-modal-finance]').onclick=()=>{closeModal();navigate(`/finance?project_id=${id}`)};m.querySelector('[data-modal-document]').onclick=()=>{closeModal();navigate(`/documents?project_id=${id}`)}}catch(ex){showError(ex,m.querySelector('.modal-body'))}} public/assets/bcos-s25-3.js-347- public/assets/bcos-s25-3.js-348-async function viewFinance(page){const projectsData=await api('/api/projects?status=ACTIVE');const projects=projectsData.projects||[];const params=new URLSearchParams(location.search);let projectId=params.get('project_id')||projects[0]?.id||'';page.innerHTML=`${pageHead('Finance & Project Funds','Track approved budgets, client funds, expenditure, invoices, balances and potential refunds.',projectId&&has('finance.manage_invoices')?button('Create invoice','primary','id="new-invoice"','plus'):'','Commercial Stewardship')}
${projectId?loading(5):emptyState('Select a project','Choose a project to view its approved financial position.','','money')}
`;$('#finance-project').onchange=e=>{projectId=e.target.value;history.replaceState({},'',`/finance${projectId?`?project_id=${encodeURIComponent(projectId)}`:''}`);loadFinance(projectId)};$('#new-invoice')?.addEventListener('click',()=>newInvoiceModal(projectId));$('#record-funding').onclick=()=>recordFundingModal(projectId);if(projectId)await loadFinance(projectId)} public/assets/bcos-s25-3.js-349-async function loadFinance(projectId){const el=$('#finance-view');if(!projectId){el.innerHTML=emptyState('Select a project','Choose a project to view its finances.','','money');return}el.innerHTML=loading(5);try{const d=await api(`/api/finance/project?project_id=${encodeURIComponent(projectId)}`);const s=d.summary||{},c=s.base_currency||'USD',budget=num(s.approved_budget),spent=num(s.approved_expenditure),funds=num(s.client_funds_received),committed=Math.max(0,num(s.approved_commitments||s.committed_expenditure||0)),available=num(s.available_balance);const spentPct=budget?Math.min(100,spent/budget*100):0,committedPct=budget?Math.min(100,committed/budget*100):0,availablePct=budget?Math.max(0,Math.min(100,available/budget*100)):0;el.innerHTML=`
${metricCard('Approved budget',fmtMoney(budget,c),'Approved financial envelope','money')}${metricCard('Funds received',fmtMoney(funds,c),'Cash received from client','download')}${metricCard('Approved expenditure',fmtMoney(spent,c),'Paid or recognised','chart')}${metricCard('Available balance',fmtMoney(available,c),'Uncommitted balance','money')}${metricCard('Potential refund',fmtMoney(s.potential_refund,c),'Subject to reconciliation','refresh')}${metricCard('Outstanding funding',fmtMoney(s.outstanding_funding,c),'Still required','clock')}

Project fund utilisation

Spent, committed and available amounts must remain distinct

${spentPct.toFixed(1)}% spent
Spent ${fmtMoney(spent,c)}Committed ${fmtMoney(committed,c)}Available ${fmtMoney(available,c)}

Invoices

Issued, paid and outstanding

${ledgerRows(d.invoices,'total_amount',c,'No invoices recorded')}

Recent expenses

Approved and submitted expenditure

${ledgerRows(d.expenses,'amount',c,'No expenses recorded')}
`}catch(ex){showError(ex,el)}} public/assets/bcos-s25-3.js-350-function ledgerRows(rows,field,c,empty){return (rows||[]).length?rows.slice(0,10).map(r=>`
${esc(r.reference_no||r.description||'Record')}${esc(r.status||'')} · ${fmtDate(r.issue_date||r.transaction_date||r.received_date||r.created_at)}
${fmtMoney(r[field],r.currency_code||c)}
`).join(''):emptyState(empty,'Records will appear after an authorised transaction is created.','','money')} public/assets/bcos-s25-3.js-351-async function newInvoiceModal(projectId){if(!projectId){toast('Select a project','Choose a project before creating an invoice.','error');return}const p=await api(`/api/projects/show?id=${encodeURIComponent(projectId)}`);const m=openModal('Create draft invoice','The invoice remains a draft until authorised and issued.',`
Tax must not be added unless the issuing entity’s tax and VAT status has been verified for this transaction.
`);m.querySelector('[data-close]').onclick=closeModal;m.querySelector('#invoice-form').onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,fd=Object.fromEntries(new FormData(f).entries()),btn=f.querySelector('[type="submit"]');btn.disabled=true;const body={project_id:projectId,client_organization_id:p.client_organization_id,currency_code:fd.currency_code,issue_date:fd.issue_date,due_date:fd.due_date,status:'DRAFT',purchase_order_reference:fd.purchase_order_reference||null,notes:fd.notes||null,lines:[{description:fd.description,quantity:num(fd.quantity),unit_price:num(fd.unit_price),tax_rate:num(fd.tax_rate)}]};try{const r=await api('/api/finance/invoices',{method:'POST',body:JSON.stringify(body)});closeModal();toast('Draft invoice created',`${r.reference_no} · ${fmtMoney(r.total_amount,fd.currency_code)}`);await loadFinance(projectId)}catch(ex){toast('Invoice creation failed',ex.message,'error')}finally{btn.disabled=false}}} public/assets/bcos-s25-3.js-352-async function recordFundingModal(projectId){if(!projectId){toast('Select a project','Choose a project before recording funding.','error');return}const p=await api(`/api/projects/show?id=${encodeURIComponent(projectId)}`);const m=openModal('Record client funding','Record only funds confirmed by reliable evidence.',`
`);m.querySelector('[data-close]').onclick=closeModal;m.querySelector('#funding-form').onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,body=Object.fromEntries(new FormData(f).entries()),btn=f.querySelector('[type="submit"]');body.project_id=projectId;body.client_organization_id=p.client_organization_id;body.status='PENDING';btn.disabled=true;try{const r=await api('/api/finance/funding',{method:'POST',body:JSON.stringify(body)});closeModal();toast('Funding recorded',r.reference_no);await loadFinance(projectId)}catch(ex){toast('Funding record failed',ex.message,'error')}finally{btn.disabled=false}}} public/assets/bcos-s25-3.js-353- public/assets/bcos-s25-3.js-354-async function viewDocuments(page){const params=new URLSearchParams(location.search);const selectedProject=params.get('project_id')||'';const [profiles,clients,projectsData]=await Promise.all([api('/api/administration/legal-profiles').catch(()=>[]),api('/api/clients?status=ACTIVE&limit=100').catch(()=>({clients:[]})),api('/api/projects?status=ACTIVE').catch(()=>({projects:[]}))]);const types=[['PROPOSAL','Proposal'],['QUOTATION','Quotation'],['PRO_FORMA_INVOICE','Pro Forma Invoice'],['INVOICE','Invoice'],['ENGAGEMENT_LETTER','Engagement Letter'],['SERVICE_AGREEMENT','Service Agreement'],['CONTRACT','Contract'],['NDA','NDA'],['NCNDA','NCNDA'],['PURCHASE_ORDER','Purchase Order'],['REPORT','Report'],['DOCUMENT','Other Document']];page.innerHTML=`${pageHead('Commercial Documents & Records Centre','Generate controlled, versioned documents with unique references and issuing-entity snapshots.','','Controlled Documents')}

Create controlled document

Every final document requires an authorised issuing entity

${types.map(([v,l],i)=>``).join('')}
Generated documents are drafts until reviewed and approved. The system preserves the issuing entity details used at creation time.
${emptyState('Document preview','Complete the form and create a controlled draft. The unique reference and preview will appear here.','','file')}
`; public/assets/bcos-s25-3.js-355- $$('[data-document-type]').forEach(b=>b.onclick=()=>{$$('[data-document-type]').forEach(x=>x.classList.remove('active'));b.classList.add('active');const f=$('#document-form');f.document_type.value=b.dataset.documentType;const label=types.find(x=>x[0]===b.dataset.documentType)?.[1]||'Controlled Document';f.title.value=defaultDocumentTitle(b.dataset.documentType,label)});$('#document-form').onsubmit=createDocument; public/assets/bcos-s25-3.js-356-} public/assets/bcos-s25-3.js-357-function defaultDocumentTitle(type,label){const map={NDA:'Mutual Non-Disclosure Agreement',NCNDA:'Non-Circumvention and Non-Disclosure Agreement',PRO_FORMA_INVOICE:'Pro Forma Invoice',INVOICE:'Commercial Invoice',ENGAGEMENT_LETTER:'Professional Engagement Letter',SERVICE_AGREEMENT:'Professional Services Agreement',CONTRACT:'Commercial Contract',PROPOSAL:'Professional Services Proposal'};return map[type]||label} public/assets/bcos-s25-3.js-358-async function createDocument(e){e.preventDefault();const f=e.currentTarget,btn=f.querySelector('[type="submit"]'),fd=Object.fromEntries(new FormData(f).entries());btn.disabled=true;btn.textContent='Creating controlled draft…';const content={purpose:fd.purpose||'To be completed during controlled review.',scope_and_deliverables:fd.scope||'To be completed during controlled review.',commercial_terms:fd.commercial_terms||'To be confirmed and approved.',confidentiality_and_non_circumvention:fd.confidentiality||'Use approved clauses from the controlled clause library.',governing_law_and_dispute_resolution:fd.governing_law||'Transaction-specific legal review required.',additional_terms:fd.additional_terms||'None recorded.',signatures:'Authorised signatories, names, titles and dates.'};const body={document_type:fd.document_type,title:fd.title,issuing_legal_entity_id:fd.issuing_legal_entity_id,jurisdiction_code:fd.jurisdiction_code,client_organization_id:fd.client_organization_id||null,project_id:fd.project_id||null,language_mode:fd.language_mode,classification:fd.classification,watermark_text:'DRAFT',content};try{const r=await api('/api/documents',{method:'POST',body:JSON.stringify(body)});const preview=$('#document-preview .paper');preview.innerHTML=`
${esc(r.reference)} · Version ${esc(r.version)}
Integrity hash: ${esc(r.hash)}
`;toast('Controlled draft created',r.reference)}catch(ex){toast('Document generation failed',ex.message,'error')}finally{btn.disabled=false;btn.innerHTML=`${icon('file')}Create controlled draft`}} public/assets/bcos-s25-3.js-359- public/assets/bcos-s25-3.js-360-async function viewReports(page){const [templates,runs]=await Promise.all([api('/api/reports/templates').catch(()=>[]),api('/api/reports/runs').catch(()=>[])]);page.innerHTML=`${pageHead('Reports & Executive Outputs','Generate governed PDF, editable DOCX and Excel outputs from verified BCOS records.',has('reports.generate')?button('Generate report','primary','id="generate-report"','plus'):'','Report Factory')}
${metricCard('Templates',templates.length,'Available report structures','file')}${metricCard('Recent runs',runs.length,'Generated outputs','chart')}${metricCard('PDF','Supported','Controlled output','download')}${metricCard('DOCX & XLSX','Supported','Editable and analytical','download')}

Recent reports

Controlled outputs, verification hashes and approval status

${runs.length?`
${runs.map(r=>``).join('')}
ReferenceTitleTypeStatusGeneratedFormats
${esc(r.reference_no)}${esc(r.title)}${esc(r.report_type)}${statusPill(r.status)}${fmtDateTime(r.generated_at)}${esc(r.output_formats||'')}
`:emptyState('No reports generated','Generate a report from verified project, financial, risk or assessment records.','','chart')}
`;$('#generate-report')?.addEventListener('click',()=>generateReportModal(templates))} public/assets/bcos-s25-3.js-361-function generateReportModal(templates){const m=openModal('Generate controlled report','Select the report context and approved outputs.',`
`);m.querySelector('[data-close]').onclick=closeModal;m.querySelector('#report-form').onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,fd=new FormData(f),body=Object.fromEntries(fd.entries());body.formats=fd.getAll('format');body.context_type=body.report_type;body.include_charts=true;body.include_appendices=true;body.watermark_text=body.classification;const btn=f.querySelector('[type="submit"]');btn.disabled=true;try{const r=await api('/api/reports/generate',{method:'POST',body:JSON.stringify(body)});closeModal();toast('Report generated',r.reference_no||'Controlled output created');await loadRoute()}catch(ex){toast('Report generation failed',ex.message,'error')}finally{btn.disabled=false}}} public/assets/bcos-s25-3.js-362- public/assets/bcos-s25-3.js-363-async function viewGovernance(page){const d=await api('/api/governance/dashboard');const e=d.executive||{};page.innerHTML=`${pageHead('Governance, Legal, Risk & Quality','One view of obligations, enterprise risks, quality findings, incidents and resilience.','','Enterprise Assurance')}
${metricCard('Legal matters',e.open_legal_matters||0,'Open register items','file')}${metricCard('Compliance due',e.compliance_due_30_days||0,'Next 30 days','calendar')}${metricCard('Overdue compliance',e.overdue_compliance||0,'Immediate attention','risk',num(e.overdue_compliance)?'bad':'good')}${metricCard('High risks',e.high_risks||0,'Enterprise register','shield')}${metricCard('Serious findings',e.serious_audit_findings||0,'Audit and quality','check')}${metricCard('Open complaints',e.open_complaints||0,'Service accountability','message')}

Compliance calendar

Upcoming and overdue obligations

${(d.calendar||[]).length?d.calendar.map(x=>`
${esc(x.title)}${esc(x.record_type)} · ${esc(x.reference_no)} · Due ${fmtDate(x.due_date)}
${statusPill(x.priority)}
`).join(''):emptyState('No compliance dates recorded','Add verified statutory and policy obligations.','','calendar')}
`} public/assets/bcos-s25-3.js-364-async function viewAssessments(page){const d=await api('/api/assessments');const items=Array.isArray(d)?d:(d.assessments||[]);page.innerHTML=`${pageHead('Technical Item Assessments','Evidence-led decisions for vehicles, machinery, equipment, software and other assets.',has('assessments.manage')?button('New assessment','primary','','plus'):'','Assessment Centre')}
${items.length?items.map(x=>`
${statusPill(x.status)}${esc(x.reference_no)}

${esc(x.item_name)}

${esc([x.manufacturer,x.model].filter(Boolean).join(' · ')||x.item_type||'Technical assessment')}

${fmtNum(x.overall_score)}score${fmtNum(x.serious_findings)}serious findings${fmtNum(x.evidence_count)}evidence items
`).join(''):emptyState('No assessments recorded','Create an evidence-led technical assessment when a project requires it.','','check')}
`} public/assets/bcos-s25-3.js-365-async function viewLearning(page){const d=await api('/api/learning/dashboard');const s=d.summary||{};page.innerHTML=`${pageHead('Knowledge & Learning Centre','Turn every engagement into reusable knowledge, stronger judgment and measurable capability.',button('Capture lesson','primary','','plus'),'Learn · Think · Improve')}
${metricCard('Active courses',s.active_courses||0,'Current learning','book')}${metricCard('Completed',s.completed_courses||0,'Courses completed','check')}${metricCard('Quizzes passed',s.quizzes_passed||0,'Knowledge checks','task')}${metricCard('Scenarios',s.games_attempted||0,'Practical exercises','chart')}${metricCard('Certificates',s.active_certifications||0,'Active credentials','shield')}
${d.wisdom?.quote_text?`

${esc(d.wisdom.theme||'Global Wisdom')}

“${esc(d.wisdom.quote_text)}”

— ${esc(d.wisdom.source_name||d.wisdom.source_type||'Source')}

`:''}

My learning path

Assigned courses and progress

${(d.courses||[]).length?d.courses.map(c=>`
${esc(c.title)}${esc(c.difficulty||'')} · ${fmtNum(c.estimated_minutes)} minutes
${fmtNum(c.progress_percent)}%
`).join(''):emptyState('No active courses','Assigned learning will appear here.','','book')}

Published knowledge

Reusable institutional knowledge

${(d.knowledge||[]).length?d.knowledge.slice(0,8).map(k=>`
${esc(k.title)}${esc(k.article_type)} · ${esc(k.summary||'Open to review and reuse')}
${statusPill(k.status||'Published')}
`).join(''):emptyState('No published knowledge','Capture lessons and approve them for reuse.','','book')}
`} public/assets/bcos-s25-3.js-366- public/assets/bcos-s25-3.js-367-async function viewNotifications(page){const [items,summary]=await Promise.all([api('/api/notifications').catch(()=>[]),api('/api/notifications/summary').catch(()=>({}))]);page.innerHTML=`${pageHead('Notification Centre','One governed place for priorities, approvals, reminders and operational alerts.','','Enterprise Communication')}
${metricCard('Unread',summary.unread_count||0,'Requires review','bell')}${metricCard('Critical',summary.critical_count||0,'Immediate attention','risk')}${metricCard('High priority',summary.high_count||0,'Important alerts','clock')}${metricCard('Current view',items.length,'Available notices','mail')}

Inbox

Open an item to continue to the related record

${items.length?items.map(x=>`
${esc(x.title)}${esc(x.message)} · ${fmtDateTime(x.created_at)}
${statusPill(x.priority||x.status)}
`).join(''):emptyState('No notifications','Your notification inbox is clear.','','bell')}
`} public/assets/bcos-s25-3.js-368-async function viewCollaboration(page){const items=await api('/api/collaboration/conversations');page.innerHTML=`${pageHead('Enterprise Conversations','Secure project, client, approval and direct discussions with governed participants.',has('collaboration.message')?button('New conversation','primary','','plus'):'','Collaboration')}
${items.length?items.map(x=>`
${esc(x.reference_no)}${statusPill(x.audience)}

${esc(x.subject)}

${fmtNum(x.participant_count)} participants · ${fmtNum(x.message_count)} messages

${fmtNum(x.unread_count)}unread${esc(x.conversation_type)}
`).join(''):emptyState('No conversations','Create a governed conversation when project collaboration begins.','','message')}
`} public/assets/bcos-s25-3.js-369-async function viewSearch(page){const q=new URLSearchParams(location.search).get('q')||'';page.innerHTML=`${pageHead('Enterprise Search','Find governed clients, projects, documents, reports, assessments, finance and knowledge within your permissions.','','Search')}
${icon('search')}
${q?loading(4):emptyState('Search BCOS','Enter at least two characters to search governed information.','','search')}
`;$('#search-form').onsubmit=e=>{e.preventDefault();const term=$('#search-q').value.trim();history.replaceState({},'',`/search?q=${encodeURIComponent(term)}`);runSearch(term)};if(q)await runSearch(q)} public/assets/bcos-s25-3.js-370-async function runSearch(q){const el=$('#search-results');if(q.length<2){el.innerHTML=emptyState('Enter more detail','Search requires at least two characters.','','search');return}el.innerHTML=loading(4);try{const rows=await api(`/api/search?q=${encodeURIComponent(q)}`);el.innerHTML=rows.length?`
${rows.map(x=>`
${esc(x.entity_type)}${esc(x.reference_no||'')}

${esc(x.title)}

${esc(x.summary||'No summary available.')}

${esc(x.classification)}${esc(x.status||'ACTIVE')}
`).join('')}
`:emptyState('No results','Try a different name, reference or topic.','','search')}catch(ex){showError(ex,el)}} public/assets/bcos-s25-3.js-371- public/assets/bcos-s25-3.js-372-async function viewAdministration(page){const d=await api('/api/administration/dashboard');const o=d.operations||{};page.innerHTML=`${pageHead('Enterprise Administration','Manage organisation structure, system health, jobs, backups and controlled settings.',button('Legal & banking settings','primary','data-nav="/settings"','settings'),'System Administration')}
${metricCard('Indexed records',o.indexed_records||0,'Enterprise search','search')}${metricCard('Active jobs',o.active_jobs||0,'Background processing','refresh')}${metricCard('Verified backups',o.successful_backups||0,'Recovery evidence','shield')}${metricCard('Maintenance',num(o.maintenance_enabled)?'ON':'OFF','Controlled state','settings')}

System health

Latest application and operational checks

${(d.health||[]).length?d.health.map(x=>`
${esc(x.check_code)}${fmtDateTime(x.checked_at)}
${statusPill(x.status)}
`).join(''):emptyState('No health records','Run the health-check process to populate this register.','','shield')}

Legal entities

Contracting and issuing entities

${(d.entities||[]).map(x=>`
${esc(x.legal_name)}${esc(x.entity_code)} · ${esc(x.jurisdiction||'Jurisdiction not recorded')}
${statusPill(x.verification_status||x.status)}
`).join('')}
`} public/assets/bcos-s25-3.js-373- public/assets/bcos-s25-3.js-374-async function viewSettings(page){page.innerHTML=`${pageHead('Settings Centre','Manage your profile, security, notifications, appearance and authorised organisation details.','','Account & Organisation')}
${loading(2)}
${loading(2)}
${loading(2)}
${appearanceSettings()}
${has('administration.view')?'
'+loading(3)+'
':''}
`;$$('[data-settings]').forEach(b=>b.onclick=()=>activateSettingsTab(b.dataset.settings));await Promise.all([loadProfileSettings(),loadSecuritySettings(),loadNotificationSettings(),has('administration.view')?loadLegalSettings():Promise.resolve()]);const appearanceButton=$('#save-appearance');if(appearanceButton)appearanceButton.onclick=()=>{const density=$('#density').value;localStorage.setItem('bcos_density',density);document.documentElement.dataset.density=density;toast('Appearance saved',density==='compact'?'Compact information density enabled.':'Comfortable information density enabled.')}} public/assets/bcos-s25-3.js-375-function activateSettingsTab(tab){$$('[data-settings]').forEach(b=>b.classList.toggle('active',b.dataset.settings===tab));$$('[data-section]').forEach(s=>s.classList.toggle('active',s.dataset.section===tab))} public/assets/bcos-s25-3.js-376-async function loadProfileSettings(){const el=$('[data-section="profile"]');el.innerHTML=`

Profile details

Keep your name, locale and timezone accurate for project records and notifications.

Email changes require administrator review.
`;$('#profile-form').onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,body=Object.fromEntries(new FormData(f).entries()),btn=f.querySelector('[type="submit"]');btn.disabled=true;try{const r=await api('/api/account/profile',{method:'POST',body:JSON.stringify(body)});state.user={...state.user,...r.user};toast('Profile updated');renderShell();loadRoute()}catch(ex){toast('Profile update failed',ex.message,'error')}finally{btn.disabled=false}}} public/assets/bcos-s25-3.js:377:async function loadSecuritySettings(){const el=$('[data-section="security"]');try{const [d,mfa]=await Promise.all([api('/api/account/sessions'),api('/api/account/mfa')]);el.innerHTML=`

Password, MFA and active sessions

Protect your BCOS account and sign out devices that you do not recognise.

${mfa.enabled?'Multi-factor authentication is enabled':'Multi-factor authentication is not enabled'}${mfa.enabled?`${fmtNum(mfa.recovery_codes_remaining)} unused recovery code(s) remain.`:'Enable an authenticator app before granting external client production access.'}
${mfa.enabled?'':''}
At least 14 characters with uppercase, lowercase, number and symbol.

Active sessions

Current and recently active authorised devices

${(d.sessions||[]).length?(d.sessions||[]).map(s=>`
${icon('device')}
${s.is_current?'Current session':'Authorised session'}${esc(s.user_agent||'Unknown device')}
${esc(s.ip_address||'Unknown address')} · Last active ${fmtDateTime(s.last_seen_at)}
${s.is_current?statusPill('Current'):statusPill(s.mfa_verified_at?'MFA verified':'Password')}
`).join(''):emptyState('No active sessions','Active devices will appear here.','','device')}`;$('#password-form').onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,body=Object.fromEntries(new FormData(f).entries()),btn=f.querySelector('[type="submit"]');if(body.new_password!==body.confirm_password){toast('Passwords do not match','','error');return}btn.disabled=true;try{await api('/api/account/password',{method:'POST',body:JSON.stringify(body)});f.reset();toast('Password changed','Other active devices were signed out.');await loadSecuritySettings()}catch(ex){toast('Password not changed',ex.message,'error')}finally{btn.disabled=false}};$('#revoke-other').onclick=async()=>{try{const r=await api('/api/account/sessions/revoke-other',{method:'POST'});toast('Other devices signed out',`${fmtNum(r.revoked)} session(s) revoked.`);await loadSecuritySettings()}catch(ex){toast('Sessions not revoked',ex.message,'error')}};$('#enable-mfa')?.addEventListener('click',beginMfaModal);$('#disable-mfa')?.addEventListener('click',disableMfaModal)}catch(ex){showError(ex,el)}} public/assets/bcos-s25-3.js:378:async function beginMfaModal(){try{const d=await api('/api/account/mfa/begin',{method:'POST'});const m=openModal('Enable multi-factor authentication','Add BCOS to an authenticator app, then confirm the current six-digit code.',`
Manual setup key${esc(d.secret)}

In Microsoft Authenticator, Google Authenticator, 1Password or another TOTP app, choose to enter a setup key manually. Account: ${esc(state.user.email)}. Type: time based.

Advanced authenticator URI
`);m.querySelector('[data-close]').onclick=closeModal;m.querySelector('#copy-mfa-secret').onclick=async()=>{try{await navigator.clipboard.writeText(d.secret);toast('Setup key copied')}catch{m.querySelector('#mfa-secret').focus?.();toast('Select and copy the setup key manually')}};m.querySelector('#verify-mfa-form').onsubmit=async e=>{e.preventDefault();const btn=e.currentTarget.querySelector('[type="submit"]');btn.disabled=true;try{const result=await api('/api/account/mfa/verify',{method:'POST',body:JSON.stringify({code:e.currentTarget.code.value.trim()})});state.user.mfa_required=1;const codes=(result.recovery_codes||[]).join('\n');m.innerHTML=`
${icon('shield')}

MFA enabled

Store these one-time recovery codes securely. Each code works once. BCOS will not show this list again.

`;m.querySelector('#print-recovery').onclick=()=>window.print();m.querySelector('#finish-mfa').onclick=async()=>{closeModal();toast('MFA enabled','Your account now requires a code at sign-in.');await loadSecuritySettings()}}catch(ex){toast('MFA not enabled',ex.message,'error');btn.disabled=false}}}catch(ex){toast('MFA enrolment could not start',ex.message,'error')}} public/assets/bcos-s25-3.js:379:function disableMfaModal(){const m=openModal('Disable multi-factor authentication','This reduces account security. Confirm with your password and current authenticator code.',`
External client access should not be used while MFA is disabled.
`);m.querySelector('[data-close]').onclick=closeModal;m.querySelector('#disable-mfa-form').onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,btn=f.querySelector('[type="submit"]');btn.disabled=true;try{await api('/api/account/mfa/disable',{method:'POST',body:JSON.stringify(Object.fromEntries(new FormData(f).entries()))});state.user.mfa_required=0;closeModal();toast('MFA disabled','','error');await loadSecuritySettings()}catch(ex){toast('MFA not disabled',ex.message,'error');btn.disabled=false}}} public/assets/bcos-s25-3.js-380-async function loadNotificationSettings(){const el=$('[data-section="notifications"]');try{const p=await api('/api/notifications/preferences');el.innerHTML=`

Notification preferences

Choose how BCOS should surface important work without overwhelming you.

`;$('#notification-form').onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,body=Object.fromEntries(new FormData(f).entries());['in_app_enabled','email_enabled','daily_digest_enabled','weekly_digest_enabled'].forEach(k=>body[k]=body[k]==='1');body.timezone=state.user.timezone||'UTC';try{await api('/api/notifications/preferences',{method:'POST',body:JSON.stringify(body)});toast('Notification preferences saved')}catch(ex){toast('Preferences not saved',ex.message,'error')}}}catch(ex){showError(ex,el)}} public/assets/bcos-s25-3.js-381-function appearanceSettings(){const current=localStorage.getItem('bcos_density')||'comfortable';return `

Appearance and accessibility

Choose a comfortable information density. BCOS follows your operating system’s accessibility preferences.

Motion
`} public/assets/bcos-s25-3.js-382-async function loadLegalSettings(){const el=$('[data-section="legal"]');try{const profiles=await api('/api/administration/legal-profiles');if(!profiles.length){el.innerHTML=emptyState('No legal entities available','Create a legal entity from Administration first.','','building');return}let active=profiles[0].legal_entity_id;const render=()=>{const p=profiles.find(x=>x.legal_entity_id===active)||profiles[0];el.innerHTML=`

Legal entities, tax and payment instructions

Administrator-editable, evidence-supported and historically preserved. Previously issued documents are never rewritten.

${profiles.map(x=>``).join('')}
`;$$('[data-entity]',el).forEach(b=>b.onclick=()=>{active=b.dataset.entity;render()});$('#legal-form',el).onsubmit=async e=>{e.preventDefault();const f=e.currentTarget,fd=Object.fromEntries(new FormData(f).entries()),btn=f.querySelector('[type="submit"]');btn.disabled=true;try{await api('/api/administration/legal-entities/update',{method:'POST',body:JSON.stringify({id:fd.legal_entity_id,legal_name:fd.legal_name,entity_code:fd.entity_code,registration_number:fd.registration_number,jurisdiction:fd.jurisdiction,country_code:fd.country_code,base_currency:p.base_currency||'USD',timezone:p.timezone||'UTC',status:p.entity_status||'ACTIVE'})});await api('/api/administration/legal-profiles',{method:'POST',body:JSON.stringify(fd)});toast('Legal entity profile saved','Future documents will use the newly approved details.');await loadLegalSettings()}catch(ex){toast('Legal profile not saved',ex.message,'error')}finally{btn.disabled=false}}};render()}catch(ex){showError(ex,el)}} public/assets/bcos-s25-3.js-383- public/assets/bcos-s25-3.js-384-async function viewClientPortal(page){const d=await api('/api/client-portal/dashboard');const f=d.financial||{},c=f.currency_code||f.base_currency||'USD';page.innerHTML=`

Secure Client Workspace

Welcome, ${esc(d.membership.organization_name)}

Project delivery, financial stewardship, approvals, learning and authorised records.

${esc(String(d.membership.portal_role).replaceAll('_',' '))}
${metricCard('Active projects',(d.projects||[]).length,'Current engagements','folder')}${metricCard('Funds received',fmtMoney(f.total_funds_received||0,c),'Client funding','download')}${metricCard('Approved expenditure',fmtMoney(f.total_approved_expenditure||0,c),'Verified spend','chart')}${metricCard('Available balance',fmtMoney(f.total_available_balance||0,c),'Remaining project funds','money')}${metricCard('Potential refund',fmtMoney(f.total_potential_refund||0,c),'Subject to reconciliation','refresh')}${metricCard('Outstanding amount',fmtMoney(f.total_outstanding_funding||f.total_outstanding_amount||0,c),'Payment or funding due','clock')}

Your project portfolio

Progress, milestones and delivery status

${(d.projects||[]).length?d.projects.map(p=>`
${esc(p.reference_no)}

${esc(p.name)}

${statusPill(p.health)}

${fmtNum(p.progress_percent)}% complete · ${fmtNum(p.completed_milestones)}/${fmtNum(p.total_milestones)} milestones · ${fmtNum(p.accepted_deliverables)}/${fmtNum(p.total_deliverables)} deliverables accepted

`).join(''):emptyState('No visible projects','Your authorised projects will appear here.','','folder')}
`} public/assets/bcos-s25-3.js-385- public/assets/bcos-s25-3.js-386-function debounce(fn,wait){let t;return(...a)=>{clearTimeout(t);t=setTimeout(()=>fn(...a),wait)}} public/assets/bcos-s25-3.js-387- public/assets/bcos-s25-3.js-388-async function init(){document.documentElement.dataset.density=localStorage.getItem('bcos_density')||'comfortable';try{const d=await api('/api/auth/me');state.user=d.user;state.csrf=d.csrf_token||'';if(location.pathname==='/'||location.pathname==='/login')history.replaceState({},'',defaultRoute());renderShell();loadRoute()}catch{loginView()}} public/assets/bcos-s25-3.js-389- public/assets/bcos-s25-3.js-390-init();