Art StudioCreatividad que se administra

Iniciar sesión

Ingresa con tu cuenta de empleado o administrador.

Tus cuentas creadas aquí se guardan en este navegador. La próxima vez podrás entrar con el mismo usuario y contraseña.

`);w.document.close()} async function renderEmployees(){const v=$("#view-employees");state.employees=await listEmployees();v.innerHTML=`
${employeesTable(state.employees)}
`;$("#newEmployee").onclick=employeeModal;$("#backupBtn").onclick=exportBackup;$("#restoreBtn").onclick=()=>$("#restoreFile").click();$("#salesPeriod").onchange=loadEmployeeSales;await loadEmployeeSales();$$(".employee-detail").forEach(b=>b.onclick=()=>showEmployee(b.dataset.id))} async function period(){const end=new Date(),start=new Date(0);const p=$("#salesPeriod")?.value||"today";if(p==="today")start.setHours(0,0,0,0);if(p==="week"){start.setDate(end.getDate()-6);start.setHours(0,0,0,0)}if(p==="month"){start.setDate(1);start.setHours(0,0,0,0)}return [start.toISOString(),end.toISOString()]} async function loadEmployeeSales(){const [from,to]=await period();for(const e of state.employees){const s=await employeeSales(e.id,from,to);const td=$(`[data-sales="${e.id}"]`),tm=$(`[data-money="${e.id}"]`);if(td)td.textContent=s.length;if(tm)tm.textContent=money(s.reduce((a,x)=>a+Number(x.total),0))}} async function showEmployee(id){const [from,to]=await period(),e=state.employees.find(x=>x.id===id),s=await employeeSales(id,from,to);$("#employeeDetail").innerHTML=employeeCard(e,s)} async function employeeModal(){const d=openModal("Crear nuevo empleado",`
`,``);$("#saveEmployee").onclick=async()=>{try{const {data:session}=await sb.auth.getSession();const res=await fetch(`${window.ART_STUDIO_CONFIG.SUPABASE_URL}/functions/v1/admin-user`,{method:"POST",headers:{"Content-Type":"application/json","Authorization":`Bearer ${session.session.access_token}`},body:JSON.stringify({full_name:$("#eName").value.trim(),username:$("#eUser").value.trim(),password:$("#ePass").value})});if(!res.ok)throw Error(await res.text());d.close();toast("Empleado creado");await renderEmployees()}catch(e){toast(e.message)}}} async function renderChat(){const v=$("#view-chat");const employees=state.profile.role==="admin"?state.employees:await listEmployees();v.innerHTML=`
💬 Chat general
${employees.filter(e=>e.id!==state.user.id).map(e=>`
👤 ${esc(e.full_name)}
`).join("")}
Chat general
`;$$(".user-chat").forEach(b=>b.onclick=()=>{$$(".user-chat").forEach(x=>x.classList.remove("selected"));b.classList.add("selected");state.chatRoom=b.dataset.room;$("#chatTitle").textContent=b.dataset.room==="general"?"Chat general":"Chat privado";loadMessages()});$("#chatForm").onsubmit=async e=>{e.preventDefault();const x=$("#chatInput").value.trim();if(!x)return;try{await sendMessage(x,state.chatRoom);$("#chatInput").value="";await loadMessages()}catch(err){toast(err.message)}};await loadMessages()} async function loadMessages(){const rows=await messages(state.chatRoom);$("#messages").innerHTML=messageList(rows,state.user.id);$("#messages").scrollTop=$("#messages").scrollHeight} function subscribeRealtime(){sb.channel("art-studio-data").on("postgres_changes",{event:"*",schema:"public",table:"products"},()=>refreshAll()).on("postgres_changes",{event:"*",schema:"public",table:"clients"},()=>refreshAll()).on("postgres_changes",{event:"*",schema:"public",table:"invoices"},()=>refreshAll()).on("postgres_changes",{event:"*",schema:"public",table:"profiles"},()=>{if(state.profile.role==="admin")renderEmployees()}).subscribe(s=>{$("#connectionStatus").textContent=s==="SUBSCRIBED"?"● En tiempo real":"● Sin conexión"})} async function boot(){const p=await profile();if(!p){$("#loginView").classList.remove("hidden");return}state.user=await sb.auth.getUser().then(x=>x.data.user);state.profile=p;$("#loginView").classList.add("hidden");$("#appView").classList.remove("hidden");$("#sessionName").textContent=p.full_name;$("#sessionRole").textContent=p.role==="admin"?"Administrador":"Empleado";$("#sessionAvatar").textContent=p.full_name?.[0]?.toUpperCase()||"A";if(p.role!=="admin")$$(".admin-only").forEach(x=>x.classList.add("hidden"));await refreshAll();setView("home");subscribeRealtime();await initChat(state.user,()=>state.view==="chat"&&loadMessages())} $("#loginForm").onsubmit=async e=>{e.preventDefault();$("#loginError").textContent="";try{await signIn($("#loginUser").value.trim(),$("#loginPassword").value);location.reload()}catch(err){$("#loginError").textContent="Usuario o contraseña incorrectos."}} $("#logoutBtn").onclick=async()=>{stopChat();await signOut();location.reload()} $("#mainNav").onclick=e=>{const b=e.target.closest("[data-view]");if(b){setView(b.dataset.view);$(".sidebar").classList.remove("open")}} $("#mobileMenuBtn").onclick=()=>$(".sidebar").classList.toggle("open") $("#importFile").onchange=e=>e.target.files[0]&&importSpreadsheet(e.target.files[0]) $("#restoreFile").onchange=async e=>{if(!e.target.files[0])return;if(!confirm("Restaurar reemplazará los datos respaldados. ¿Continuar?"))return;try{await restoreBackup(e.target.files[0]);toast("Copia restaurada");await refreshAll()}catch(err){toast(err.message)}} boot().catch(e=>{console.error(e);$("#loginError").textContent="No se pudo conectar con la base de datos. Revisa la configuración."})