📊 Meu painel de vendas

Clique no cartão para abrir. Use ⋯ para mover ou acessar outras ações; você também pode arrastar entre etapas. Abra os detalhes para consultar observações e fotos. Os dados são sincronizados com o servidor. Confira o indicador de salvamento e mantenha uma cópia de segurança completa.
↕ Arraste e solte para reorganizar · ajuste pela borda inferior

Agenda Óticas Carol

Entre com o acesso da sua loja.

Sou vendedor · cadastrar ou acessar meu painel

Acesso aguardando aprovação

Sua conta ainda não foi liberada por um administrador do Grupo Feletto. Não é necessário fazer outro cadastro.

Para acompanhar, toque em “Verificar de novo”. Se precisar de ajuda, procure o responsável pela gestão da sua loja. O sistema não informa um prazo de aprovação.

;prefix.style.cssText='font-size:15px;white-space:nowrap';input.parentNode.insertBefore(wrap,input);wrap.append(prefix,input);input.style.flex='1'}const total=document.createElement('p');total.id='mfCashTotal';total.style.cssText='font-size:18px;font-weight:700;margin:12px 0';const refresh=()=>{const amount=m.campos.filter(f=>f.tipo==='valor').reduce((v,f)=>v+(Number(o.querySelector('[id="mf_'+f.id+'"]')?.value)||0),0);total.textContent='Total automático: '+amount.toLocaleString('pt-BR',{style:'currency',currency:'BRL'})};o.querySelector('.task-field-grid').append(total);o.querySelectorAll('#mfForm input[type=number]').forEach(input=>input.addEventListener('input',refresh));refresh()} o.querySelector('#mfHist').innerHTML=(r.hist||[]).slice().reverse().slice(0,8).map(x=>'
  • '+new Date(x.t).toLocaleString('pt-BR',{dateStyle:'short',timeStyle:'short'})+' — '+mmEsc(x.a)+' · '+mmEsc(x.by?.email||'Autor não registrado')+'
  • ').join(''); o.querySelector('#mfC').onclick=()=>o.remove(); if(!canMod(m)||cashClosed(m,r)||locked){o.querySelectorAll('#mfForm input,#mfForm select,#mfForm textarea').forEach(x=>x.disabled=true);o.querySelectorAll('[type=submit],#mfDel,#mfImgRm').forEach(x=>x.hidden=true);const h=o.querySelector('h2');if(h)h.textContent='Visualizar registro'} let img=r.imagem||'',ipath=r.imagemPath||'';const ie=o.querySelector('#mfImg'),rm=o.querySelector('#mfImgRm'); const fileInput=o.querySelector('#mfFile'),drop=o.querySelector('#mfDrop'),uploadStatus=o.querySelector('#mfUploadStatus'),saveImageBtn=o.querySelector('[type=submit]');let imageVersion=0; let extraPhotos=Array.isArray(r.imagens)?r.imagens.map(p=>({...p})):[],pendingPhotos=[]; const gallery=o.querySelector('#mfGallery'),extraInput=o.querySelector('#mfFilesExtra'); const drawGallery=()=>{gallery.innerHTML='';const count=document.createElement('div');count.className='feletto-gallery-count';count.textContent=(extraPhotos.length+pendingPhotos.length+((img||ipath)?1:0))+' foto(s) anexada(s)';gallery.append(count); if(img||ipath){const tile=document.createElement('div');tile.className='feletto-photo';const legacy=document.createElement('img');legacy.alt='Foto anterior';legacy.title='Clique para ampliar';if(ipath){legacy.dataset.ipath=ipath;imgSrc(ipath).then(u=>{if(u)legacy.src=u})}else legacy.src=img;const del=document.createElement('button');del.type='button';del.textContent='×';del.title='Remover foto anterior';del.hidden=!canMod(m)||cashClosed(m,r)||locked;del.onclick=()=>{rm.click();drawGallery()};tile.append(legacy,del);gallery.append(tile)} [...extraPhotos.map((p,i)=>({p,i,stored:true})),...pendingPhotos.map((p,i)=>({p,i,stored:false}))].forEach(({p,i,stored})=>{ const tile=document.createElement('div');tile.className='feletto-photo'; const img=document.createElement('img');img.alt=p.nome||'Imagem anexada';img.title='Clique para ampliar'; if(stored){if(p.path){img.dataset.ipath=p.path;imgSrc(p.path).then(u=>{if(u)img.src=u})}else if(p.data)img.src=p.data} else img.src=p.preview; img.onclick=null; const remove=document.createElement('button');remove.type='button';remove.textContent='×';remove.title='Remover anexo';remove.hidden=!canMod(m)||cashClosed(m,r)||locked; remove.onclick=()=>{if(stored)extraPhotos.splice(i,1);else{URL.revokeObjectURL(p.preview);pendingPhotos.splice(i,1)}drawGallery()}; tile.append(img,remove);gallery.append(tile); }); }; const addExtras=files=>{ if(!canMod(m)||cashClosed(m,r)||locked)return; for(const f of Array.from(files||[])){ if(!f.type.startsWith('image/'))continue; if(f.size>8e6){toast('Cada imagem deve ter até 8 MB.');continue} pendingPhotos.push({file:f,preview:URL.createObjectURL(f),nome:f.name||'Print colado'}); } drawGallery(); }; extraInput.onchange=e=>{addExtras(e.target.files);extraInput.value=''}; o.addEventListener('paste',e=>{if(!canMod(m)||cashClosed(m,r))return;const files=Array.from(e.clipboardData?.items||[]).filter(i=>i.kind==='file'&&i.type.startsWith('image/')).map(i=>i.getAsFile());if(files.length){e.preventDefault();addExtras(files)}},true); drawGallery(); const acceptImage=fl=>{if(!fl||fileInput.disabled)return;if(!fl.type.startsWith('image/')){toast('Escolha uma imagem ou um print.');return}if(fl.size>8e6){toast('A imagem deve ter até 8 MB.');return}const version=++imageVersion;saveImageBtn.disabled=true;uploadStatus.textContent='Preparando imagem…';const reader=new FileReader();reader.onerror=()=>{if(version===imageVersion){saveImageBtn.disabled=false;uploadStatus.textContent='Não foi possível ler a imagem. Tente novamente.'}};reader.onload=()=>{const source=new Image();source.onerror=reader.onerror;source.onload=()=>{if(version!==imageVersion)return;try{mmImg(fl,d=>{if(version!==imageVersion)return;img=d;ipath='';ie.removeAttribute('data-ipath');ie.src=d;ie.classList.add('visible');rm.hidden=false;saveImageBtn.disabled=false;uploadStatus.textContent=(fl.name||'Print colado')+' · pronto para salvar'})}catch(err){reader.onerror()}};source.src=reader.result};reader.readAsDataURL(fl)}; fileInput.onchange=e=>acceptImage(e.target.files[0]); drop.addEventListener('dragover',e=>{e.preventDefault();if(!fileInput.disabled){e.dataTransfer.dropEffect='copy';drop.style.borderColor='#154d80';drop.style.background='#e6f0fb'}}); drop.addEventListener('dragleave',()=>{drop.style.borderColor='#a9bfd2';drop.style.background='#f4f8fc'}); drop.addEventListener('drop',e=>{e.preventDefault();e.stopPropagation();drop.style.borderColor='#a9bfd2';drop.style.background='#f4f8fc';const files=Array.from(e.dataTransfer.files||[]);addExtras(files)}); /* O listener de colar imagens na galeria já trata múltiplos arquivos. */ rm.onclick=()=>{imageVersion++;saveImageBtn.disabled=false;fileInput.value='';uploadStatus.textContent='Uma imagem por registro · até 8 MB';img='';ipath='';ie.classList.remove('visible');ie.removeAttribute('src');ie.removeAttribute('data-ipath');rm.hidden=true}; if(!nova)o.querySelector('#mfDel').onclick=()=>{if(!flag('excluir')||!canMod(m)||!reviewCanEdit(m,r))return deny();if(!allowCashChange(m,r))return;if(confirm('Mover este registro para a lixeira?')){putTrash('record',{moduleId:m.id,record:r},String(Object.values(r.valores)[0]||'Registro'),m.id);m.regs=m.regs.filter(x=>x!==r);saveM();o.remove();renderMod();toast('Registro excluído')}}; o.querySelector('#mfForm').onsubmit=async e=>{e.preventDefault();if(!canMod(m)||!reviewCanEdit(m,r))return deny();if(!allowCashChange(m,r))return;const candidate={...r,criado:r.criado||new Date().toISOString(),valores:Object.fromEntries(m.campos.map(f=>[f.id,mmRead(f,'mf_')]))};if(!allowCashChange(m,candidate))return;const antes=JSON.stringify(r.valores),prev=r.status;const nextValues=candidate.valores,nextStatus=o.querySelector('#mfEtapa').value;if(pendingPhotos.length){ if(!window.__img){toast('Armazenamento de imagens indisponível. Nenhum registro foi salvo.');return} saveImageBtn.disabled=true;uploadStatus.textContent='Enviando '+pendingPhotos.length+' imagens…'; try{ for(const photo of [...pendingPhotos]){ const data=await new Promise((resolve,reject)=>{mmImg(photo.file,d=>resolve(d));setTimeout(()=>reject(Error('Tempo limite de imagem')),30000)}); const path=await sendImg(data);if(!path)throw Error('Falha no armazenamento'); extraPhotos.push({path,nome:photo.nome});const pos=pendingPhotos.indexOf(photo);if(pos>=0)pendingPhotos.splice(pos,1); } }catch(err){uploadStatus.textContent='Falha no envio: '+pendingPhotos.length+' foto(s) pendente(s). As fotos enviadas foram preservadas; tente novamente.';drawGallery();toast('Falha ao enviar anexos. '+pendingPhotos.length+' foto(s) pendente(s).');saveImageBtn.disabled=false;return} saveImageBtn.disabled=false; } r.valores=nextValues;r.status=nextStatus;r.imagem=img;r.imagemPath=ipath;r.imagens=extraPhotos.map(p=>({path:p.path,nome:p.nome}));if(reviewModule(m))r.conferencia={estado:'pendente',motivo:'',em:new Date().toISOString(),por:window.__actor?.email||''}; if(nova){r.criado=new Date().toISOString();mmHist(r,'Registro criado');m.regs.push(r);recordAudit('Registro criado',r.id,m.id)}else{if(prev!==r.status)mmHist(r,'Movido: '+etapaNome(m,prev)+' → '+etapaNome(m,r.status));if(antes!==JSON.stringify(r.valores))mmHist(r,'Dados editados')} saveM();o.remove();renderMod();toast('Registro salvo')}} function csvEsc(v){return FelettoCore.csvCell(v)} function exportMod(m){if(!flag('backup')||!seeMod(m))return deny();const cols=m.campos,fv=(f,v)=>v===''||v==null?'':f.tipo==='valor'?Number(v).toFixed(2).replace('.',','):f.tipo==='numero'?String(v).replace('.',','):f.tipo==='data'?String(v).split('-').reverse().join('/'):v; const linhas=[cols.map(f=>f.nome).concat(['Etapa','Criado em']).map(csvEsc).join(';')].concat(moduleRows(m).slice().sort((a,b)=>String(a.criado||'').localeCompare(String(b.criado||''))).map(r=>cols.map(f=>fv(f,r.valores[f.id])).concat([etapaNome(m,r.status),r.criado?new Date(r.criado).toLocaleDateString('pt-BR'):'']).map(csvEsc).join(';'))); const blob=new Blob(['\ufeff'+linhas.join('\r\n')],{type:'text/csv;charset=utf-8'}),a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=m.nome.replace(/[^\w\- ]+/g,'')+'-'+new Date().toISOString().slice(0,10)+'.csv';document.body.append(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(a.href),1500);toast('Arquivo gerado com os registros filtrados. Abra no Excel.')} function fechamentoCaixa(m){return closeCash(m)} function modAddCol(m){if(!canMod(m)||!flag('config'))return deny();if(hasClosedCash(m)){toast('Reabra o caixa antes de alterar suas colunas.');return}const l=prompt('Nome da nova coluna:');if(!l||!l.trim())return;m.colunas.push(['c'+nid(),l.trim().slice(0,28)]);saveM();renderMod();toast('Coluna criada')} function modRenameCol(m,id){if(!canMod(m)||!flag('config'))return deny();if(hasClosedCash(m)){toast('Reabra o caixa antes de alterar suas colunas.');return}const c=m.colunas.find(x=>x[0]===id);if(!c)return;const l=prompt('Novo nome da coluna:',c[1]);if(!l||!l.trim())return;c[1]=l.trim().slice(0,28);saveM();renderMod();toast('Coluna renomeada')} function modRemoveCol(m,id){if(!canMod(m)||!flag('config'))return deny();if(hasClosedCash(m)){toast('Reabra o caixa antes de alterar suas colunas.');return}const i=m.colunas.findIndex(x=>x[0]===id);if(i<0)return;if(m.colunas.length===1){toast('Mantenha ao menos uma coluna no quadro.');return}const d=m.colunas.find(x=>x[0]!==id);if(!confirm('Excluir a coluna “'+m.colunas[i][1]+'”? Os cartões serão movidos para “'+d[1]+'”.'))return;m.regs.forEach(r=>{if(r.status===id)r.status=d[0]});m.colunas=m.colunas.filter(x=>x[0]!==id);saveM();renderMod();toast('Coluna removida')} function modMoveCol(m,id,dir){if(!canMod(m)||!flag('config'))return deny();if(hasClosedCash(m)){toast('Reabra o caixa antes de alterar suas colunas.');return}const f=m.colunas.findIndex(x=>x[0]===id),to=f+dir;if(f<0||to<0||to>=m.colunas.length)return;[m.colunas[f],m.colunas[to]]=[m.colunas[to],m.colunas[f]];saveM();renderMod();toast('Ordem das colunas atualizada')} function wireBoard(pane,m){const find=id=>m.regs.find(r=>r.id===id),move=(r,s,msg)=>{if(!canMod(m)||!reviewCanEdit(m,r))return deny();if(!allowCashChange(m,r))return;const prev=r.status;r.status=s;mmHist(r,'Movido: '+etapaNome(m,prev)+' → '+etapaNome(m,s));saveM();renderMod();toast(msg||'Cartão movido')}; const eb=pane.querySelector('[data-a=etapas]');if(eb)eb.onclick=()=>modAddCol(m);if(!canMod(m))pane.querySelectorAll('.task-card').forEach(c=>c.draggable=false); pane.querySelectorAll('.kanban-column[data-c]').forEach(col=>{col.addEventListener('dragover',e=>{e.preventDefault();col.classList.add('over')});col.addEventListener('dragleave',()=>col.classList.remove('over'));col.addEventListener('drop',e=>{e.preventDefault();col.classList.remove('over');const r=find(e.dataTransfer.getData('text/plain'));if(r&&r.status!==col.dataset.c)move(r,col.dataset.c)})}); pane.querySelectorAll('.task-card[data-r]').forEach(c=>{const r=find(c.dataset.r);c.addEventListener('dragstart',e=>e.dataTransfer.setData('text/plain',r.id));onOpen(c,()=>openRec(m,r)); c.querySelectorAll('[data-mv]').forEach(b=>b.onclick=()=>{const i=m.colunas.findIndex(x=>x[0]===r.status),n=m.colunas[i+Number(b.dataset.mv)];if(n)move(r,n[0])}); const ed=c.querySelector('[data-edit]');if(ed)ed.onclick=()=>openRec(m,r);const dn=c.querySelector('[data-done]');if(dn)dn.onclick=()=>move(r,m.colunas[m.colunas.length-1][0],'Registro concluído')}); pane.querySelectorAll('[data-cm]').forEach(b=>b.onclick=()=>modMoveCol(m,b.dataset.col,Number(b.dataset.cm))); pane.querySelectorAll('[data-cr]').forEach(b=>b.onclick=()=>modRenameCol(m,b.dataset.cr)); pane.querySelectorAll('[data-cx]').forEach(b=>b.onclick=()=>modRemoveCol(m,b.dataset.cx))} function openFields(target){if(!flag('config'))return deny();if(target==='caixa'&&hasClosedCash(MODULOS.find(m=>m.id==='caixa'))){toast('Reabra os dias fechados antes de alterar os campos do caixa.');return}const lab=target==='lab',m=lab?null:MODULOS.find(x=>x.id===target);if(!lab&&!m)return;const work=JSON.parse(JSON.stringify(lab?LABCAMPOS:m.campos)); const o=mmOverlay(`

    Campos do cartão · ${lab?'Laboratório':mmEsc(m.nome)}

    Dê um nome e escolha o tipo de cada campo. Em “Lista de opções”, separe as opções por vírgula.${lab?' Os campos de lente, armação, laboratório e WhatsApp já existem e continuam.':''}

    `); const rows=o.querySelector('#fedRows'); const draw=()=>{rows.innerHTML=work.map((f,i)=>`
    `).join('')||'

    Nenhum campo ainda.

    '}; rows.addEventListener('input',e=>{const row=e.target.closest('.fed-row');if(!row)return;const f=work[row.dataset.i];if(e.target.classList.contains('fed-n'))f.nome=e.target.value;if(e.target.classList.contains('fed-o'))f.opcoes=e.target.value.split(',').map(x=>x.trim()).filter(Boolean)}); rows.addEventListener('change',e=>{if(e.target.classList.contains('fed-t')){work[e.target.closest('.fed-row').dataset.i].tipo=e.target.value;draw()}}); rows.addEventListener('click',e=>{const row=e.target.closest('.fed-row');if(!row)return;const i=Number(row.dataset.i);if(e.target.closest('[data-rm]'))work.splice(i,1);else if(e.target.closest('[data-up]')&&i>0)[work[i-1],work[i]]=[work[i],work[i-1]];else if(e.target.closest('[data-dn]')&&i{work.push({id:nid(),nome:'',tipo:'texto'});draw();const n=rows.querySelectorAll('.fed-n');n[n.length-1].focus()}; o.querySelector('#fedC').onclick=()=>o.remove(); o.querySelector('#fedOk').onclick=()=>{if(work.some(f=>!String(f.nome).trim())){toast('Dê um nome a todos os campos.');return}work.forEach(f=>{f.nome=f.nome.trim();if(f.tipo!=='lista')delete f.opcoes});if(lab)LABCAMPOS=work;else m.campos=work;saveM();o.remove();renderMod();renderBoard();toast('Campos salvos')}; draw()} {const b=document.createElement('button');b.className='btn';b.id='labFieldsBtn';b.type='button';b.textContent='Campos do cartão';b.onclick=()=>openFields('lab');const ref=$('#manageLabelsBoardBtn');ref.parentNode.insertBefore(b,ref.nextSibling)} function cardDisplayTools(){const detailed=localStorage.getItem('agenda-leve-card-display')==='detailed';document.body.classList.toggle('cards-detailed',detailed);document.querySelectorAll('.nc-details').forEach(el=>el.open=detailed);document.querySelectorAll('[data-card-display]').forEach(b=>{const selected=(b.dataset.cardDisplay==='detailed')===detailed;b.classList.toggle('selected',selected);b.setAttribute('aria-pressed',String(selected))})} function cardDisplayHTML(){return '
    '} document.addEventListener('click',e=>{const b=e.target.closest('[data-card-display]');if(!b)return;localStorage.setItem('agenda-leve-card-display',b.dataset.cardDisplay);cardDisplayTools()}); {const tools=document.querySelector('#boardPanel .board-tools');tools.insertAdjacentHTML('beforeend',cardDisplayHTML());tools.insertAdjacentHTML('afterend','
    '+[['all','Todos'],['late','Atrasados'],['today','Entrega hoje'],['none','Sem prazo']].map(([id,label])=>'').join('')+'Filtros de prazo mostram pedidos em aberto.
    ');document.querySelectorAll('[data-deadline]').forEach(b=>b.addEventListener('click',()=>{quickDeadline=b.dataset.deadline;renderBoard()}))} applyPerm();setActive(ACTIVE);queueSweep(); {let d0=new Date().toDateString();setInterval(()=>{const d=new Date().toDateString();if(d!==d0){d0=d;try{renderBoard();renderDailyAgenda()}catch{}}},60000)} window.__goto=(mod,reg)=>{if(!mod||!MODULOS.some(x=>x.id===mod))return;setActive(mod);const m=MODULOS.find(x=>x.id===mod),r=reg&&m.regs.find(x=>x.id===reg);if(r)setTimeout(()=>openRec(m,r),250)}; if(window.__store?.name){const h=$('.brandhint');if(h)h.textContent=window.__store.name}refreshTaskLabelOptions();render();renderBoard();renderToday(); {const last=Number(localStorage.getItem('agenda-leve-lastbackup')||0);if(tasks.length&&Date.now()-last>14*864e5)setTimeout(()=>toast('Faça uma cópia de segurança completa (⇩), incluindo as imagens.'),1500)} };