PageSourceSearch

https://drophighlight.com/assets/CostSimulator-sZUaZwy-.js

js drophighlight.com collected 2026-09-30 06:38:52 UTC 16,384 bytes, 2 lines download raw bytes

1import{G as H,s as F,a1 as W,j as e,r as T,C as O,h as E,i as M,l as k,B as q,o as Q,J as P}from"./index-P0N1Je5T.js";import{T as X,a as Z,b as R,c as I}from"./tabs-DitHknAx.js";import{L as U}from"./label-CoHN_xq2.js";import{I as u}from"./input-1tKBwIjz.js";import{S as ee,a as se,b as te,c as ae,d as ne}from"./select-Supfcq-S.js";import{u as oe}from"./useMutation-DJI0oEf-.js";import{L as K}from"./loader-circle-BQs4OzSl.js";import{D as re}from"./download-JbV5bimG.js";import{T as le,a as ie,b as D,c as N,d as ce,e as w}from"./table-DyXCBCTZ.js";import{S as de}from"./save-BIgwChhU.js";function J(){return H({queryKey:["cost-pricing"],queryFn:async()=>{const{data:s,error:o}=await F.from("cost_pricing").select("id, service, category, unit, unit_price_usd, notes, sort_order").order("sort_order",{ascending:!0});if(o)throw o;return(s||[]).map(t=>({...t,unit_price_usd:Number(t.unit_price_usd)}))},staleTime:5*6e4})}function ue(){const s=W();return oe({mutationFn:async o=>{const{id:t,...a}=o,{error:r}=await F.from("cost_pricing").update(a).eq("id",t);if(r)throw r},onSuccess:()=>s.invalidateQueries({queryKey:["cost-pricing"]})})}function me(){return H({queryKey:["cost-baselines"],queryFn:async()=>{const{data:s,error:o}=await F.rpc("get_cost_simulator_baselines");if(o)throw o;return s||{}},staleTime:60*6e4})}const he={galleries:{galleries:50,photos_per_gallery:100,videos_per_gallery:100,photo_mb:4,video_mb:50,views_per_gallery:200},campaign:{recipients:1e5,open_rate_pct:30,click_rate_pct:8},fan_uploads:{fan_uploads:2e4,fan_photo_pct:70,fan_photo_mb:5,fan_video_mb:40,heic_pct:25},downloads:{downloads:1e4,download_mb:30},custom:{}},pe={preset:"galleries",galleries:50,photos_per_gallery:100,videos_per_gallery:100,photo_mb:4,video_mb:50,views_per_gallery:200,recipients:1e5,open_rate_pct:30,click_rate_pct:8,fan_uploads:2e4,fan_photo_pct:70,fan_photo_mb:5,fan_video_mb:40,heic_pct:25,downloads:1e4,download_mb:30,months:12},_=(s,o)=>s.find(t=>t.service.toLowerCase()===o.toLowerCase())||null,g=(s,o,t,a)=>{if(!s||o<=0)return null;const r=o/t*Number(s.unit_price_usd||0);return{service:s.service,category:s.category,quantity:o,unit:s.unit,unit_price_usd:Number(s.unit_price_usd),cost_usd:r,note:a}};function G(s,o){const t=s.galleries*s.photos_per_gallery,a=s.galleries*s.videos_per_gallery,r=(t*s.photo_mb+a*s.video_mb)/1024,i=s.galleries*s.views_per_gallery,h=i*(s.photo_mb*s.photos_per_gallery*.3+s.video_mb*s.videos_per_gallery*.5)/1024/Math.max(s.galleries,1)*s.galleries,l=t*3+a*2,p=(t*.5+a*10)/1024,d=i*s.photos_per_gallery*.4/1024,m=[],c=[],b=g(_(o,"Cloudinary Transformations"),l,1e3,"Initial derivatives");b&&m.push(b);const f=g(_(o,"Supabase Storage"),r,1,"Originals stored");f&&c.push(f);const n=g(_(o,"Cloudinary Storage"),p,1,"Optimized versions");n&&c.push(n);const j=g(_(o,"Supabase Egress"),h,1,"View egress");j&&c.push(j);const y=g(_(o,"Cloudinary Bandwidth"),d,1);return y&&c.push(y),{oneOff:m,recurring:c}}function V(s,o){const t=s.recipients,a=Math.ceil(t/100)+t,r=[],i=g(_(o,"Resend Emails"),t,1e3);i&&r.push(i);const h=g(_(o,"Supabase Edge Invocations"),a,1e6,"Send + webhook invocations");return h&&r.push(h),{oneOff:r,recurring:[]}}function A(s,o){const t=Math.round(s.fan_uploads*(s.fan_photo_pct/100)),a=s.fan_uploads-t,r=Math.round(t*(s.heic_pct/100)),i=(t*s.fan_photo_mb+a*s.fan_video_mb)/1024,h=(r*5+a*Math.max(s.fan_video_mb,5)+(t-r)*1)/3600,l=h*2,p=s.fan_uploads*4,d=[],m=[],c=g(_(o,"Railway CPU"),h,1,`${r} HEIC + ${a} videos`);c&&d.push(c);const b=g(_(o,"Railway RAM"),l,1);b&&d.push(b);const f=g(_(o,"Supabase Edge Invocations"),p,1e6);f&&d.push(f);const n=g(_(o,"Supabase Storage"),i,1);return n&&m.push(n),{oneOff:d,recurring:m}}function $(s,o){const t=s.downloads*s.download_mb/1024,a=s.downloads*2,r=[],i=g(_(o,"Supabase Egress"),t,1);i&&r.push(i);const h=g(_(o,"Cloudinary Bandwidth"),t*.3,1,"Optimized variants");h&&r.push(h);const l=g(_(o,"Supabase Edge Invocations"),a,1e6);return l&&r.push(l),{oneOff:r,recurring:[]}}function xe(s,o){let t=[],a=[];const r=c=>{t=t.concat(c.oneOff),a=a.concat(c.recurring)};s.preset==="custom"?(r(G(s,o)),r(V(s,o)),r(A(s,o)),r($(s,o))):s.preset==="galleries"?r(G(s,o)):s.preset==="campaign"?r(V(s,o)):s.preset==="fan_uploads"?r(A(s,o)):s.preset==="downloads"&&r($(s,o));const i=t.reduce((c,b)=>c+b.cost_usd,0),h=a.reduce((c,b)=>c+b.cost_usd,0),l=Math.max(1,s.months||1),p=i+h*l,d=[...t,...a],m={};for(const c of d)m[c.category]=(m[c.category]||0)+c.cost_usd;return{oneOffCost:i,monthlyRecurring:h,totalOverMonths:p,breakdown:d,byCategory:m}}const C=s=>new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:2}).format(s||0),_e=s=>new Intl.NumberFormat("en-US",{maximumFractionDigits:2}).format(s||0),z={supabase:"bg-emerald-500",railway:"bg-purple-500",cloudinary:"bg-sky-500",resend:"bg-orange-500",lovable_ai:"bg-pink-500"};function ge({breakdown:s,byCategory:o}){const t=Object.values(o).reduce((a,r)=>a+r,0);return t===0?e.jsx("div",{className:"text-sm text-muted-foreground text-center py-4",children:"No costs to display."}):e.jsxs("div",{className:"space-y-4",children:[e.jsx("div",{className:"flex h-3 rounded-full overflow-hidden bg-muted",children:Object.entries(o).map(([a,r])=>e.jsx("div",{className:z[a]||"bg-gray-400",style:{width:`${r/t*100}%`},title:`${a}: ${C(r)}`},a))}),e.jsx("div",{className:"space-y-1.5",children:Object.entries(o).sort(([,a],[,r])=>r-a).map(([a,r])=>e.jsxs("div",{className:"flex items-center justify-between text-sm",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:`h-2 w-2 rounded-full ${z[a]||"bg-gray-400"}`}),e.jsx("span",{className:"capitalize",children:a.replace("_"," ")})]}),e.jsx("span",{className:"font-medium",children:C(r)})]},a))}),e.jsxs("div",{className:"pt-3 border-t space-y-1.5",children:[e.jsx("div",{className:"text-xs font-semibold text-muted-foreground",children:"Line items"}),s.map((a,r)=>
1e.jsxs("div",{className:"flex items-center justify-between text-xs",children:[e.jsx("span",{className:"truncate pr-2",children:a.service}),e.jsx("span",{className:"text-muted-foreground tabular-nums",children:C(a.cost_usd)})]},r))]})]})}const fe=[{id:"galleries",label:"Galleries",description:"N galleries × photos × videos"},{id:"campaign",label:"Email Campaign",description:"N recipients with open/click rates"},{id:"fan_uploads",label:"Fan Uploads",description:"N submissions, photo/video mix, HEIC %"},{id:"downloads",label:"Landing Downloads",description:"N downloads of media files"},{id:"custom",label:"Custom (all scenarios combined)",description:"Mix everything in one simulation"}];function be(){const{data:s=[],isLoading:o}=J(),{data:t}=me(),[a,r]=T.useState(pe);T.useEffect(()=>{t&&r(n=>({...n,photo_mb:t.photo_avg_mb??n.photo_mb,video_mb:t.video_avg_mb??n.video_mb,fan_photo_mb:t.fan_photo_avg_mb??n.fan_photo_mb,fan_video_mb:t.fan_video_avg_mb??n.fan_video_mb,heic_pct:t.heic_pct??n.heic_pct,open_rate_pct:t.campaign_open_rate??n.open_rate_pct,click_rate_pct:t.campaign_click_rate??n.click_rate_pct}))},[t]);const i=(n,j)=>r(y=>({...y,[n]:j})),h=n=>r(j=>({...j,preset:n,...he[n]})),l=T.useMemo(()=>xe(a,s),[a,s]),p=()=>{const j=[["Service","Category","Quantity","Unit","Unit Price USD","Cost USD","Note"],...l.breakdown.map(v=>[v.service,v.category,String(v.quantity),v.unit,String(v.unit_price_usd),v.cost_usd.toFixed(4),v.note||""]),[],["One-off total","","","","",l.oneOffCost.toFixed(2),""],["Monthly recurring","","","","",l.monthlyRecurring.toFixed(2),""],[`Total over ${a.months} months`,"","","","",l.totalOverMonths.toFixed(2),""]].map(v=>v.map(Y=>`"${String(Y).replace(/"/g,'""')}"`).join(",")).join(`
2`),y=new Blob([j],{type:"text/csv"}),L=URL.createObjectURL(y),B=document.createElement("a");B.href=L,B.download=`cost-simulation-${a.preset}-${Date.now()}.csv`,B.click(),URL.revokeObjectURL(L)},d=a.preset==="galleries"||a.preset==="custom",m=a.preset==="campaign"||a.preset==="custom",c=a.preset==="fan_uploads"||a.preset==="custom",b=a.preset==="downloads"||a.preset==="custom",f=(n,j="")=>n!=null?e.jsxs("span",{className:"text-xs text-muted-foreground",children:["real avg: ",_e(n),j]}):null;return e.jsxs("div",{className:"grid lg:grid-cols-[1fr_400px] gap-6",children:[e.jsxs(O,{children:[e.jsx(E,{children:e.jsx(M,{children:"Scenario"})}),e.jsxs(k,{className:"space-y-6",children:[e.jsxs("div",{children:[e.jsx(U,{children:"Preset"}),e.jsxs(ee,{value:a.preset,onValueChange:n=>h(n),children:[e.jsx(se,{children:e.jsx(te,{})}),e.jsx(ae,{children:fe.map(n=>e.jsx(ne,{value:n.id,children:e.jsxs("div",{className:"flex flex-col",children:[e.jsx("span",{children:n.label}),e.jsx("span",{className:"text-xs text-muted-foreground",children:n.description})]})},n.id))})]})]}),e.jsxs("div",{children:[e.jsx(U,{children:"Time horizon (months for recurring storage)"}),e.jsx(u,{type:"number",min:1,value:a.months,onChange:n=>i("months",Number(n.target.value))})]}),d&&e.jsxs("fieldset",{className:"border rounded-md p-4 space-y-3",children:[e.jsx("legend",{className:"text-sm font-semibold px-2",children:"Galleries"}),e.jsxs(S,{children:[e.jsx(x,{label:"Number of galleries",children:e.jsx(u,{type:"number",value:a.galleries,onChange:n=>i("galleries",Number(n.target.value))})}),e.jsx(x,{label:"Photos per gallery",children:e.jsx(u,{type:"number",value:a.photos_per_gallery,onChange:n=>i("photos_per_gallery",Number(n.target.value))})}),e.jsx(x,{label:"Videos per gallery",children:e.jsx(u,{type:"number",value:a.videos_per_gallery,onChange:n=>i("videos_per_gallery",Number(n.target.value))})}),e.jsx(x,{label:"Avg photo size (MB)",hint:f(t==null?void 0:t.photo_avg_mb," MB"),children:e.jsx(u,{type:"number",step:"0.1",value:a.photo_mb,onChange:n=>i("photo_mb",Number(n.target.value))})}),e.jsx(x,{label:"Avg video size (MB)",hint:f(t==null?void 0:t.video_avg_mb," MB"),children:e.jsx(u,{type:"number",step:"0.1",value:a.video_mb,onChange:n=>i("video_mb",Number(n.target.value))})}),e.jsx(x,{label:"Views per gallery",children:e.jsx(u,{type:"number",value:a.views_per_gallery,onChange:n=>i("views_per_gallery",Number(n.target.value))})})]})]}),m&&e.jsxs("fieldset",{className:"border rounded-md p-4 space-y-3",children:[e.jsx("legend",{className:"text-sm font-semibold px-2",children:"Email Campaign"}),e.jsxs(S,{children:[e.jsx(x,{label:"Recipients",children:e.jsx(u,{type:"number",value:a.recipients,onChange:n=>i("recipients",Number(n.target.value))})}),e.jsx(x,{label:"Open rate (%)",hint:f(t==null?void 0:t.campaign_open_rate,"%"),children:e.jsx(u,{type:"number",value:a.open_rate_pct,onChange:n=>i("open_rate_pct",Number(n.target.value))})}),e.jsx(x,{label:"Click rate of opens (%)",hint:f(t==null?void 0:t.campaign_click_rate,"%"),children:e.jsx(u,{type:"number",value:a.click_rate_pct,onChange:n=>i("click_rate_pct",Number(n.target.value))})})]})]}),c&&e.jsxs("fieldset",{className:"border rounded-md p-4 space-y-3",children:[e.jsx("legend",{className:"text-sm font-semibold px-2",children:"Fan Uploads"}),e.jsxs(S,{children:[e.jsx(x,{label:"Total submissions",children:e.jsx(u,{type:"number",value:a.fan_uploads,onChange:n=>i("fan_uploads",Number(n.target.value))})}),e.jsx(x,{label:"Photo share (%)",children:e.jsx(u,{type:"number",value:a.fan_photo_pct,onChange:n=>i("fan_photo_pct",Number(n.target.value))})}
2),e.jsx(x,{label:"Avg fan photo (MB)",hint:f(t==null?void 0:t.fan_photo_avg_mb," MB"),children:e.jsx(u,{type:"number",step:"0.1",value:a.fan_photo_mb,onChange:n=>i("fan_photo_mb",Number(n.target.value))})}),e.jsx(x,{label:"Avg fan video (MB)",hint:f(t==null?void 0:t.fan_video_avg_mb," MB"),children:e.jsx(u,{type:"number",step:"0.1",value:a.fan_video_mb,onChange:n=>i("fan_video_mb",Number(n.target.value))})}),e.jsx(x,{label:"HEIC share of photos (%)",hint:f(t==null?void 0:t.heic_pct,"%"),children:e.jsx(u,{type:"number",value:a.heic_pct,onChange:n=>i("heic_pct",Number(n.target.value))})})]})]}),b&&e.jsxs("fieldset",{className:"border rounded-md p-4 space-y-3",children:[e.jsx("legend",{className:"text-sm font-semibold px-2",children:"Landing Downloads"}),e.jsxs(S,{children:[e.jsx(x,{label:"Total downloads",children:e.jsx(u,{type:"number",value:a.downloads,onChange:n=>i("downloads",Number(n.target.value))})}),e.jsx(x,{label:"Avg file size (MB)",children:e.jsx(u,{type:"number",step:"0.1",value:a.download_mb,onChange:n=>i("download_mb",Number(n.target.value))})})]})]})]})]}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs(O,{children:[e.jsx(E,{className:"pb-3",children:e.jsx(M,{className:"text-base",children:"Estimated Cost"})}),e.jsx(k,{children:o?e.jsx("div",{className:"flex justify-center py-6",children:e.jsx(K,{className:"h-6 w-6 animate-spin"})}):e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-sm text-muted-foreground",children:"One-off"}),e.jsx("div",{className:"text-3xl font-bold",children:C(l.oneOffCost)})]}),e.jsxs("div",{className:"flex justify-between text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Monthly recurring"}),e.jsx("span",{className:"font-medium",children:C(l.monthlyRecurring)})]}),e.jsxs("div",{className:"flex justify-between text-sm pt-2 border-t",children:[e.jsxs("span",{className:"text-muted-foreground",children:["Total over ",a.months," months"]}),e.jsx("span",{className:"font-semibold",children:C(l.totalOverMonths)})]}),e.jsxs(q,{variant:"outline",size:"sm",className:"w-full",onClick:p,children:[e.jsx(re,{className:"h-4 w-4 mr-2"}),"Export CSV"]})]})})]}),e.jsxs(O,{children:[e.jsx(E,{className:"pb-3",children:e.jsx(M,{className:"text-base",children:"By Service"})}),e.jsx(k,{children:e.jsx(ge,{breakdown:l.breakdown,byCategory:l.byCategory})})]}),(t==null?void 0:t.sampled_at)&&e.jsx("div",{className:"text-xs text-muted-foreground text-center",children:e.jsx(Q,{variant:"outline",className:"text-[10px]",children:"Baselines from real data"})})]})]})}const S=({children:s})=>e.jsx("div",{className:"grid sm:grid-cols-2 gap-3",children:s}),x=({label:s,hint:o,children:t})=>e.jsxs("div",{className:"space-y-1",children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-2",children:[e.jsx(U,{className:"text-xs",children:s}),o]}),t]});function je(){const{data:s=[],isLoading:o}=J(),t=ue(),[a,r]=T.useState({}),i=(l,p)=>r(d=>({...d,[l]:{...d[l],...p}})),h=async l=>{const p=a[l];if(p)try{await t.mutateAsync({id:l,unit_price_usd:p.unit_price_usd??0,notes:p.notes}),r(d=>{const m={...d};return delete m[l],m}),P.success("Price updated")}catch(d){P.error(d.message||"Failed to update")}};return o?e.jsx("div",{className:"flex justify-center py-8",children:e.jsx(K,{className:"h-6 w-6 animate-spin text-muted-foreground"})}):e.jsx("div",{className:"border rounded-md",children:e.jsxs(le,{children:[e.jsx(ie,{children:e.jsxs(D,{children:[e.jsx(N,{children:"Service"}),e.jsx(N,{children:"Category"}),e.jsx(N,{children:"Unit"}),e.jsx(N,{className:"w-40",children:"Price (USD)"}),e.jsx(N,{children:"Notes"}),e.jsx(N,{className:"w-24"})]})}),e.jsx(ce,{children:s.map(l=>{const p=a[l.id],d=!!p&&(p.unit_price_usd!==void 0||p.notes!==void 0);return e.jsxs(D,{children:[e.jsx(w,{className:"font-medium",children:l.service}),e.jsx(w,{children:e.jsx(Q,{variant:"outline",children:l.category})}),e.jsxs(w,{className:"text-sm text-muted-foreground",children:["per ",l.unit]}),e.jsx(w,{children:e.jsx(u,{type:"number",step:"0.000001",defaultValue:l.unit_price_usd,onChange:m=>i(l.id,{unit_price_usd:Number(m.target.value)}),className:"h-8"})}),e.jsx(w,{children:e.jsx(u,{defaultValue:l.notes||"",onChange:m=>i(l.id,{notes:m.target.value}),className:"h-8"})}),e.jsx(w,{children:e.jsxs(q,{size:"sm",disabled:!d||t.isPending,onClick:()=>h(l.id),children:[e.jsx(de,{className:"h-3 w-3 mr-1"}),"Save"]})})]},l.id)})})]})})}const Me=()=>e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-bold",children:"Cost Simulator"}),e.jsx("p",{className:"text-muted-foreground",children:"Estimate the cost of platform features using current pricing of Supabase, Railway, Cloudinary, Resend and L
2ovable AI."})]}),e.jsxs(X,{defaultValue:"simulator",children:[e.jsxs(Z,{children:[e.jsx(R,{value:"simulator",children:"Simulator"}),e.jsx(R,{value:"pricing",children:"Pricing Table"})]}),e.jsx(I,{value:"simulator",className:"mt-6",children:e.jsx(be,{})}),e.jsx(I,{value:"pricing",className:"mt-6",children:e.jsx(je,{})})]})]});export{Me 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.