from pathlib import Path
p=Path('public/assets/app.js')
s=p.read_text(encoding='utf-8')
def replace(old,new):
    global s
    assert old in s, old[:120]
    s=s.replace(old,new)
replace("const courseName=c=>`${grades[c.grade-1]} «${c.section}»`;", "const courseName=c=>c.name || `${grades[c.grade-1]} «${c.section}»`;")
replace("const paths={grid:","const paths={trash:'M3 6h18 M9 6V3h6v3 M5 6l1 15h12l1-15 M10 10v7 M14 10v7',building:'M3 21V5h12v16 M15 11h6v10 M7 9h4 M7 13h4 M7 17h4 M18 15h1 M18 18h1',grid:")
replace("['inventory','box','Inventario'],['teachers'", "['environments','building','Ambientes'],['inventory','box','Inventario'],['search','search','BUSCAR'],['teachers'")
replace("idx===3?", "idx===5?")
replace("['Aulas registradas',cs.length,'Distribuidas en 6 grados','book']", "['Aulas y ambientes',cs.length,`${cs.filter(c=>c.grade).length} aulas · ${cs.filter(c=>!c.grade).length} ambientes`,'book']")
replace("${grades.map((_,i)=>gradeCard(i+1)).join('')}</div>", "${grades.map((_,i)=>gradeCard(i+1)).join('')}${environmentCard()}</div>")
replace("</div></section>${rows.length>6?", "</div></section>${!grade||grade===6?'<div class=\"environment-next\">'+environmentCard()+'</div>':''}${rows.length>6?")
replace("href=\"#grade/${course.grade}\"", "href=\"${course.grade?'#grade/'+course.grade:'#environments'}\"")
replace("Gestiona los bienes, responsables y documentos de esta aula.", "Gestiona los bienes, responsables y documentos de esta ubicación.")
replace("<div class=\"eyebrow\">Inventario del aula pedagógica</div>", "<div class=\"eyebrow\">${course.grade?'Inventario del aula pedagógica':'Inventario del ambiente'}</div>")
replace("bienes en este curso.","bienes en esta ubicación.")
replace("Crea o modifica los bienes de esta aula.","Crea o modifica los bienes de esta ubicación.")
replace("Registra los bienes y mantén actualizada la información del aula.","Registra los bienes y mantén actualizada la información de esta ubicación.")
replace("Volver al curso", "Volver al inventario")
replace("Registra al menos un bien en el aula.","Registra al menos un bien en esta ubicación.")
replace("Visto bueno · Dirección", "Firma de Dirección")
replace("${icon('edit')}</button></td></tr>`).join('')}</tbody></table></div><div class=\"table-foot\">", "${icon('edit')}</button><button class=\"btn icon small danger\" data-action=\"item-delete\" data-id=\"${x.id}\" aria-label=\"Eliminar ${esc(x.name)}\">${icon('trash')}</button></td></tr>`).join('')}</tbody></table></div><div class=\"table-foot\">")
replace("<td><button class=\"btn icon small\" data-action=\"item\"", "<td class=\"row-actions\"><button class=\"btn icon small\" data-action=\"item\"")
replace("settings:'Configuración'}", "settings:'Configuración',environments:'Ambientes',search:'Buscar activo'}")
replace("['dashboard','grades','grade','inventory'].includes(r)","['dashboard','grades','grade','inventory','environments'].includes(r)")
replace("if(r==='dashboard')html=dashboardView();else if", "if(r==='dashboard')html=dashboardView();else if(r==='environments')html=environmentsView();else if")
replace("Selecciona el grado y el paralelo para consultar o registrar bienes.","Selecciona un grado o ambiente para consultar o registrar bienes.")
replace("else if(r==='settings')html=settingsView();", "else if(r==='search')html=searchView();else if(r==='settings')html=settingsView();")
replace("case'item':itemModal(b.dataset.id);break;", "case'item':itemModal(b.dataset.id);break;case'item-delete':deleteModal(b.dataset.id);break;case'environment':openModal('Crear ambiente','Agrega un espacio de la unidad educativa.','environment-save',`<div class=\"full\">${input('Nombre del ambiente','name','','required maxlength=\"180\" placeholder=\"Ej. Laboratorio, Dirección, Sala de computación\"')}</div>`);break;")
replace("if(form.id!=='login-form'&&!form.dataset.form)return;", "if(form.id==='asset-search'){e.preventDefault();await searchAssets(form);return;}if(form.id!=='login-form'&&!form.dataset.form)return;")
replace("if(form.dataset.form==='snapshot')location.hash='document/'+result.id;else await route();", "if(form.dataset.form==='snapshot')location.hash='document/'+result.id;else if(form.dataset.form==='environment-save')location.hash='course/'+result.id;else await route();")
start=s.index('function documentView(')
end=s.index('function settingsView(',start)
s=s[:start]+'''function documentView(s){
 const d=s.document;
 const environment=!d.classroom.grade;
 return `<div class="print-controls"><a class="btn" href="#history/${d.classroom.id}">${icon('back')}Volver al historial</a>${btn('Imprimir / Guardar PDF','print','print',true)}<p>Documento #${s.id} · Solo lectura · ${datetime(s.created_at)}<br>Imprime en A4, escala 100 %, sin encabezados ni pies del navegador.</p></div>
 <article class="print-page">
 <header class="print-header"><div class="fe-logo"><img src="assets/fe-y-alegria.png" alt="Fe y Alegría"></div><strong>DIRECCIÓN DEPARTAMENTAL DE EDUCACIÓN DE ORURO<br>UNIDAD EDUCATIVA “LA KANTUTA 3”<br>ORURO - BOLIVIA</strong><img class="school-logo" src="assets/logo.jpeg" alt="Escudo de la Unidad Educativa La Kantuta 3"></header>
 <h1>${environment?'INVENTARIO DEL AMBIENTE':'INVENTARIO DEL AULA PEDAGÓGICA'} ${d.year}</h1><h2>${esc(courseName(d.classroom)).toUpperCase()}</h2>
 <p>En la ciudad de Oruro, se hicieron presentes en instalaciones de la Unidad Educativa “LA KANTUTA 3” los asesores de curso y/o responsables del ${environment?'ambiente':'aula'}, con el objeto de proceder con la entrega y recepción del mobiliario y equipos tecnológicos, en presencia de la Dirección de la Unidad Educativa, de acuerdo al siguiente detalle:</p>
 <table><colgroup><col style="width:5%"><col style="width:11%"><col style="width:9%"><col style="width:9%"><col style="width:38%"><col style="width:11%"><col style="width:17%"></colgroup><thead><tr><th>N°</th><th>Código</th><th>Cantidad</th><th>Unidad</th><th>Nombre, material, dimensiones, marca, modelo y accesorios</th><th>Estado</th><th>Observaciones</th></tr></thead><tbody>${d.items.map((x,i)=>`<tr><td>${i+1}</td><td>${esc(x.code)}</td><td>${x.quantity}</td><td>${esc(x.unit)}</td><td class="justify"><strong>${esc(x.name)}</strong>${esc([x.material,x.dimensions,x.brand,x.model,x.accessories].filter(Boolean).join(' · '))}</td><td>${esc(x.condition)}</td><td class="justify">${esc(x.notes||'Sin observaciones')}</td></tr>`).join('')}</tbody></table>
 <p class="print-date">Oruro, ${new Date(d.date).toLocaleDateString('es-BO',{day:'numeric',month:'long',year:'numeric'})}</p>
 <section class="signatures">${[['outgoing','ENTREGUÉ CONFORME','SALIENTE'],['incoming','RECIBÍ CONFORME','ENTRANTE']].map(([key,label,extra])=>`<div class="signature"><div>${esc(d.signers[key].name)}<br>${esc(d.signers[key].position).toUpperCase()} ${extra}<br><b>${label}</b></div></div>`).join('')}<div class="signature"><div>${esc(d.signers.director.name)}<br><b>DIRECTOR DE LA UNIDAD EDUCATIVA</b></div></div></section>
 <p class="motto">“AMOR Y MÁS AMOR, QUE NUNCA DICE BASTA”</p></article>`;
}
''' + s[end:]
extra='''
function environmentCard(){
 const rooms=dashboard.classrooms.filter(c=>!c.grade);
 return `<a class="grade-card environment-card" href="#environments"><span class="grade-number">${icon('building')}</span>${icon('arrow').replace('<svg','<svg class="arrow"')}<h3>Otros ambientes</h3><p>Laboratorios, oficinas, depósitos y más</p><div class="grade-meta"><span><b>${rooms.length}</b> ambientes</span><span><b>${fmt(rooms.reduce((n,c)=>n+Number(c.units),0))}</b> bienes</span></div></a>`;
}
function environmentsView(){
 const rooms=dashboard.classrooms.filter(c=>!c.grade);
 return heading('Ambientes de la institución','Crea tantos ambientes como necesites. Cada uno tiene su propio inventario e historial.',`<a class="btn" href="#grades">${icon('back')}Grados</a>${btn('Crear ambiente','environment','plus',true)}`)+`<section class="panel"><div class="panel-head"><div><h2>Otros ambientes</h2><p>Laboratorio, salón de la banda, educación física, computación, secretaría, dirección…</p></div>${icon('building')}</div>${rooms.length?`<div class="grade-grid">${rooms.map(c=>`<a class="grade-card" href="#course/${c.id}"><div class="grade-number">${icon('building')}</div><h3>${esc(c.name)}</h3><p>${c.records} registros · ${fmt(c.units)} bienes</p><span class="text-link">Abrir inventario ${icon('arrow')}</span></a>`).join('')}</div>`:empty('Crea el primer ambiente','Selecciona “Crear ambiente” y escribe el nombre del espacio.','building')}</section>`;
}
function deleteModal(id){
 const x=inventory.find(x=>Number(x.id)===Number(id));if(!x)return;
 openModal('Eliminar activo','Confirma el activo que deseas quitar del inventario.','item-delete',`<div class="full"><p><strong>${esc(x.name)}</strong></p><p>Código: ${esc(x.code)} · Cantidad: ${x.quantity}</p><p>Ubicación: ${esc(courseName(course))}</p><p class="hint">El activo dejará de aparecer en el inventario y en las búsquedas. Los documentos históricos ya guardados conservarán su contenido.</p></div>`,{id:x.id,classroom_id:course.id,version:x.version},'Eliminar activo');
}
function searchView(){return heading('Buscar activo','Ingresa el código completo para saber en qué curso o ambiente se encuentra.')+`<section class="panel"><form id="asset-search" class="toolbar"><label class="field code-search">Código del activo<input name="code" required maxlength="80" placeholder="Ej. 00101" autocomplete="off"></label><button type="submit" class="btn primary">${icon('search')}Buscar</button></form><div class="detail-note">La búsqueda incluye todos los cursos y ambientes. Si ingresas S/C, se mostrarán los activos sin código.</div><div id="search-results" aria-live="polite">${empty('Encuentra cada activo','El resultado mostrará su ubicación actual.','search')}</div></section>`;}
let searchRequest=0;
async function searchAssets(form){
 const code=new FormData(form).get('code').trim();const request=++searchRequest;const result=$('#search-results');const submit=$('[type="submit"]',form);
 if(!code){toast('Ingresa el código del activo.',true);return;}
 submit.disabled=true;result.innerHTML='<div class="empty">Buscando activo…</div>';
 try{const rows=await api('search',null,{code});if(request!==searchRequest||!result.isConnected)return;
 result.innerHTML=rows.length?`<div class="table-wrap"><table><thead><tr><th>Código</th><th>Activo</th><th>Cantidad</th><th>Estado</th><th>Ubicación actual</th><th></th></tr></thead><tbody>${rows.map(x=>`<tr><td class="code">${esc(x.code)}</td><td><strong>${esc(x.name)}</strong></td><td>${x.quantity}<small>${esc(x.unit)}</small></td><td><span class="badge ${x.condition.toLowerCase()}">${esc(x.condition)}</span></td><td><strong>${esc(x.location_name||courseName(x))}</strong><small>${x.grade?'Aula pedagógica':'Ambiente de la institución'}</small></td><td><a class="btn small" href="#items/${x.classroom_id}">${icon('arrow')}Ver inventario</a></td></tr>`).join('')}</tbody></table></div><div class="table-foot">${rows.length} coincidencia(s) para ${esc(code)}</div>`:empty('No se encontró el activo','No hay un activo vigente con el código '+code+'. Revisa el código ingresado.','search');
 }catch(e){if(result.isConnected)result.innerHTML=`<div class="error">${esc(e.message)}</div>`;}finally{submit.disabled=false;}
}
'''
s=s.replace("document.addEventListener('click'",extra+"\ndocument.addEventListener('click'",1)
p.write_text(s,encoding='utf-8')
