PageSourceSearch

https://portal.easymarine.com.br/assets/TicketReclassificationPage_b8a463a0.js

js easymarine.com.br collected 2026-10-02 00:15:11 UTC 11,807 bytes, 1 lines download raw bytes

1import{L as e,bo as Y,Z as S,T as M,Y as A,dG as X,aP as ee,aQ as se,aR as O,aS as p,bC as P,aT as te,aV as x,cl as ae,aE as C,aF as N,aG as w,aH as T,aI as v,al as ne,a3 as R,a7 as re,eO as ie,O as j,a0 as f,a1 as ce,b9 as B,aa as _,R as E,d2 as oe,U as L,W as z,aJ as le}from"./index_480d3d6c.js";import{P as de}from"./PageContainer_4b587133.js";import{P as ue}from"./PageHeader_0a1f55fa.js";import"./design-tokens_ac19ed4e.js";function ge({message:s,onRetry:t}){return e.jsxs("div",{className:"flex flex-col items-center gap-3 rounded-md border border-destructive/40 bg-destructive/5 p-8 text-center",children:[e.jsx(Y,{className:"h-6 w-6 text-destructive"}),e.jsxs("p",{className:"text-sm text-destructive",children:["Falha ao carregar os tickets desta categoria.",s?` ${s}`:""]}),e.jsx(S,{variant:"outline",size:"sm",onClick:t,children:"Tentar novamente"})]})}const me=[{key:"total",label:"Tickets na categoria"},{key:"withoutSuggestion",label:"Sem sugestão"},{key:"suggested",label:"Aguardando confirmação"},{key:"rejected",label:"Decidir depois"},{key:"applied",label:"Reclassificados"}];function he({counts:s}){return e.jsx("div",{className:"grid grid-cols-2 md:grid-cols-5 gap-3",children:me.map(t=>e.jsx(M,{children:e.jsxs(A,{className:"p-4",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:t.label}),e.jsx("p",{className:"text-2xl font-semibold",children:s[t.key]})]})},t.key))})}function pe(s){return s==="applied"?"Reclassificado":s==="rejected"?"Decidir depois":s==="pending"?"Sugerido":"Sem sugestão"}function xe({rows:s,categories:t,selected:a,overrides:i,onToggle:n,onToggleAll:d,onOverride:c,onApplyOne:g}){const{openTicketSheet:m}=X();return e.jsxs(ee,{children:[e.jsx(se,{children:e.jsxs(O,{children:[e.jsx(p,{className:"w-10",children:e.jsx(P,{checked:s.length>0&&a.size===s.length,onCheckedChange:d,"aria-label":"Selecionar todos"})}),e.jsx(p,{className:"w-20",children:"Ticket"}),e.jsx(p,{children:"Assunto"}),e.jsx(p,{className:"w-28",children:"Criado"}),e.jsx(p,{className:"w-40",children:"Aberto por"}),e.jsx(p,{className:"w-64",children:"Categoria sugerida"}),e.jsx(p,{className:"w-24",children:"Confiança"}),e.jsx(p,{children:"Justificativa"}),e.jsx(p,{className:"w-32",children:"Situação"}),e.jsx(p,{className:"w-28"})]})}),e.jsx(te,{children:s.map(o=>{const l=i[o.ticketId]??o.suggestedCategoryId??"";return e.jsxs(O,{children:[e.jsx(x,{children:e.jsx(P,{checked:a.has(o.ticketId),onCheckedChange:()=>n(o.ticketId),"aria-label":`Selecionar ticket ${o.ticketNumber}`})}),e.jsx(x,{children:e.jsxs("button",{type:"button",className:"font-mono text-xs text-link hover:underline",onClick:r=>{r.stopPropagation(),m(o.ticketId)},title:"Abrir detalhe do ticket",children:["#",o.ticketNumber]})}),e.jsx(x,{className:"max-w-[24rem] truncate",title:o.subject,children:o.subject}),e.jsx(x,{className:"text-xs text-muted-foreground",children:ae(o.createdAt,"dd/MM/yyyy")??"-"}),e.jsx(x,{className:"max-w-[10rem] truncate text-xs",title:o.openedByName??void 0,children:o.openedByName??"-"}),e.jsx(x,{children:e.jsxs(C,{value:l,onValueChange:r=>c(o.ticketId,r),children:[e.jsx(N,{children:e.jsx(w,{placeholder:"Escolher categoria"})}),e.jsx(T,{children:t.map(r=>e.jsx(v,{value:r.value,children:r.label},r.value))})]})}),e.jsx(x,{className:"text-xs",children:o.confidence===null?"-":`${Math.round(o.confidence*100)}%`}),e.jsx(x,{className:"max-w-[20rem] text-xs text-muted-foreground",children:o.rationale??"-"}),e.jsx(x,{children:e.jsx(ne,{variant:o.status==="applied"?"default":"secondary",children:pe(o.status)})}),e.jsx(x,{children:e.jsx(S,{size:"sm",variant:"outline",disabled:!l||o.status==="applied",onClick:()=>g(o),children:"Confirmar"})})]},o.ticketId)})})]})}const F="__all__";function fe({teams:s,categories:t,openers:a,supportTeamId:i,fromCategoryId:n,openedById:d,isGenerating:c,onChangeTeam:g,onChangeCategory:m,onChangeOpener:o,onGenerate:l}){return e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-4 gap-3 items-end",children:[e.jsxs("div",{children:[e.jsx(R,{children:"Time de suporte"}),e.jsxs(C,{value:i??"",onValueChange:g,children:[e.jsx(N,{children:e.jsx(w,{placeholder:"Selecione"})}),e.jsx(T,{children:s.map(r=>e.jsx(v,{value:r.value,children:r.label},r.value))})]})]}),e.jsxs("div",{children:[e.jsx(R,{children:"Categoria de origem"}),e.jsxs(C,{value:n??"",onValueChange:m,children:[e.jsx(N,{children:e.jsx(w,{placeholder:"Selecione"})}),e.jsx(T,{children:t.map(r=>e.jsx(v,{value:r.value,children:r.label},r.value))})]})]}),e.jsxs("div",{children:[e.jsx(R,{children:"Colaborador"}),e.jsxs(C,{value:d??F,onValueChange:r=>o(r===F?"":r),disabled:a.length===0,children:[e.jsx(N,{children:e.jsx(w,{placeholder:"Todos"})}),e.jsxs(T,{children:[e.jsx(v,{value:F,children:"Todos"}),a.map(r=>e.jsx(v,{value:r.value,children:r.label},r.value))]})]})]}),e.jsxs(S,{onClick:l,disabled:!n||c,children:[c?e.jsx(re,{className:"mr-2 h-4 w-4 animate-spin"}):e.jsx(ie,{className:"mr-2 h-4 w-4"}),"Gerar sugestões (próximos 60)"]})]})}function je(s){const[t,a]=j.useState(),i=j.useMemo(()=>{const d=new Map;for(const c of s)c.openedById&&c.openedByName&&d.set(c.openedById,c.openedByName);return[...d.entries()].map(([c,g])=>({value:c,label:g})).sort((c,g)=>c.label.localeCompare(g.label))},[s]),n=j.useMemo(()=>t?s.filter(d=>d.openedById===t):s,[s,t]);return{openers:i,openedById:t,filteredRows:n,setOpenedById:a}}async function ye(s){let t=0;for(const a of s){if(a.ticketIds.length===0)continue;const{data:i,error:n}=await f.rpc("apply_ticket_category_suggestions",{_ticket_ids:a.ticketIds,_category_id:a.categoryId});if(n)throw n;t+=(i==null?void 0:i.applied)??0}return t}function V(s,t){const a=new Map;for(const i of s){const n=t[i.ticketId]??i.suggestedCategoryId;if(!n)continue;const d=a.get(n)??[];d.push(i.ticketId),a.set(n,d)}return[...a.entries()].map(([i,n])=>({categoryId:i,ticketIds:n}))}function ke(s){const t=ce(),a=()=>
1t.invalidateQueries({queryKey:["reclass-rows",s]}),i=B({mutationFn:async({fromCategoryId:c,limit:g=60})=>{const{data:m,error:o}=await f.functions.invoke("ticket-category-suggest",{body:{from_category_id:c,limit:g}});if(o)throw o;if(m!=null&&m.error)throw new Error(m.error);return m},onSuccess:c=>{_.success(`${c.processed} sugestões geradas. Restam ${c.remaining}.`),a()},onError:c=>_.error(c.message||"Falha ao gerar sugestões")}),n=B({mutationFn:c=>ye(c),onSuccess:c=>{_.success(`${c} ticket(s) reclassificado(s).`),a()},onError:c=>_.error(c.message||"Falha ao aplicar reclassificação")}),d=B({mutationFn:async c=>{const{error:g}=await f.rpc("reject_ticket_category_suggestions",{_ticket_ids:c});if(g)throw g},onSuccess:()=>{_.success("Sugestões marcadas para decidir depois."),a()},onError:c=>_.error(c.message||"Falha ao rejeitar sugestões")});return{generate:i,apply:n,reject:d}}const $=100;function H(s,t){const a=[];for(let i=0;i<s.length;i+=t)a.push(s.slice(i,i+t));return a}async function be(s,t){const a=new Map;for(const i of H(s,$)){const n=await f.from("ticket_history").select("ticket_id, user_id, created_at").in("ticket_id",i).eq("action","created").order("created_at",{ascending:!0});if(n.error)throw n.error;for(const d of n.data??[])!d.user_id||a.has(d.ticket_id)||a.set(d.ticket_id,d.user_id)}for(const[i,n]of t)!a.has(i)&&n&&a.set(i,n);return a}async function _e(s){const t=new Map;for(const a of H(s,$)){const i=await f.from("profiles").select("id, full_name").in("id",a);if(i.error)throw i.error;for(const n of i.data??[])n.full_name&&t.set(n.id,n.full_name)}return t}function ve(s){return E({queryKey:["reclass-rows",s],enabled:!!s,queryFn:async()=>{const[t,a,i]=await Promise.all([f.from("tickets").select("id, ticket_number, subject, created_at, assigned_to").eq("category_id",s).order("created_at",{ascending:!1}).limit(1e3),f.from("ticket_category_suggestions").select("ticket_id, suggested_category_id, confidence, rationale, status").eq("from_category_id",s),f.from("ticket_categories").select("id, name")]);if(t.error)throw t.error;if(a.error)throw a.error;if(i.error)throw i.error;const n=t.data??[],d=new Map((i.data??[]).map(l=>[l.id,l.name])),c=new Map((a.data??[]).map(l=>[l.ticket_id,l])),g=new Map(n.map(l=>[l.id,l.assigned_to])),m=await be(n.map(l=>l.id),g),o=await _e([...new Set(m.values())]);return n.map(l=>{const r=c.get(l.id),y=m.get(l.id)??null;return{ticketId:l.id,ticketNumber:l.ticket_number,subject:l.subject,createdAt:l.created_at??"",openedById:y,openedByName:y?o.get(y)??null:null,suggestedCategoryId:(r==null?void 0:r.suggested_category_id)??null,suggestedCategoryName:r!=null&&r.suggested_category_id?d.get(r.suggested_category_id)??null:null,confidence:(r==null?void 0:r.confidence)??null,rationale:(r==null?void 0:r.rationale)??null,status:(r==null?void 0:r.status)??"none"}})}})}function Se(){return E({queryKey:["reclass-support-teams"],staleTime:5*60*1e3,queryFn:async()=>{const{data:s,error:t}=await f.from("support_teams").select("id, name").order("name");if(t)throw t;return(s??[]).map(a=>({value:a.id,label:a.name}))}})}function Ce(s){return E({queryKey:["reclass-categories",s],enabled:!!s,staleTime:5*60*1e3,queryFn:async()=>{const{data:t,error:a}=await f.from("ticket_categories").select("id, name, is_active").eq("support_team_id",s).order("name");if(a)throw a;const i=t??[];return{all:i.map(n=>({value:n.id,label:n.is_active?n.name:`${n.name} (inativa)`})),active:i.filter(n=>n.is_active).map(n=>({value:n.id,label:n.name}))}}})}function Ne(s){return{total:s.length,suggested:s.filter(t=>t.status==="pending").length,applied:s.filter(t=>t.status==="applied").length,rejected:s.filter(t=>t.status==="rejected").length,withoutSuggestion:s.filter(t=>t.status==="none").length}}function Be(){const[s,t]=j.useState(),[a,i]=j.useState(),[n,d]=j.useState(new Set),[c,g]=j.useState({}),{data:m=[]}=Se(),{data:o}=Ce(s),l=ve(a),{generate:r,apply:y,reject:q}=ke(a),D=j.useMemo(()=>l.data??[],[l.data]),{openers:G,openedById:K,filteredRows:k,setOpenedById:I}=je(D),Q=j.useMemo(()=>Ne(k),[k]),U=u=>d(h=>{const b=new Set(h);return b.has(u)?b.delete(u):b.add(u),b}),J=()=>d(u=>u.size===k.length?new Set:new Set(k.map(h=>h.ticketId))),Z=u=>{const h=V([u],c);h.length>0&&y.mutate(h)},W=()=>{const u=V(k.filter(h=>n.has(h.ticketId)),c);u.length!==0&&y.mutate(u,{onSuccess:()=>d(new Set)})};return e.jsxs(de,{children:[e.jsx(ue,{icon:e.jsx(oe,{className:"h-6 w-6 text-primary"}),title:"Reclassificação de Atendimentos",description:"Ferramenta transitória: a IA sugere a categoria mais adequada e o gestor de CS&S confirma."}),e.jsxs(M,{children:[e.jsx(L,{children:e.jsx(z,{className:"text-base",children:"Origem"})}),e.jsx(A,{className:"space-y-4",children:e.jsx(fe,{teams:m,categories:(o==null?void 0:o.all)??[],openers:G,supp
1ortTeamId:s,fromCategoryId:a,openedById:K,isGenerating:r.isPending,onChangeTeam:u=>{t(u),i(void 0),I(),d(new Set)},onChangeCategory:u=>{i(u),I(),d(new Set)},onChangeOpener:u=>{I(u||void 0),d(new Set)},onGenerate:()=>a&&r.mutate({fromCategoryId:a})})})]}),a&&e.jsx(he,{counts:Q}),a&&e.jsxs(M,{children:[e.jsxs(L,{className:"flex-row items-center justify-between gap-2",children:[e.jsx(z,{className:"text-base",children:"Tickets"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(S,{variant:"outline",size:"sm",disabled:n.size===0||q.isPending,onClick:()=>q.mutate([...n],{onSuccess:()=>d(new Set)}),children:"Decidir depois"}),e.jsx(S,{size:"sm",disabled:n.size===0||y.isPending,onClick:W,children:"Confirmar selecionados"})]})]}),e.jsx(A,{className:"overflow-x-auto",children:l.isLoading?e.jsx(le,{className:"h-64 w-full"}):l.isError?e.jsx(ge,{message:l.error instanceof Error?l.error.message:void 0,onRetry:()=>void l.refetch()}):e.jsx(xe,{rows:k,categories:(o==null?void 0:o.active)??[],selected:n,overrides:c,onToggle:U,onToggleAll:J,onOverride:(u,h)=>g(b=>({...b,[u]:h})),onApplyOne:Z})})]})]})}export{Be as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.