PageSourceSearch

https://app.thefestivalbabes.com/assets/AdminDiscountCodes-CMYXrwdK.js

js thefestivalbabes.com collected 2026-10-04 20:48:07 UTC 10,073 bytes, 1 lines download raw bytes

1import{k as Y,r as h,f as y,l as N,j as e,aq as R,dx as G,B as m,ab as J,ar as E,as as M,at as O,au as F,aB as x,I as j,aN as W,C as X,d as Z,p as ee,s as se,a as te,g as u,i as p,b as ae,D as b,E as A}from"./index-CAQ9C6-z.js";import{A as ie}from"./AdminBreadcrumb-DnS-FtUE.js";import{S as ne}from"./switch-CBkylJyN.js";import{T as I,a as $,b as f,c as n,d as P,e as d}from"./table-BaZ6TUhP.js";import{A as de,a as oe,b as re,c as ce,d as le,e as me,f as xe,g as ue}from"./alert-dialog-B8z1m0Cj.js";import{S as pe,a as he,b as je,c as fe,d as q}from"./select-C1jcuWaT.js";import{T as ge}from"./ticket-CTu4JHnO.js";import{f as B}from"./format-DECx0Hkk.js";import{E as ve}from"./eye-Q3U5VEuk.js";import{T as Ce}from"./trash-2-CJsVXXEd.js";import"./index-Q3UH4NY9.js";import"./index-BdQq_4o_.js";import"./chevron-down--E-1bxpl.js";import"./chevron-up-BOfZRiUg.js";import"./en-US-K2-Byqu-.js";import"./differenceInCalendarDays-BvXbP6kq.js";function ye(t){return t==null?"-":t%100===0?`$${t/100}`:`$${(t/100).toFixed(2)}`}function Ne(t){return t.isActive===0?"inactive":t.expiresAt&&new Date(t.expiresAt).getTime()<Date.now()?"expired":t.maxRedemptions!=null&&t.redemptionCount>=t.maxRedemptions?"redeemed":"active"}function be(t){switch(t){case"active":return e.jsx(p,{className:"bg-green-500/10 text-green-600 border-green-500/20",children:"Active"});case"inactive":return e.jsx(p,{variant:"outline",children:"Inactive"});case"expired":return e.jsx(p,{variant:"secondary",children:"Expired"});case"redeemed":return e.jsx(p,{className:"bg-amber-500/10 text-amber-600 border-amber-500/20",children:"Fully redeemed"})}}const D="__any__",U={code:"",percentOff:"",maxRedemptions:"",expiresAt:"",courseId:D};function Ue(){const{toast:t}=Y(),[K,g]=h.useState(!1),[a,c]=h.useState(U),[l,v]=h.useState(null),[o,w]=h.useState(null),{data:T=[],isLoading:L}=y({queryKey:["/api/admin/discount-codes"]}),{data:S=[]}=y({queryKey:["/api/admin/courses"]}),{data:k=[],isLoading:_}=y({queryKey:[`/api/admin/discount-codes/${o==null?void 0:o.id}/redemptions`],enabled:!!o}),H=s=>{var i;return s?((i=S.find(r=>r.id===s))==null?void 0:i.title)??"Unknown course":"Any course"},C=N({mutationFn:async s=>(await A("POST","/api/admin/discount-codes",s)).json(),onSuccess:()=>{b.invalidateQueries({queryKey:["/api/admin/discount-codes"]}),t({title:"Discount code created"}),g(!1),c(U)},onError:s=>{t({title:"Failed to create",description:(s==null?void 0:s.serverMessage)||(s==null?void 0:s.message),variant:"destructive"})}}),Q=N({mutationFn:async({id:s,isActive:i})=>(await A("POST",`/api/admin/discount-codes/${s}/toggle`,{isActive:i})).json(),onSuccess:()=>{b.invalidateQueries({queryKey:["/api/admin/discount-codes"]})},onError:s=>{t({title:"Failed to update",description:(s==null?void 0:s.serverMessage)||(s==null?void 0:s.message),variant:"destructive"})}}),V=N({mutationFn:async s=>(await A("DELETE",`/api/admin/discount-codes/${s}`,{})).json(),onSuccess:()=>{b.invalidateQueries({queryKey:["/api/admin/discount-codes"]}),t({title:"Discount code deleted"}),v(null)},onError:s=>{t({title:"Failed to delete",description:(s==null?void 0:s.serverMessage)||(s==null?void 0:s.message),variant:"destructive"})}}),z=()=>{const s=r=>r.trim()===""?null:parseInt(r,10),i={code:a.code.trim().toUpperCase(),percentOff:parseInt(a.percentOff,10),courseId:a.courseId===D?null:a.courseId,maxRedemptions:s(a.maxRedemptions),expiresAt:a.expiresAt.trim()===""?null:new Date(a.expiresAt).toISOString()};C.mutate(i)};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(ie,{items:[{label:"Discount Codes"}]}),e.jsxs("div",{className:"flex items-center justify-between flex-wrap gap-3",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"text-2xl font-bold flex items-center gap-2",children:[e.jsx(ge,{className:"w-6 h-6 text-pink-500"}),"Discount Codes"]}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Create promo codes that give a percentage off course checkout. A 100% code unlocks the course for free."})]}),e.jsxs(R,{open:K,onOpenChange:g,children:[e.jsx(G,{asChild:!0,children:e.jsxs(m,{"data-testid":"button-create-code",children:[e.jsx(J,{className:"w-4 h-4 mr-2"}),"New Code"]})}),e.jsxs(E,{className:"max-w-lg",children:[e.jsxs(M,{children:[e.jsx(O,{children:"Create Discount Code"}),e.jsx(F,{children:"Leave max redemptions blank for unlimited use, and expiry blank for a code that never expires."})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(x,{htmlFor:"code",children:"Code"}),e.jsx(j,{id:"code",value:a.code,onChange:s=>c({...a,code:s.target.value.toUpperCase()}),placeholder:"SUMMER25","data-testid":"input-code"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(x,{htmlFor:"percentOff",children:"Percent off (1-100)"}),e.jsx(j,{id:"percentOff",inputMode:"numeric",value:a.percentOff,onChange:s=>c({...a,percentOff:s.target.value}),placeholder:"25","data-testid":"input-percent-off"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(x,{htmlFor:"maxRedemptions",children:"Max redemptions"}),e.jsx(j,{id:"maxRedemptions",inputMode:"numeric",value:a.maxRedemptions,onChange:s=>c({...a,maxRedemptions:s.target.value}),placeholder:"Unlimited","data-testid":"input-max-redemptions"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(x,{htmlFor:"expiresAt",children:"Expires"}),e.jsx(j,{id:"expiresAt",type:"date",value:a.expiresAt,onChange:s=>c({...a,expiresAt:s.target.value}),"data-testid":"input-expires-at"})]}),e.jsxs("div",{className:"col-span-2 space-y-2",children:[e.jsx(x,{htmlFor:"courseId",children:"Course scope"}),e.jsxs(pe,{value:a.courseId,onValueChange:s=>c({...a,courseId:s}),children:[e.jsx(he,{id:"courseId","data-testid":"select-course",children:e.jsx(je,{placeholder:"Any course"})}),e.jsxs(fe,{children:[e.jsx(q,{value:D,children:"Any course"}),S.map(s=>e.jsx(q,{value:s.id,children:s.title},s.id))]})]})]})]}),e.jsxs(W,{children:[e.jsx(m,{variant:"outline",onClick:()=>g(!1),"data-testid":"button-cancel-create",children:"Cancel"}),e.jsx(m,{onClick:z,disabled:C.isPending,"data-testid":"button-submit-create",children:C.isPending?"Creating…":"Create Code"})]})]})]})]}),e.jsxs(X,{children:[e.jsxs(Z,{children:[e.jsx(ee,{children:"All Codes"}),e.jsx(se,{children:"Toggle a code off to disable it instantly. Deleting a code is permanent but does not affect past redemptions."})]}),e.jsx(te,{children:L?e.jsxs("div",{className:"space-y-2",children:[e.jsx(u,{className:"h-10"}),e.jsx(u,{className:"h-10"}),e.jsx(u,{className:"h-10"})]}):T.length===0?e.jsx("p",{className:"text-sm text-muted-foreground py-8 text-center",children:"No discount codes yet. Create one to offer a promo on course checkout."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(I,{children:[e.jsx($,{children:e.jsxs(f,{children:[e.jsx(n,{children:"Code"}),e.jsx(n,{children:"Discount"}),e.jsx(n,{children:"Scope"}),e.jsx(n,{children:"Redemptions"}),e.jsx(n,{children:"Expires"}),e.jsx(n,{children:"Status"}),e.jsx(n,{})]})}),e.jsx(P,{children:T.map(s=>{const i=Ne(s);return e.jsxs(f,{"data-testid":`row-code-${s.code}`,children:[e.jsx(d,{className:"font-mono font-medium","data-testid":`text-code-${s.code}`,children:s.code}),e.jsx(d,{children:s.percentOff===100?e.jsx(p,{className:"bg-pink-500/10 text-pink-600 border-pink-500/20",children:"FREE"}):e.jsxs("span",{className:"text-sm",children:[s.percentOff,"%"]})}),e.jsx(d,{className:"text-sm",children:H(s.courseId)}),e.jsxs(d,{className:"text-sm whitespace-nowrap","data-testid":`text-redemptions-${s.code}`,children:[s.redemptionCount," / ",s.maxRedemptions!=null?s.maxRedemptions:"∞"]}),e.jsx(d,{className:"text-xs",children:s.expiresAt?e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(ae,{className:"w-3 h-3"}),B(new Date(s.expiresAt),"MMM d, yyyy")]}):e.jsx("span",{className:"text-muted-foreground",children:"Never"})}),e.jsx(d,{"data-testid":`text-status-${s.code}`,children:be(i)}),e.jsx(d,{children:e.jsxs("div",{className:"flex items-center gap-2 justify-end",children:[e.jsx(ne,{checked:s.isActive===1,onCheckedChange:r=>Q.mutate({id:s.id,isActive:r}),"data-testid":`switch-active-${s.code}`}),e.jsx(m,{size:"sm",variant:"outline",onClick:()=>w(s),"data-testid":`button-view-${s.code}`,children:e.jsx(ve,{className:"w-4 h-4"})}),e.jsx(m,{size:"sm",variant:"outline",onClick:()=>v(s),"data-testid":`button-delete-${s.code}`,children:e.jsx(Ce,{className:"w-4 h-4"})})]})})]},s.id)})})]})})})]}),e.jsx(R,{open:!!o,onOpenChange:s=>!s&&w(null),children:e.jsxs(E,{className:"max-w-2xl",children:[e.jsxs(M,{children:[e.jsxs(O,{children:["Redemptions ",o?`· ${o.code}`:""]}),e.jsx(F,{children:"Everyone who has used this code and the price they paid."})]}),_?e.jsxs("div",{className:"space-y-2",children:[e.jsx(u,{className:"h-10"}),e.jsx(u,{className:"h-10"})]}):k.length===0?e.jsx("p",{className:"text-sm text-muted-foreground py-8 text-center",children:"This code has not been redeemed yet."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(I,{children:[e.jsx($,{children:e.jsxs(f,{children:[e.jsx(n,{children:"User"}),e.jsx(n,{children:"Paid"}),e.jsx(n,{children:"Redeemed"})]})}),e.jsx(P,{children:k.map(s=>e.jsxs(f,{"data-testid":`row-redemption-${s.id}`,children:[e.jsx(d,{className:"font-mono text-xs",children:s.userId}),e.jsx(d,{className:"text-sm",children:ye(s.finalPriceCents)}),e.jsx(d,{className:"text-xs",children:B(new Date(s.redeemedAt),"MMM d, yyyy h:mm a")})]},s.id))})]})})]})}),e.jsx(de,{open:!!l,onOpenChange:s=>!s&&v(null),children:e.jsxs(oe,{children:[e.jsxs(re,{children:[e.jsx(ce,{children:"Delete this discount code?"}),e.jsxs(le,{children:["The code ",e.jsx("span",{className:"font-mono",children:l==null?void 0:l.code})," will be permanently removed and can no longer be applied at checkout. Past redemptions are kept for your records."]})]}),e.jsxs(me,{children:[e.jsx(xe,{"data-testid":"button-cancel-delete",children:"Cancel"}),e.jsx(ue,{onClick:()=>l&&V.mutate(l.id),"data-testid":"button-confirm-delete",children:"Delete Code"})]})]})})]})}export{Ue 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.