PageSourceSearch

https://unmodel.ai/assets/OpsCreditDiscountPanel-Bt08hLKc.js

js unmodel.ai collected 2026-10-05 15:57:30 UTC 19,052 bytes, 1 lines download raw bytes

1import{p as ye,a,C as $,j as e,O as y,n as r,cs as Ce,ct as be,cv as Se,cw as Ee,cx as ce,S as g,dI as $e,aL as Ue,eB as Me,$ as ze,fu as je,bR as He,cr as Be,bx as X,L as Fe,dJ as Ge,dK as Je,dL as Qe,dM as Ve,dN as qe,dO as Ke,dP as Ye,dQ as We}from"./try-on-links-D-kskELd.js";import{T as we,a as Pe,b as _,c as p,d as Te,e as o}from"./table-DqdFGgO3.js";import{D as oe,b as de,d as ke,a as M,c as me}from"./creditDiscountPolicy-BSFNeCYu.js";import{p as C,O as Ae,a as Le,b as xe,r as ue,l as O,s as z,m as v,k as Xe,q as Ze,n as es,j as ss}from"./OpsPage-BVQOenM-.js";import"./tabs-BLT7ss3V.js";import"./useAuthRedirect-BdmZE0z3.js";async function fe(d){try{return await navigator.clipboard.writeText(d),!0}catch{return!1}}function ts({isSuperAdmin:d}){const{toast:l}=ye(),[L,S]=a.useState([]),[E,ee]=a.useState(!1),[N,se]=a.useState(""),[u,H]=a.useState(String(oe)),[U,te]=a.useState(""),[w,ae]=a.useState(""),[P,B]=a.useState(""),[T,F]=a.useState(""),[G,J]=a.useState(!1),[i,Q]=a.useState(null),[R,V]=a.useState(null),[q,D]=a.use
1State([]),[ne,K]=a.useState(!1),k=a.useCallback(async()=>{if(d){ee(!0);try{const s=await $("/api/user-management/users/discount-coupons?limit=50"),n=await s.json();if(!s.ok||!n.success)throw new Error(n.error||"Failed to load coupons");S(n.data||[])}catch(s){l({title:"Coupons unavailable",description:s instanceof Error?s.message:"Try again",variant:"destructive"})}finally{ee(!1)}}},[d,l]);a.useEffect(()=>{k()},[k]);const ie=async(s,n)=>{await fe(s)?(n&&(Q(n),window.setTimeout(()=>Q(I=>I===n?null:I),1500)),l({title:"Copied",description:s})):l({title:"Copy failed",description:s,variant:"destructive"})},re=async()=>{J(!0);try{const s=await $("/api/user-management/users/discount-coupons",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:N,discountPercent:Number(u),campaign:U.trim()||null,reason:w.trim(),expiresAt:P||null,maxRedemptions:T?Number(T):null})}),n=await s.json();if(!s.ok||!n.success)throw new Error(n.details||n.error||"Create failed");const c=String(n.coupon?.code||N).toUpperCase();await fe(c),l({title:"Coupon created · copied",description:c}),se(""),te(""),ae(""),B(""),F(""),await k()}catch(s){l({title:"Create failed",description:s instanceof Error?s.message:"Try again",variant:"destructive"})}finally{J(!1)}},Y=async s=>{try{const n=await $(`/api/user-management/users/discount-coupons/${s}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({isActive:!1})}),c=await n.json();if(!n.ok||!c.success)throw new Error(c.error||"Deactivate failed");await k()}catch(n){l({title:"Deactivate failed",description:n instanceof Error?n.message:"Try again",variant:"destructive"})}},W=async s=>{if(R===s){V(null),D([]);return}V(s),K(!0);try{const n=await $(`/api/user-management/users/discount-coupons/${encodeURIComponent(s)}/redemptions?limit=50`),c=await n.json();if(!n.ok||!c.success)throw new Error(c.error||"Failed to load redeemers");D(c.data||[])}catch(n){D([]),l({title:"Redeemers unavailable",description:n instanceof Error?n.message:"Try again",variant:"destructive"})}finally{K(!1)}};return d?e.jsxs("div",{className:"rounded-xl border border-white/10 bg-white/[0.02] p-4 space-y-3",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-medium text-white",children:"Campaign coupons"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:"Codes redeem into the same credit-% program. Not payment coupons. Created codes are copied for you."})]}),e.jsxs("div",{className:"grid gap-2 sm:grid-cols-2 lg:grid-cols-3",children:[e.jsx(y,{value:N,onChange:s=>se(s.target.value.toUpperCase()),placeholder:"CODE",className:r("h-9",C),maxLength:32}),e.jsxs(Ce,{value:u,onValueChange:H,children:[e.jsx(be,{className:r("h-9",Ae),children:e.jsx(Se,{})}),e.jsx(Ee,{className:Le,children:de.map(s=>e.jsxs(ce,{value:String(s),className:xe,children:[s,"%"]},s))})]}),e.jsx(y,{value:U,onChange:s=>te(s.target.value),placeholder:"Campaign (optional)",className:r("h-9",C),maxLength:80}),e.jsx(y,{value:w,onChange:s=>ae(s.target.value),placeholder:"Reason (required)",className:r("h-9",C),maxLength:ke}),e.jsx(y,{type:"datetime-local",value:P,onChange:s=>B(s.target.value),className:r("h-9",C),title:"Expires at (optional)"}),e.jsx(y,{value:T,onChange:s=>F(s.target.value),placeholder:"Max redemptions (optional)",className:r("h-9",C),inputMode:"numeric"})]}),e.jsx(g,{size:"sm",className:r("h-9",ue),disabled:G||!N.trim()||!w.trim(),onClick:()=>{re()},children:G?"Creating…":"Create coupon"}),e.jsx("div",{className:"overflow-x-auto rounded-lg border border-white/10",children:e.jsxs(we,{children:[e.jsx(Pe,{children:e.jsxs(_,{className:"border-white/10 hover:bg-transparent",children:[e.jsx(p,{className:O,children:"Code"}),e.jsx(p,{className:O,children:"%"}),e.jsx(p,{className:O,children:"Campaign"}),e.jsx(p,{className:O,children:"Redeemed"}),e.jsx(p,{className:O,children:"Expires"}),e.jsx(p,{className:O})]})}),e.jsx(Te,{children:E?e.jsx(_,{children:e.jsx(o,{colSpan:6,className:"text-sm text-gray-500",children:"Loading…"})}):L.length===0?e.jsx(_,{children:e.jsx(o,{colSpan:6,className:"text-sm text-gray-500",children:"No coupons yet."})}):L.map(s=>e.jsxs($e.Fragment,{children:[e.jsxs(_,{className:"border-white/10",children:[e.jsx(o,{className:"font-mono text-sm text-white",children:e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{children:s.code}),e.jsx(g,{size:"sm",variant:"ghost",className:"h-7 w-7 p-0 text-gray-400 hover:text-white",onClick:()=>{ie(s.code,s.id)},title:"Copy code",children:i===s.id?e.jsx(Ue,{className:"h-3.5 w-3.5 text-emerald-400"}):e.jsx(Me,{className:"h-3.5 w-3.5"})})]})}),e.jsxs(o,{className:"tabular-nums text-emerald-300",children:[s.discountPercent,"%"]}),e.jsx(o,{className:"text-sm text-gray-400",children:s.campaign||z}),e.jsxs(o,{className:"tabular-nums text-sm text-gray-300",children:[s.redemptionCount,s.maxRedemptions!=null?` / ${s.maxRedemptions}`:""]}),e.jsxs(o,{className:"text-xs text-gray-500",children:[s.expiresAt?new Date(s.expiresAt).toLocaleStr
1ing():"Open",s.isActive?"":" · off"]}),e.jsx(o,{children:e.jsxs("div",{className:"flex flex-wrap gap-1.5 justify-end",children:[e.jsxs(g,{size:"sm",variant:"outline",className:r("h-8",v),onClick:()=>{W(s.id)},children:[e.jsx(ze,{className:"h-3.5 w-3.5 mr-1"}),R===s.id?"Hide":"Who"]}),s.isActive?e.jsx(g,{size:"sm",variant:"outline",className:r("h-8",v),onClick:()=>{Y(s.id)},children:"Deactivate"}):null]})})]}),R===s.id?e.jsx(_,{className:"border-white/10 bg-white/[0.015]",children:e.jsx(o,{colSpan:6,className:"py-3",children:ne?e.jsx("p",{className:"text-xs text-gray-500",children:"Loading redeemers…"}):q.length===0?e.jsx("p",{className:"text-xs text-gray-500",children:"No redemptions yet."}):e.jsx("div",{className:"space-y-1.5",children:q.map(n=>e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2 text-xs text-gray-300",children:[e.jsxs("span",{children:[e.jsx("span",{className:"text-white",children:n.fullName}),n.email?e.jsxs("span",{className:"text-gray-500",children:[" · ",n.email]}):null]}),e.jsx("span",{className:"tabular-nums text-gray-500",children:new Date(n.redeemedAt).toLocaleString()})]},n.id))})})}):null]},s.id))})]})})]}):null}const Z=25,as=250,ve="clear";function ns(d){if(!d)return z;const l=new Date(d);return Number.isNaN(l.getTime())?z:l.toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"})}function le(d){const l=me(M,d);return`${M} → ${l} credits`}function ms({isSuperAdmin:d=!1,focusUserId:l=null,onOpenAuditForUser:L}){const{toast:S}=ye(),[E,ee]=a.useState([]),[N,se]=a.useState(0),[u,H]=a.useState(0),[U,te]=a.useState(""),[w,ae]=a.useState(""),[P,B]=a.useState(!1),[T,F]=a.useState(""),[G,J]=a.useState(!1),[i,Q]=a.useState(null),[R,V]=a.useState(String(oe)),[q,D]=a.useState(""),[ne,K]=a.useState(""),[k,ie]=a.useState(""),[re,Y]=a.useState(!1),[W,s]=a.useState(!1),n=a.useCallback(async(t,b)=>{B(!0);try{const j=new URLSearchParams({limit:String(Z),offset:String(t)});b.trim()&&j.set("search",b.trim());const x=await $(`/api/user-management/users/discounts?${j.toString()}`,{credentials:"include"}),h=await x.json();if(!x.ok||!h.success)throw new Error(h.error||"Failed to load credit discounts");const _e=(Array.isArray(h.data)?h.data:[]).map(f=>({userId:f.userId,email:f.email??null,fullName:f.fullName??"Unknown user",discountPercent:Number(f.discountPercent)||0,campaign:f.campaign??null,expiresAt:f.expiresAt??null,expired:f.expired===!0,offPolicyPercent:f.offPolicyPercent!=null?Number(f.offPolicyPercent):null,updatedAt:f.updatedAt??null}));ee(_e),se(Number(h.pagination?.total)||0)}catch(j){S({title:"Could not load credit discounts",description:j instanceof Error?j.message:"Try again",variant:"destructive"})}finally{B(!1)}},[S]);a.useEffect(()=>{const t=setTimeout(()=>{H(0),ae(U.trim())},as);return()=>clearTimeout(t)},[U]),a.useEffect(()=>{n(u,w)},[n,u,w]);const c=a.useCallback(async t=>{const b=t.trim();if(b){J(!0);try{const j=await $(`/api/user-management/users/${encodeURIComponent(b)}/discount`,{credentials:"include"}),x=await j.json();if(!j.ok||!x.success)throw new Error(x.error||"User not found");const h={userId:x.userId,email:x.email??null,fullName:x.fullName??"Unknown user",discountQualifies:x.discountQualifies===!0,discountPercent:Number(x.discountPercent)||0,storedPercent:Number(x.storedPercent)||0,campaign:x.campaign??null,expiresAt:x.expiresAt??null,updatedAt:x.updatedAt??null};Q(h),V(h.discountPercent>
10?String(h.discountPercent):String(oe)),K(h.campaign||""),ie(h.expiresAt?new Date(h.expiresAt).toISOString().slice(0,16):""),D("")}catch(j){Q(null),S({title:"Lookup failed",description:j instanceof Error?j.message:"Check the email or user id",variant:"destructive"})}finally{J(!1)}}},[S]);a.useEffect(()=>{l&&c(l)},[l,c]);const I=q.trim(),m=R===ve,A=m?0:Number(R)||0,pe=m?"":ne.trim(),he=m?null:k?new Date(k).toISOString():null,ge=!!i&&i.discountPercent===A&&i.storedPercent===A&&(i.campaign||"")===pe&&(i.expiresAt||null)===he,Re=!!i&&d&&I.length>0&&!W&&!ge,De=async()=>{if(i){s(!0);try{const t=await $(`/api/user-management/users/${i.userId}/discount`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({discountQualifies:!m,discountPercent:m?0:A,reason:I,campaign:m?null:pe||null,expiresAt:he})}),b=await t.json();if(!t.ok||!b.success)throw new Error(b.details||b.error||"Update failed");S({title:m?"Credit discount removed":`Credit discount set to ${A}%`,description:m?`${i.fullName} is back to list credit prices.`:`${i.fullName} now pays ${le(A)}.`}),Y(!1),D(""),await c(i.userId),await n(u,w)}catch(t){S({title:"Update failed",description:t instanceof Error?t.message:"Try again",variant:"destructive"})}finally{s(!1)}}},Ie=N===0?0:u+1,Oe=Math.min(u+E.length,N),Ne=a.useMemo(()=>E.filter(t=>t.offPolicyPercent!=null&&t.offPolicyPercent>0).length,[E]);return e.jsxs("div",{className:r(ss,"space-y-5"),children:[e.jsx(Xe,{title:"Credit discounts",description:"Percentage off the credits charged per generation, per user.",icon:je,details:e.jsxs(e.Fragment,{children:["Applies at charge time in billing, floored in the user's favour, never below 1 credit. Granted steps: ",de.join("% · "),"%."]}),actions:e.jsxs(g,{size:"sm",variant:"outline",className:r("h-9",v),onClick:()=>{n(u,w)},disabled:P,children:[e.jsx(He,{className:r("h-3.5 w-3.5 mr-1.5",P&&"animate-spin")}),"Refresh"]})}),d?e.jsxs("div",{className:"rounded-xl border border-white/10 bg-white/[0.02] p-4 space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-end gap-2",children:[e.jsxs("div",{className:"min-w-[240px] flex-1",children:[e.jsx("label",{className:"block text-[11px] uppercase tracking-wide text-gray-500 mb-1.5",children:"User (email or id)"}),e.jsx(y,{value:T,onChange:t=>F(t.target.value),onKeyDown:t=>{t.key==="Enter"&&c(T)},placeholder:"[email protected]",className:r("h-9",C)})]}),e.jsxs(g,{size:"sm",variant:"outline",className:r("h-9",v),onClick:()=>{c(T)},disabled:G||!T.trim(),children:[e.jsx(Be,{className:r("h-3.5 w-3.5 mr-1.5",G&&"animate-pulse")}),"Look up"]})]}),i?e.jsxs("div",{className:"space-y-3 border-t border-white/10 pt-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-sm",children:[e.jsx("span",{className:"font-medium text-white",children:i.fullName}),e.jsx("span",{className:"text-gray-500",children:i.email||z}),i.discountPercent>0?e.jsxs(X,{variant:"outline",className:"border-emerald-500/30 bg-emerald-500/10 text-emerald-300",children:[i.discountPercent,"% today"]}):e.jsx(X,{variant:"outline",className:"border-white/15 text-gray-400",children:"List price today"}),i.campaign?e.jsx(X,{variant:"outline",className:"border-white/15 bg-white/[0.03] text-gray-300",children:i.campaign}):null,i.expiresAt?e.jsxs(X,{variant:"outline",className:"border-white/15 bg-white/[0.03] text-gray-400",children:["Until ",new Date(i.expiresAt).toLocaleString()]}):null,L?e.jsx(g,{size:"sm",variant:"outline",className:r("h-7 text-xs",v),onClick:()=>L(i.userId,i.email||i.fullName),children:"Audit trail"}):null,i.storedPercent>0&&i.discountPercent===0?e.jsxs(X,{variant:"outline",className:"border-amber-500/30 bg-amber-500/10 text-amber-300",children:["Stored ",i.storedPercent,"% is off-policy — charging list"]}):null]}),e.jsxs("div",{className:"flex flex-wrap items-end gap-2",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[11px] uppercase tracking-wide text-gray-500 mb-1.5",children:"Discount"}),e.jsxs(Ce,{value:R,onValueChange:V,children:[e.jsx(be,{className:Ae,children:e.jsx(Se,{})}),e.jsxs(Ee,{className:Le,children:[de.map(t=>e.jsxs(ce,{value:String(t),className:xe,children:[t,"% · ",le(t)]},t)),e.jsx(ce,{value:ve,className:xe,children:"No discount · list price"})]})]})]}),e.jsxs("div",{className:"min-w-[160px] flex-1",children:[e.jsx("label",{className:"block text-[11px] uppercase tracking-wide text-gray-500 mb-1.5",children:"Campaign"}),e.jsx(y,{value:ne,onChange:t=>K(t.target.value),placeholder:"Optional label",className:r("h-9",C),maxLength:80,disabled:m})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[11px] uppercase tracking-wide text-gray-500 mb-1.5",children:"Expires"}),e.jsx(y,{type:"datetime-local",value:k,onChange:t=>ie(t.target.value),className:r("h-9",C),disabled:m})]}),e.jsxs("div",{className:"min-w-[240px] flex-1",children:[e.jsx("label",{className:"block text-[11px] uppercase tracking-wide text-gray-500 mb-1.5",children:"Reason (audited)"}),e.jsx(y,{value:q,maxLength:ke,onChange:t=>D(t.target.value),placeholder:"Pilot agreement, launch partner, goodwill credit…",className:r("h-9",C)})]}),e.jsx(g,{size:"sm",className:r("h-9",ue),onClick:()=>Y(!0),disabled:!Re,children:m?"Remove discount":"Apply discount"})]}),e.jsxs("p",{className:"text-[12px] text-gray-500",children:[ge?"Already set to this — pick a different step to make a change.":m?`${i.fullName} will pay list credit prices on the next generation.`:`Next generation costing ${M} credits will charge ${me(M,A)}.`,I.length===0?" A reason is required.":""]})]}):null]}):e.jsx("p",{className:"text-[13px] text-gray-500",children:"Read-only: only super admins can change credit discounts."}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx(y,{value:U,onChange:t=>te(t.target.value),placeholder:"Search discounted users…",className:r("h-9 max-w-[280px]",C)}),e.jsxs("span",{className:"text-[12px] text-gray-500",children:[N===0?"No users on the program":`${Ie}–${Oe} of ${N}`,Ne>0?` · ${Ne} off-policy`:""]})]}),P&&E.length===0?e.jsx("div",{className:"flex justify-center py-10",children:e.jsx(Fe,{})}):E.length===0?e.jsx(Ze,{icon:je,title:"No credit discounts granted",hint:"Look up a user above to put them on the program. Everyone else pays list credit prices."}):e.jsx("div",{className:"overflow-x-auto rounded-xl border border-white/10",children:e.jsxs(we,{children:[e.jsx(Pe,{children:e.jsxs(_,{className:r(O,"hover:bg-transparent"),children:[e.jsx(p,{className:"text-gray-500 text-[11px]",children:"User"}),e.jsx(p,{className:"text-gray-500 text-[11px]",children:"Discount"}),e.jsx(p,{className:"text-gray-500 text-[11px]",children:"Campaign"}),e.jsx(p,{className:"text-gray-500 text-[11px]",children:"Expires"}),e.jsxs(p,{className:"text-gray-500 text-[11px]",children:[M," credits cost"]}),e.jsx(p,{className:"text-gray-500 text-[11px]",children:"Updated"}),e.jsx(p,{className:"w-36"})]})}),e.jsx(Te,{children:E.map(t=>e.jsxs(_,{className:"border-white/10",children:[e.jsxs(o,{className:"align-top",children:[e.jsx("div",{className:"text-sm text-white",children:t.fullName}),e.jsx("div",{className:"text-[11px] text-gray-500",children:t.email||t.userId})]}),e.jsx(o,{className:"align-top",children:t.discountPercent>0?e.jsxs("span",{className:"text-sm tabular-nums text-emerald-300",children:[t.discountPercent,"%"]}):t.expired?e.jsx("span",{className:"text-[12px] text-amber-300",children:"Expired"}):e.jsxs("span",{className:"text-[12px] text-amber-300",children:[t.offPolicyPercent,"% off-policy"]})}),e.jsx(o,{className:"align-top text-[12px] text-gray-400",children:t.campaign||z}),e.jsx(o,{className:"align-top text-[12px] text-gray-500",children:t.expiresAt?new Date(t.expiresAt).toLocaleStr
1ing():"Open"}),e.jsx(o,{className:"align-top text-sm tabular-nums text-gray-300",children:me(M,t.discountPercent)}),e.jsx(o,{className:"align-top text-[12px] text-gray-500",children:ns(t.updatedAt)}),e.jsx(o,{className:"align-top",children:e.jsxs("div",{className:"flex flex-wrap gap-1.5",children:[e.jsx(g,{size:"sm",variant:"outline",className:r("h-7 text-xs",v),onClick:()=>{F(t.email||t.userId),c(t.userId)},disabled:!d,children:"Edit"}),L?e.jsx(g,{size:"sm",variant:"outline",className:r("h-7 text-xs",v),onClick:()=>L(t.userId,t.email||t.fullName),children:"Audit"}):null]})})]},t.userId))})]})}),N>Z?e.jsxs("div",{className:"flex items-center justify-end gap-2",children:[e.jsx(g,{size:"sm",variant:"outline",className:r("h-8 text-xs",v),onClick:()=>H(Math.max(0,u-Z)),disabled:u===0||P,children:"Previous"}),e.jsx(g,{size:"sm",variant:"outline",className:r("h-8 text-xs",v),onClick:()=>H(u+Z),disabled:u+Z>=N||P,children:"Next"})]}):null]}),e.jsx(ts,{isSuperAdmin:d}),e.jsx(Ge,{open:re,onOpenChange:Y,children:e.jsxs(Je,{className:es,children:[e.jsxs(Qe,{children:[e.jsx(Ve,{children:m?"Remove credit discount?":`Charge ${i?.fullName??"this user"} ${A}% less?`}),e.jsx(qe,{className:"text-gray-400",children:m?`${i?.fullName??"This user"} goes back to list credit prices on their next generation.`:`Every generation is discounted from now on — ${le(A)}. This is logged against your account.`})]}),e.jsxs("p",{className:"text-[12px] text-gray-500",children:["Reason: ",I||z]}),e.jsxs(Ke,{children:[e.jsx(Ye,{className:v,children:"Cancel"}),e.jsx(We,{className:ue,onClick:t=>{t.preventDefault(),De()},disabled:W,children:W?"Saving…":m?"Remove discount":"Apply discount"})]})]})})]})}export{ms as OpsCreditDiscountPanel};

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.