PageSourceSearch

https://syncreo.ai/assets/Rules-D5VGdzE3.js

js syncreo.ai collected 2026-10-05 14:46:17 UTC 14,702 bytes, 1 lines download raw bytes

1import{ak as le,aW as oe,r as d,h as $,db as ce,an as h,o as x,j as e,B as l,bT as de,af as p,C as L,v as V,a4 as D,X as I,P as ue,I as O,as as C,au as N,av as b,aw as T,ax as w,T as me,x as xe,ag as pe,ah as ge,ai as he,aj as ye,L as B,bs as je}from"./entry-JtbrXxFu.js";import{R as ve}from"./index-mI1byp4d.js";import{P as fe}from"./seatState-BuDsX7at.js";import{A as Ce}from"./alarm-clock-DDwAqTj2.js";import{S as Ne}from"./useDebouncedState-BIzAC1cd.js";import{P as be}from"./color-swatch-picker-ByXvWZyL.js";import"./SidebarShell-DbQyiDvc.js";import"./ComposioExplainerModal-6a6JC41D.js";import"./header-Bc3q6Clk.js";import"./FirefliesUserEmailModal-NexWGgUb.js";import"./collapsible-CDI1IVAD.js";import"./assistant-personas-BhwMY7gk.js";import"./puzzle-C7olF60I.js";import"./parseLocalDay-BSh_QUPX.js";import"./tag-WjH8fg76.js";import"./crown-CLY6lTY-.js";import"./skeleton-Dz9j7B4F.js";import"./AIConsentModal-DpEDvM57.js";const H={sender:"Sender",domain:"Domain",subject_pattern:"Subject contains",content_pattern:"Email contains"},se=[{value:"sender",label:"Sender email"},{value:"domain",label:"Sender domain"},{value:"subject_pattern",label:"Subject contains"},{value:"content_pattern",label:"Email contains"}],te={deep_triage:"Auto-detected",ai_suggestion:"AI suggestion",user_manual:"Manual",user_voice:"Voice",user_correction:"From correction",admin_rule:"Manual",system:"Auto-detected",pattern_detection:"Auto-detected"};function Te(n){if(!n||typeof n!="object")return"";const r=Object.entries(n).filter(([,c])=>typeof c=="number"&&c>0);return r.length===0?"":r.map(([c,A])=>`${A} ${c.replace(/_/g," ")}`).join(", ")}function Ie(){const{toast:n}
1=le(),r=oe(),[c,A]=d.useState(""),[ae,y]=d.useState(null),[o,j]=d.useState({conditionType:"",conditionValue:"",targetCategory:""}),[ie,E]=d.useState(!1),[u,S]=d.useState({conditionType:"domain",conditionValue:"",targetCategory:"",isException:!1}),{data:k,isLoading:q}=$({queryKey:["/api/user/rule-cards"]}),{data:_,isLoading:F}=$({queryKey:["/api/classification-rules",{isActive:!0,limit:200}],queryFn:async()=>(await x("GET","/api/classification-rules?isActive=true&limit=200")).json()}),{data:v=[],isLoading:K}=$({queryKey:["/api/email-learning/suggestions"]}),{data:z=[]}=$({queryKey:["/api/email-categories"]}),P=(k==null?void 0:k.ruleCards)??[],R=(_==null?void 0:_.items)??[],U=d.useRef(!1);d.useEffect(()=>{q||F||K||U.current||(U.current=!0,ce({kind:"learned-rules.opened",ruleCount:P.length+R.length,pendingCount:v.length}))},[q,F,K,P.length,R.length,v.length]);const f=d.useMemo(()=>{const s=P.map(a=>({id:a.id,source:"rule-card",ruleType:a.conditionType,pattern:a.conditionValue,targetCategory:a.targetCategory,isException:a.isException,sourceLabel:te[a.source]??"Manual",confidence:a.confidence??100})),t=R.map(a=>({id:a.id,source:"classification",ruleType:a.ruleType,pattern:a.pattern,targetCategory:a.targetCategory??"",isException:!1,sourceLabel:te[a.createdFrom]??a.createdFrom,confidence:a.confidence??0}));return[...s,...t]},[P,R]),Y=d.useMemo(()=>{if(!c.trim())return f;const s=c.trim().toLowerCase();return f.filter(t=>{const a=(H[t.ruleType]??t.ruleType).toLowerCase();return t.pattern.toLowerCase().includes(s)||t.targetCategory.toLowerCase().includes(s)||a.includes(s)||t.sourceLabel.toLowerCase().includes(s)})},[f,c]),G=h({mutationFn:async({id:s,updates:t})=>(await x("PATCH",`/api/user/rule-cards/${s}`,t)).json(),onSuccess:()=>{r.invalidateQueries({queryKey:["/api/user/rule-cards"]}),y(null),n({title:"Rule updated"})},onError:()=>n({title:"Couldn't update rule",description:"Please try again.",variant:"destructive"})}),W=h({mutationFn:async({id:s,targetCategory:t})=>(await x("PATCH",`/api/classification-rules/${s}`,{targetCategory:t})).json(),onSuccess:()=>{r.invalidateQueries({queryKey:["/api/classification-rules"]}),r.invalidateQueries({queryKey:["/api/messages/sync-status"]}),y(null),n({title:"Rule updated"})},onError:()=>n({title:"Couldn't update rule",description:"Please try again.",variant:"destructive"})}),X=h({mutationFn:async s=>(await x("DELETE",`/api/user/rule-cards/${s}`)).json(),onSuccess:()=>{r.invalidateQueries({queryKey:["/api/user/rule-cards"]}),n({title:"Rule removed"})},onError:()=>n({title:"Couldn't remove rule",description:"Please try again.",variant:"destructive"})}),J=h({mutationFn:async s=>(await x("PATCH",`/api/classification-rules/${s}`,{isActive:!1})).json(),onSuccess:()=>{r.invalidateQueries({queryKey:["/api/classification-rules"]}),r.invalidateQueries({queryKey:["/api/messages/sync-status"]}),n({title:"Rule removed"})},onError:()=>n({title:"Couldn't remove rule",description:"Please try again.",variant:"destructive"})}),M=h({mutationFn:async s=>(await x("POST","/api/user/rule-cards",s)).json(),onSuccess:()=>{r.invalidateQueries({queryKey:["/api/user/rule-cards"]}),E(!1),S({conditionType:"domain",conditionValue:"",targetCategory:"",isException:!1}),n({title:"Rule created"})},onError:()=>n({title:"Couldn't create rule",description:"Please try again.",variant:"destructive"})}),g=h({mutationFn:async({id:s,action:t})=>(await x("POST",`/api/email-learning/suggestions/${s}/respond`,{action:t})).json(),onSuccess:()=>{r.invalidateQueries({queryKey:["/api/email-learning/suggestions"]}),r.invalidateQueries({queryKey:["/api/classification-rules"]}),r.invalidateQueries({queryKey:["/api/notifications"]})},onError:()=>n({title:"Couldn't update suggestion",description:"Please try again.",variant:"destructive"})}),ne=s=>{y(`${s.source}:${s.id}`),j({conditionType:s.ruleType,conditionValue:s.pattern,targetCategory:s.targetCategory})},Z=s=>{s.source==="rule-card"?G.mutate({id:s.id,updates:{conditionType:o.conditionType,conditionValue:o.conditionValue,targetCategory:o.targetCategory}}):W.mutate({id:s.id,targetCategory:o.targetCategory})},re=s=>{s.source==="rule-card"?X.mutate(s.id):J.mutate(s.id)};return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{children:[e.jsxs("h2",{className:"text-xl font-semibold text-foreground flex items-center gap-2",children:[e.jsx(ve,{className:"w-5 h-5 text-primary"}),"Rules"]}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Categorization rules — both the ones you've created and the ones your assistant has learned from your corrections. Accept pending suggestions before they apply automatically, or add your own rule to route traffic before the assistant ever sees an email."})]}),e.jsxs(l,{onClick:()=>E(!0),size:"sm",children:[e.jsx(fe,{className:"w-4 h-4 mr-1"}),"Add rule"]})]}),e.jsxs("section",{className:"space-y-3","data-testid":"rules-pending",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(de,{className:"w-4 h-4 text-amber-500"}),e.jsx("h3",{className:"text-sm font-semibold",children:"Pending suggestions"}),e.jsx(p,{variant:"secondary",className:"text-xs",children:v.length})]}),K?e.jsx("div",{className:"h-16 bg-muted animate-pulse rounded-md"}):v.length===0?e.jsx(L,{className:"border-dashed",children:e.jsx(V,{className:"py-8 text-center text-sm text-muted-foreground",children:"No pending suggestions. As you triage, your assistant will surface patterns here for you to confirm."})}):e.jsx("div",{className:"space-y-2",children:v.map(s=>{const t=Te(s.actionCounts);return e.jsx(L,{"data-testid":`pending-suggestion-${s.id}`,children:e.jsxs(V,{className:"py-3 px-4 flex items-center gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[e.jsx(p,{variant:"outline",className:"text-xs",children:H[s.patternType]??s.patternType}),e.jsx("span",{className:"font-medium truncate",children:s.patternValue})]}),t&&e.jsx("div",{className:"text-xs text-muted-foreground mt-1",children:t})]}),e.jsxs("div",{className:"flex items-center gap-1 shrink-0",children:[e.jsxs(l,{size:"sm",variant:"outline",className:"h-7 text-xs gap-1",disabled:g.isPending,onClick:()=>g.mutate({id:s.id,action:"accept_auto_archive"}),"data-testid":`pending-accept-${s.id}`,children:[e.jsx(D,{className:"w-3 h-3"}),"Auto-archive"]}),e.jsxs(l,{size:"sm",variant:"ghost",className:"h-7 text-xs gap-1",disabled:g.isPending,onClick:()=>g.mutate({id:s.id,action:"snooze"}),"data-testid":`pending-snooze-${s.id}`,children:[e.jsx(Ce,{className:"w-3 h-3"}),"Snooze"]}),e.jsxs(l,{size:"sm",variant:"ghost",className:"h-7 text-xs gap-1 text-muted-foreground",disabled:g.isPending,onClick:()=>g.mutate({id:s.id,action:"dismiss"}),"data-testid":`pending-dismiss-${s.id}`,children:[e.jsx(I,{className:"w-3 h-3"}),"Dismiss"]})]})]})},s.id)})})]}),e.jsxs("section",{className:"space-y-3","data-testid":"rules-active",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(ue,{className:"w-4 h-4 text-primary"}),e.jsx("h3",{className:"text-sm font-semibold",children:"Active rules"}),e.jsx(p,{variant:"secondary",className:"text-xs",children:f.length}),e.jsx("div",{className:"flex-1"}),e.jsxs("div",{className:"relative w-64",children:[e.jsx(Ne,{className:"w-3.5 h-3.5 text-muted-foreground absolute left-2 top-1/2 -translate-y-1/2"}),e.jsx(O,{placeholder:"Search by pattern or category",value:c,onChange:s=>A(s.target.value),className:"h-8 pl-7 text-xs","data-testid":"rules-search"})]})]}),q||F?e.jsx("div",{className:"space-y-2",children:[1,2,3].map(s=>e.jsx("div",{className:"h-12 bg-muted animate-pulse rounded-md"},s))}):Y.length===0?e.jsx(L,{className:"border-dashed",children:e.jsx(V,{className:"py-8 text-center text-sm text-muted-foreground",children:f.length===0?"N
1o rules yet. Add one above, or correct an email's category and your assistant will save the pattern here.":"No rules match your search."})}):e.jsx("div",{className:"space-y-2",children:Y.map(s=>{const t=`${s.source}:${s.id}`,a=ae===t,Q=s.source==="rule-card"?G.isPending||X.isPending:W.isPending||J.isPending,ee=a&&s.source==="rule-card";return e.jsx(L,{className:"group hover:border-primary/30 transition-colors","data-testid":`rule-${s.source}-${s.id}`,children:e.jsx(V,{className:ee?"py-3 px-4 space-y-2":"py-3 px-4 flex items-center gap-3",children:ee?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsxs(C,{value:o.conditionType,onValueChange:i=>j(m=>({...m,conditionType:i})),children:[e.jsx(N,{className:"h-8 text-xs","data-testid":`rule-edit-type-${t}`,children:e.jsx(b,{})}),e.jsx(T,{children:se.map(i=>e.jsx(w,{value:i.value,children:i.label},i.value))})]}),e.jsx(O,{className:"h-8 text-xs",value:o.conditionValue,onChange:i=>j(m=>({...m,conditionValue:i.target.value})),"data-testid":`rule-edit-value-${t}`})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-muted-foreground text-xs",children:"→"}),e.jsxs(C,{value:o.targetCategory,onValueChange:i=>j(m=>({...m,targetCategory:i})),children:[e.jsx(N,{className:"h-8 text-xs flex-1","data-testid":`rule-edit-category-${t}`,children:e.jsx(b,{placeholder:"Category"})}),e.jsx(T,{children:z.map(i=>e.jsx(w,{value:i.name,children:i.label||i.name},i.name))})]}),e.jsx(l,{size:"sm",className:"h-8 px-2",onClick:()=>Z(s),disabled:Q||!o.conditionValue.trim()||!o.targetCategory,"data-testid":`rule-save-${t}`,children:e.jsx(D,{className:"w-3 h-3"})}),e.jsx(l,{size:"sm",variant:"ghost",className:"h-8 px-2",onClick:()=>y(null),"data-testid":`rule-cancel-${t}`,children:e.jsx(I,{className:"w-3 h-3"})})]})]}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex-1 min-w-0 flex items-center gap-2 text-sm",children:[e.jsx(p,{variant:"outline",className:"text-xs shrink-0",children:H[s.ruleType]??s.ruleType}),e.jsx("span",{className:"font-mono text-xs truncate",children:s.pattern}),e.jsx("span",{className:"text-muted-foreground",children:"→"}),a?e.jsxs(C,{value:o.targetCategory,onValueChange:i=>j(m=>({...m,targetCategory:i})),children:[e.jsx(N,{className:"h-7 text-xs w-44","data-testid":`rule-edit-${t}`,children:e.jsx(b,{})}),e.jsx(T,{children:z.map(i=>e.jsx(w,{value:i.name,children:i.label||i.name},i.name))})]}):e.jsx(p,{variant:"secondary",className:"text-xs",children:s.targetCategory})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0 text-xs text-muted-foreground",children:[s.isException&&e.jsxs(p,{variant:"outline",className:"text-amber-600 border-amber-300",children:[e.jsx(me,{className:"w-3 h-3 mr-1"}),"Exception"]}),e.jsx(p,{variant:"outline",className:"text-xs font-normal",children:s.sourceLabel}),e.jsxs("span",{className:"tabular-nums",children:[s.confidence,"%"]}),a?e.jsxs(e.Fragment,{children:[e.jsx(l,{size:"sm",className:"h-7 px-2",onClick:()=>Z(s),disabled:Q,"data-testid":`rule-save-${t}`,children:e.jsx(D,{className:"w-3 h-3"})}),e.jsx(l,{size:"sm",variant:"ghost",className:"h-7 px-2",onClick:()=>y(null),"data-testid":`rule-cancel-${t}`,children:e.jsx(I,{className:"w-3 h-3"})})]}):e.jsxs(e.Fragment,{children:[e.jsx(l,{size:"sm",variant:"ghost",className:"h-7 px-2 opacity-0 group-hover:opacity-100 transition-opacity",onClick:()=>ne(s),"data-testid":`rule-edit-btn-${t}`,children:e.jsx(be,{className:"w-3.5 h-3.5"})}),e.jsx(l,{size:"sm",variant:"ghost",className:"h-7 px-2 opacity-0 group-hover:opacity-100 transition-opacity text-destructive hover:text-destructive",onClick:()=>re(s),disabled:Q,"data-testid":`rule-delete-${t}`,children:e.jsx(xe,{className:"w-3.5 h-3.5"})})]})]})]})})},t)})})]}),e.jsx(pe,{open:ie,onOpenChange:E,children:e.jsxs(ge,{children:[e.jsx(he,{children:e.jsx(ye,{children:"Add a rule"})}),e.jsxs("div",{className:"space-y-4 py-2",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(B,{children:"Condition type"}),e.jsxs(C,{value:u.conditionType,onValueChange:s=>S(t=>({...t,conditionType:s})),children:[e.jsx(N,{children:e.jsx(b,{})}),e.jsx(T,{children:se.map(s=>e.jsx(w,{value:s.value,children:s.label},s.value))})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(B,{children:"Value"}),e.jsx(O,{placeholder:u.conditionType==="domain"?"example.com":u.conditionType==="sender"?"[email protected]":"keyword or phrase",value:u.conditionValue,onChange:s=>S(t=>({...t,conditionValue:s.target.value}))})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(B,{children:"Route to category"}),e.jsxs(C,{value:u.targetCategory,onValueChange:s=>S(t=>({...t,targetCategory:s})),children:[e.jsx(N,{children:e.jsx(b,{placeholder:"Select category"})}),e.jsx(T,{children:z.map(s=>e.jsx(w,{value:s.name,children:s.label||s.name},s.name))})]})]})]}),e.jsxs(je,{children:[e.jsx(l,{variant:"ghost",onClick:()=>E(!1),children:"Cancel"}),e.jsx(l,{onClick:()=>M.mutate(u),disabled:!u.conditionValue||!u.targetCategory||M.isPending,children:M.isPending?"Creating…":"Create rule"})]})]})})]})}export{Ie 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.