const B=window.psiqManageBridge; const $=id=>document.getElementById(id); const esc=value=>String(value??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const uid=()=>crypto.randomUUID(); const roles={dev:'Dev',admin:'Admin',editor:'Editor de cursos',financeiro:'Financeiro'}; const canManage=()=>['dev','admin','editor'].includes(B.role()); const urls=new Set();let editing=null,search='',fiscal='';const opened=new Set(); const seed=await fetch('/management-seed.json').then(r=>{if(!r.ok)throw new Error('catalog');return r.json()}).catch(()=>[]); const courses=Object.entries(B.library()).filter(([,c])=>!c.isRecording).map(([id,c])=>({id,name:c.name,modules:c.modules.map(m=>({id:m.id,name:m.name,lessons:m.lessons.map(l=>({id:l.id,title:l.title,description:l.description||'',status:'draft',assets:{pdf:null,mapa:null,quiz:null,cover:null,video:null,outros:[]},studentId:l.id}))}))})); const base=courses.find(c=>c.id==='base');if(seed.length&&base)base.modules=[{id:'base-principal',name:'Psiq Base: base teórica',lessons:seed.map(a=>({id:a.id,title:`Aula ${String(a.numero).padStart(2,'0')} · ${a.titulo}`,description:'Materiais importados do acervo local. Publicação ainda não definida.',status:'draft',studentId:'base-aula-'+String(a.numero).padStart(2,'0'),assets:{pdf:a.materiais.pdf?{name:a.materiais.pdf.split('/').at(-1),url:`/materiais/${a.id}/pdf`,review:'pending'}:null,mapa:a.materiais.mapa?{name:a.materiais.mapa.split('/').at(-1),url:`/materiais/${a.id}/mapa`,review:'pending'}:null,quiz:a.materiais.quiz?{name:a.materiais.quiz.split('/').at(-1),url:`/materiais/${a.id}/quiz`,review:'pending'}:null,cover:null,video:a.numero===1?{name:'Vídeo local da aula 01',url:'/video-teste'}:null,outros:[]}}))}]; const course=()=>courses.find(c=>c.id===B.course())||courses[0]; const findLesson=id=>{for(const c of courses)for(const m of c.modules){const l=m.lessons.find(l=>l.id===id);if(l)return {c,m,l}}return null}; const pill=(text,kind='')=>`${esc(text)}`; const countLessons=c=>c.modules.reduce((n,m)=>n+m.lessons.length,0); const allLessons=()=>courses.flatMap(c=>c.modules.flatMap(m=>m.lessons)); function repaint(){B.render()} function go(key){if(location.hash==='#adm-'+key)repaint();else location.hash='adm-'+key} function notice(text){window.notify(text)} function ready(l){return Boolean(l.title.trim()&&l.assets.video&&l.assets.cover&&['pdf','mapa','quiz'].every(k=>l.assets[k]?.review==='approved')&&l.assets.outros.every(a=>a.review==='approved'))} Object.assign(B.titles,{hoje:['Painel','Visão geral de vendas, conteúdo e alunos. Dados de demonstração.'],cursos:['Gestão de cursos','Abra um curso para organizar módulos, aulas e materiais.'],editor:['Gestão de cursos','Módulos, aulas e publicação do conteúdo.'],financeiro:['Financeiro e vendas','Recebimentos e cobranças de demonstração, com filtros e extrato.'],equipe:['Equipe e permissões','Quatro acessos: Dev, Admin, Editor de cursos e Financeiro.'],config:['Configurações','Conciliação e fiscal, aparência e configurações técnicas.'],comunidade:['Comunidade','Espaços, publicações e acompanhamento da comunidade.']}); function dashboard(){const sales=B.sales(),sum=status=>sales.filter(s=>s.status===status).reduce((n,s)=>n+s.amount,0),lessons=allLessons();return B.kpis([['RECEBIDO',B.money(sum('Recebido')),'Resumo dos filtros do Financeiro'],['EM ABERTO',B.money(sum('Em aberto')),'Não inclui valores recebidos'],['AULAS NÃO PUBLICADAS',String(lessons.filter(l=>l.status!=='published').length),'Conteúdo em preparação'],['ALUNOS ATIVOS','24','Número fictício de demonstração']])+`

Recebimentos por curso

${courses.map(c=>{const value=sales.filter(s=>s.product===c.id&&s.status==='Recebido').reduce((n,s)=>n+s.amount,0),max=Math.max(1,...courses.map(x=>sales.filter(s=>s.product===x.id&&s.status==='Recebido').reduce((n,s)=>n+s.amount,0)));return `
${esc(c.name)}${B.money(value)}
`}).join('')}

Conteúdo da escola

${courses.map(c=>`
${esc(c.name)}${c.modules.length} módulos · ${countLessons(c)} aulas
`).join('')}

Publicações e alterações ficam somente nesta prévia.

`} function finance(){const host=document.createElement('div');host.innerHTML=B.finance();const heading=[...host.querySelectorAll('h2')].find(h=>h.textContent==='Conciliação e fiscal');if(heading){const panel=heading.closest('section');fiscal=panel.outerHTML;panel.remove()}return host.innerHTML} function settings(){if(B.role()!=='dev')return '

Acesso restrito ao Dev

';if(!fiscal)finance();return `

Configurações da plataforma

Conciliação e fiscal ficam aqui. APIs e permissões têm seções próprias, acessíveis somente ao Dev.

${fiscal}${B.generic('config')}
`} function team(){return `

Acessos da equipe

AcessoEscopoRestrição
DevToda a plataformaInclui APIs, permissões e configurações
AdminOperação, conteúdo, alunos, financeiro e comunidadeSem APIs, permissões e configurações técnicas
Editor de cursosMódulos, aulas, capas, vídeos e materiaisSem financeiro ou configurações
FinanceiroFinanceiro e vendasSem gestão de cursos ou configurações

Não há um acesso separado de moderador. Admin é o acesso administrativo do proprietário.

Esta matriz modela o front. O backend ainda deverá conferir cada operação e registrar alterações.

`} function courseList(){return `
${courses.map(c=>`
${esc(c.name)}
${pill('Acervo em organização')}

${c.modules.length} módulos · ${countLessons(c)} aulas
${c.modules.reduce((n,m)=>n+m.lessons.filter(l=>l.status==='published').length,0)} publicadas nesta prévia

`).join('')}

Organização dos cursos

Crie módulos, abra as aulas existentes e edite vídeo, capa, PDF, mapa mental, quiz e outros anexos separadamente.

`} function modulesView(){const c=course(),term=search.toLocaleLowerCase('pt-BR');return `

${esc(c.name)}

${c.modules.length} módulos · ${countLessons(c)} aulas. Os módulos começam recolhidos.

${c.modules.map((m,index)=>{const lessons=m.lessons.filter(l=>!term||m.name.toLocaleLowerCase('pt-BR').includes(term)||l.title.toLocaleLowerCase('pt-BR').includes(term));if(term&&!m.name.toLocaleLowerCase('pt-BR').includes(term)&&!lessons.length)return '';return `
${esc(m.name)}${m.lessons.length} aulas · ${m.lessons.filter(l=>l.status==='published').length} publicadas
${lessons.map(l=>lessonRow(l,m)).join('')||'

Nenhuma aula neste módulo.

'}
`}).join('')||'
Nenhum módulo corresponde à busca.
'}
`} function lessonRow(l,m){const index=m.lessons.indexOf(l);return `
${esc(l.title)}${['pdf','mapa','quiz'].map(k=>`${k==='mapa'?'Mapa mental':k.toUpperCase()}: ${l.assets[k]?'adicionado':'pendente'}`).join(' · ')}
${pill(l.status==='published'?'Publicado':'Não publicado',l.status==='published'?'good':'')}
⋮
`} function assetField(key,title,accept){const a=editing.lesson.assets[key],review=['pdf','mapa','quiz'].includes(key);return `

${title}

${a?`
${esc(a.name)}
${key==='cover'?`Capa selecionada`:key==='video'?``:''}
${review?pill(a.review==='approved'?'Aprovado':'Aguardando revisão',a.review==='approved'?'good':''):''}${review?`Abrir material ↗`:''}
`:'

Ainda não adicionado.

'}
`} function lessonEditor(){const c=course(),l=editing.lesson,requirements=[['Título',Boolean(l.title.trim())],['Vídeo',Boolean(l.assets.video)],['Capa',Boolean(l.assets.cover)],...['pdf','mapa','quiz'].map(k=>[k==='pdf'?'PDF aprovado':k==='mapa'?'Mapa mental aprovado':'Quiz aprovado',l.assets[k]?.review==='approved'])];return `
${pill(l.status==='published'?'Publicado':'Não publicado',l.status==='published'?'good':'')}

${editing.fresh?'Nova aula':'Editar aula'}

${esc(c.name)}

${assetField('video','Vídeo da aula','video/*')}${assetField('pdf','PDF da aula','.pdf,application/pdf')}${assetField('mapa','Mapa mental da aula','.html,text/html')}${assetField('quiz','Quiz interativo','.html,text/html')}

Outros materiais opcional

${l.assets.outros.map((a,i)=>`
${esc(a.name)}${pill(a.review==='approved'?'Aprovado':'Aguardando revisão',a.review==='approved'?'good':'')}Baixar
`).join('')||'

Nenhum anexo adicional.

'}
`} function editor(){return editing?lessonEditor():modulesView()} function capture(){if(!editing||!$('manage-title'))return;editing.lesson.title=$('manage-title').value;editing.lesson.description=$('manage-description').value;editing.moduleId=$('manage-module').value;editing.lesson.status=$('manage-publication').value} function beginEdit(id,moduleId){const found=id?findLesson(id):null;editing={fresh:!found,moduleId:found?.m.id||moduleId,lesson:found?structuredClone(found.l):{id:uid(),title:'',description:'',status:'draft',studentId:null,assets:{video:null,cover:null,pdf:null,mapa:null,quiz:null,outros:[]}}};repaint();$('manage-title').focus()} const modal=document.createElement('dialog');modal.id='manage-dialog';modal.className='manage-dialog';document.body.append(modal); let modalAction=null; function dialog(title,body,action,label='Salvar'){modal.innerHTML=`

${esc(title)}

${body}
`;modalAction=action;modal.showModal()} function nameDialog(title,value,action){dialog(title,``,()=>{const name=$('manage-dialog-name').value.trim();if(!name){$('manage-dialog-name').setCustomValidity('Informe um nome.');$('manage-dialog-name').reportValidity();return false}action(name);return true})} function previewLesson(id){const found=findLesson(id);if(!found)return;const l=found.l;modal.innerHTML=`

${esc(l.title)}

${l.assets.cover?`Capa da aula`:''}${l.assets.video?``:'

Vídeo ainda não adicionado.

'}

${esc(l.description)}

Prévia de conteúdo; não altera a publicação para alunos.

`;modal.showModal()} function saveLesson(publish){capture();const l=editing.lesson;if(publish)l.status='published';if(l.status==='published'&&!ready(l)){notice('Para publicar, adicione vídeo e capa e aprove PDF, mapa mental, quiz e outros anexos.');return}const c=course(),target=c.modules.find(m=>m.id===editing.moduleId);if(!target){notice('Escolha um módulo válido.');return}const old=findLesson(l.id);if(old&&old.m.id===target.id)old.m.lessons[old.m.lessons.indexOf(old.l)]=structuredClone(l);else{if(old)old.m.lessons.splice(old.m.lessons.indexOf(old.l),1);target.lessons.push(structuredClone(l))}opened.add(target.id);editing=null;repaint();notice(l.status==='published'?'Aula publicada somente nesta prévia.':'Alterações salvas nesta sessão. Aula não publicada.')} function localAsset(file,key){const url=URL.createObjectURL(file);urls.add(url);let preview=null;if(['mapa','quiz'].includes(key)){const wrapper=`Material de apoio · prévia`;preview=URL.createObjectURL(new Blob([wrapper],{type:'text/html'}));urls.add(preview)}return {name:file.name,url,preview,review:'pending',size:file.size}} function valid(file,key){if(key==='cover')return ['image/png','image/jpeg','image/webp'].includes(file.type)&&file.size<=10*1024*1024;if(key==='video')return file.type.startsWith('video/');if(key==='pdf')return /\.pdf$/i.test(file.name);if(['mapa','quiz'].includes(key))return /\.html?$/i.test(file.name);return !/\.(html?|js|exe|cmd|bat|svg)$/i.test(file.name)} window.adminHome=dashboard;window.adminCourseList=courseList;window.adminEditor=editor;window.adminFinance=finance;window.adminTeam=team;window.adminGeneric=key=>key==='config'?settings():B.generic(key); document.addEventListener('click',e=>{const b=e.target.closest('button,a');if(!b)return;if(b.hasAttribute('data-manage-close-dialog')){e.preventDefault();modal.close();return}const key=Object.keys(b.dataset).find(k=>k.startsWith('manage'));if(!key||key==='managePublish'||!canManage())return;e.preventDefault();e.stopImmediatePropagation();const id=b.dataset[key],c=course(),found=findLesson(id);const move=(list,index,direction)=>{const next=index+direction;if(next<0||next>=list.length)return;const item=list.splice(index,1)[0];list.splice(next,0,item);repaint()}; switch(key){case 'manageCourse':B.setCourse(id);editing=null;search='';go('editor');break; case 'manageNewModule':nameDialog('Criar módulo','',name=>{c.modules.push({id:uid(),name,lessons:[]});repaint()});break; case 'manageRenameModule':{const m=c.modules.find(m=>m.id===id);nameDialog('Renomear módulo',m.name,name=>{m.name=name;repaint()});break} case 'manageDeleteModule':{const m=c.modules.find(m=>m.id===id);dialog('Excluir módulo',`

Excluir ${esc(m.name)} e suas ${m.lessons.length} aulas desta prévia? Os arquivos originais serão preservados.

`,()=>{c.modules.splice(c.modules.indexOf(m),1);repaint()},'Excluir');break} case 'manageOrderModule':move(c.modules,c.modules.findIndex(m=>m.id===id),Number(b.dataset.direction));break; case 'manageNewLesson':beginEdit(null,id);break;case 'manageEdit':beginEdit(id);break; case 'manageDuplicate':{const copy=structuredClone(found.l);copy.id=uid();copy.title+=' (cópia)';copy.status='draft';found.m.lessons.splice(found.m.lessons.indexOf(found.l)+1,0,copy);opened.add(found.m.id);repaint();break} case 'managePreview':previewLesson(id);break; case 'manageMove':dialog('Mover aula',``,()=>{const target=c.modules.find(m=>m.id===$('manage-dialog-module').value);found.m.lessons.splice(found.m.lessons.indexOf(found.l),1);target.lessons.push(found.l);opened.add(target.id);repaint()},'Mover');break; case 'manageOrderLesson':move(found.m.lessons,found.m.lessons.indexOf(found.l),Number(b.dataset.direction));break; case 'manageStatus':if(found.l.status==='published'){found.l.status='draft';repaint()}else if(ready(found.l)){found.l.status='published';repaint();notice('Publicação simulada nesta sessão.')}else{beginEdit(id);notice('Adicione os arquivos e aprove os materiais antes de publicar.')}break; case 'manageDelete':dialog('Excluir aula',`

Excluir ${esc(found.l.title)} desta prévia?

`,()=>{found.m.lessons.splice(found.m.lessons.indexOf(found.l),1);repaint()},'Excluir');break; case 'manageBack':case 'manageCancel':editing=null;repaint();break; case 'manageApprove':capture();{const a=editing.lesson.assets[id];a.review=a.review==='approved'?'pending':'approved';if(a.review==='pending')editing.lesson.status='draft';repaint()}break; case 'manageRemoveAsset':capture();editing.lesson.assets[id]=null;editing.lesson.status='draft';repaint();break; case 'manageApproveOther':capture();{const a=editing.lesson.assets.outros[Number(id)];a.review=a.review==='approved'?'pending':'approved';editing.lesson.status='draft';repaint()}break; case 'manageRemoveOther':capture();editing.lesson.assets.outros.splice(Number(id),1);editing.lesson.status='draft';repaint();break; }},true); document.addEventListener('submit',e=>{if(e.target.id==='manage-dialog-form'){e.preventDefault();if(canManage()&&modalAction?.()!==false)modal.close()}if(e.target.id==='manage-lesson-form'){e.preventDefault();if(canManage())saveLesson(e.submitter?.hasAttribute('data-manage-publish'))}}); document.addEventListener('change',e=>{const key=e.target.dataset.manageFile;if(!key||!editing||!canManage())return;capture();const files=[...e.target.files];if(files.some(f=>!valid(f,key))){notice('Arquivo incompatível. Confira o formato indicado; capas devem ter até 10 MB.');e.target.value='';return}if(key==='outros')editing.lesson.assets.outros.push(...files.map(f=>localAsset(f,key)));else if(files[0])editing.lesson.assets[key]=localAsset(files[0],key);editing.lesson.status='draft';repaint()}); document.addEventListener('input',e=>{if(e.target.id==='manage-search'){search=e.target.value;const p=e.target.selectionStart;repaint();$('manage-search').focus();$('manage-search').setSelectionRange(p,p)}}); document.addEventListener('toggle',e=>{if(e.target.matches('.manage-module')){const id=e.target.dataset.manageModule;e.target.open?opened.add(id):opened.delete(id)}},true); document.addEventListener('click',e=>{document.querySelectorAll('.manage-actions[open]').forEach(d=>{if(!d.contains(e.target))d.open=false});const mode=e.target.closest('[data-access-mode]')?.dataset.accessMode;if(mode)queueMicrotask(()=>{const p=$('login-role-copy');if(p&&roles[mode])p.textContent={dev:'Acesso completo, incluindo APIs, permissões e configurações técnicas.',admin:'Gestão da plataforma, sem APIs, permissões ou configuração técnica.',editor:'Acesso somente à gestão de cursos, módulos, aulas e materiais.',financeiro:'Acesso somente ao painel financeiro, vendas e cobranças.'}[mode]})}); window.addEventListener('beforeunload',()=>urls.forEach(url=>URL.revokeObjectURL(url))); window.psiqManagementReady=true;if(!$('admin-shell').hidden)repaint(); // Integrações: catálogo visual autorizado em 29/10/2026. const externalTools=[ {id:'panda',name:'Panda Video',mark:'P',category:'Vídeos',description:'Hospedagem e reprodução das aulas.',features:['Vídeos vinculados às aulas','Capa independente do vídeo','Acompanhamento do processamento'],purpose:'Reproduzir as aulas na área de membros.'}, {id:'bling',name:'Bling',mark:'B',category:'Financeiro',description:'Organização fiscal e integração com a gestão financeira.',features:['Dados fiscais e notas','Vínculo com vendas e clientes','Acompanhamento da sincronização'],purpose:'Integrar os dados fiscais às vendas da plataforma.'}, {id:'claude',name:'Claude · Anthropic',mark:'C',category:'Inteligência artificial',description:'Produção de materiais a partir do conteúdo das aulas.',features:['Relatório da aula','Mapa mental em HTML','Quiz interativo com revisão'],purpose:'Gerar relatório, mapa mental e quiz para aprovação.'}, {id:'openai',name:'ChatGPT · OpenAI',mark:'O',category:'Inteligência artificial',description:'Recursos de IA para os fluxos de conteúdo da plataforma.',features:['Apoio à produção de materiais','Transcrição a configurar','Geração de capas a configurar'],purpose:'Apoiar os fluxos de IA e a produção de conteúdo.'} ]; const connectionDrafts=new Map();let integrationTab='todas',integrationCategory='Todas'; B.titles.integracoes=['Integrações','Gerencie as ferramentas externas da plataforma.']; function integrationsView(){if(B.role()!=='dev')return '

Acesso exclusivo do Dev

';const categories=['Todas',...new Set(externalTools.map(t=>t.category))];const selected=externalTools.filter(t=>integrationTab!=='ativas'&&(integrationCategory==='Todas'||t.category===integrationCategory));return `

Ferramentas da plataforma

Quatro ferramentas previstas. Nenhuma conexão real foi realizada.

0 conectadas · 4 previstas
${[['todas','Todas'],['ativas','Ativas'],['categorias','Categorias']].map(([id,label])=>``).join('')}
${integrationTab==='categorias'?``:''}
${selected.map(t=>`

${t.name}

${t.description}

Não conectada${t.category}

${connectionDrafts.has(t.id)?'Planejamento salvo nesta sessão. Conexão pendente.':'Aguardando configuração segura no backend.'}

`).join('')||'

Nenhuma integração ativa

Quando as conexões reais forem implementadas e validadas, elas aparecerão aqui.

'}

As funções acima são o planejamento da integração. Credenciais, conexão, testes e sincronização dependem do backend. Esta tela não solicita nem armazena chaves de API.

`} window.adminIntegrations=integrationsView; document.addEventListener('click',e=>{const tab=e.target.closest('[data-connection-tab]'),button=e.target.closest('[data-connection-manage]');if(!tab&&!button)return;if(B.role()!=='dev')return;e.preventDefault();e.stopImmediatePropagation();if(tab){integrationTab=tab.dataset.connectionTab;if(integrationTab!=='categorias')integrationCategory='Todas';repaint();return}const t=externalTools.find(t=>t.id===button.dataset.connectionManage);const draft=connectionDrafts.get(t.id);dialog(`Configurar ${t.name}`,`

${t.description}

Não cole senhas, tokens ou chaves aqui. A conexão segura será feita no backend, com acesso exclusivo do Dev.

Salvar registra somente o planejamento nesta sessão. Não conecta a ferramenta.

`,()=>{connectionDrafts.set(t.id,{purpose:$('connection-purpose').value,account:$('connection-account').value});repaint();notice('Planejamento salvo nesta sessão. Ferramenta ainda não conectada.')},'Salvar planejamento')},true); document.addEventListener('change',e=>{if(e.target.id==='connection-category'&&B.role()==='dev'){integrationCategory=e.target.value;repaint()}}); if(!$('admin-shell').hidden)repaint();