PageSourceSearch

https://splashiq.ai/assets/templates-Buiss5db.js

js splashiq.ai collected 2026-10-05 12:43:34 UTC 9,062 bytes, 12 lines download raw bytes

1import{Y as u,am as h,Z as b,j as e,r,M as A,B as l,F as I,as as V,I as Y,t as o}from"./index-CzAkNt2W.js";import{u as p}from"./useServerFn-7MfwXlwR.js";import{L as j}from"./label-BwWVuV93.js";import{T as $}from"./textarea-CMw1Ny2O.js";import{D as g,b as w,c as N,d as T,f as k}from"./dialog-DFazxlVc.js";import{R as M}from"./RequireRole-DhUlFC_T.js";import{P as W}from"./plus-B3ftHR68.js";import{T as z}from"./trash-2-Bb14b2qT.js";const B=u({method:"GET"}).middleware([h]).handler(b("ea3b2e03510df15b26e1cf3326151895dab5993659fe85eafee4ccc35ca24618")),K=u({method:"POST"}).middleware([h]).handler(b("dac4f50f1f5ec6a428d7079bb66b5dd09d8cec7bd62217d16fe7fb7213fe4b50")),U=u({method:"POST"}).middleware([h]).handler(b("b35455d1ed55e7b7f5d3b134e503e4c9b1a2ff1c9e88f8fbfa25a04ac7250bfb")),J=u({method:"POST"}).middleware([h]).handler(b("9dbcd5e51b76ef2cd47258b171eadbb293a0ed7d927a66ae4fa172b2487460cd")),S=[{value:"service_report",label:"Service Report (after each visit)"},{value:"invoice",label:"Invoice"},{value:"quote",label:"Quote / Estimate"},{value:"welcome",label:"Welcome / Onboarding"},{value:"reminder",label:"Visit Reminder"}],G={service_report:{subject:"Your pool service report — {{visit_date}}",body_html:`<p>Hi {{customer_first_name}},</p>
2<p>Your pool at {{property_address}} was serviced today by {{technician_name}}. Here's the summary:</p>
3<p><strong>Chemistry:</strong> pH {{ph}}, Chlorine {{chlorine}} ppm, Alkalinity {{alkalinity}}</p>
4<p><strong>Notes:</strong><br>{{ai_note}}</p>
5<p>View the full report with before/after photos: <a href="{{portal_url}}">your private portal</a></p>
6<p>— The {{company_name}} team</p>`},invoice:{subject:"Invoice #{{invoice_number}} from {{company_name}}",body_html:`<p>Hi {{customer_first_name}},</p>
7<p>Your invoice for {{amount}} is ready. View the details at <a href="{{portal_url}}">your private portal</a> and contact us for payment instructions.</p>
8<p>Due {{due_date}}.</p>`},quote:{subject:"New quote from {{company_name}}: {{quote_title}}",body_html:`<p>Hi {{customer_first_name}},</p>
9<p>We've prepared a quote for: <strong>{{quote_title}}</strong> — {{amount}}</p>
10<p><a href="{{portal_url}}">Review and approve</a></p>`},welcome:{subject:"Welcome to {{company_name}}",body_html:`<p>Hi {{customer_first_name}},</p>
11<p>Welcome aboard. Your homeowner portal is ready: <a href="{{portal_url}}">{{portal_url}}</a></p>`},reminder:{subject:"Heads up — pool service {{visit_date}}",body_html:`<p>Hi {{customer_first_name}},</p>
12<p>{{technician_name}} will be by on {{visit_date}} for your scheduled service.</p>`}};function Q(){const[d,x]=r.useState([]),[_,n]=r.useState(!0),[a,i]=r.useState(null),[C,c]=r.useState(!1),[f,D]=r.useState("service_report"),[L,v]=r.useState(!1),P=p(B),E=p(K),F=p(U),R=p(J);async function m(){n(!0);try{const t=await P();x(t.templates??[])}catch(t){o.error(t?.message??"Failed to load templates")}finally{n(!1)}}r.useEffect(()=>{m()},[]);async function H(){const t=G[f]??{subject:"",body_html:""};try{const s=await E({data:{kind:f,subject:t.subject,body_html:t.body_html}});o.success("Template created"),c(!1),i(s),m()}catch(s){o.error(s?.message??"Failed to create")}}async function O(){if(a)try{await F({data:{id:a.id,subject:a.subject,body_html:a.body_html}}),o.success("Saved"),m()}catch(t){o.error(t?.message??"Failed to save")}}async function q(t){if(confirm("Delete this template?"))try{await R({data:{id:t}}),i(null),m()}catch(s){o.error(s?.message??"Failed to delete")}}const y=t=>S.find(s=>s.value===t)?.label??t;return _?e.jsx("div",{className:"p-8 text-muted-foreground",children:"Loading…"}):e.jsxs("div",{className:"p-6 md:p-8 max-w-6xl",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3 mb-6 flex-wrap",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"font-display text-3xl font-bold flex items-center gap-2",children:[e.jsx(A,{className:"h-7 w-7 text-primary"})," Email Templates"]}),e.jsxs("p",{className:"text-sm text-muted-foreground mt-1",children:["Reusable templates sent to homeowners. Variables like ",e.jsx("code",{className:"text-xs",children:"{{customer_first_name}}"}
12)," are filled in automatically."]})]}),e.jsxs(l,{onClick:()=>c(!0),className:"gap-2",children:[e.jsx(W,{className:"h-4 w-4"})," New template"]})]}),e.jsxs("div",{className:"grid lg:grid-cols-[260px_1fr] gap-6",children:[e.jsxs("aside",{className:"space-y-2",children:[d.length===0&&e.jsxs("div",{className:"rounded-xl border border-dashed border-border p-6 text-center text-sm text-muted-foreground",children:[e.jsx(I,{className:"h-6 w-6 mx-auto mb-2"}),"No templates yet"]}),d.map(t=>e.jsxs("button",{onClick:()=>i(t),className:`w-full text-left rounded-lg border p-3 transition-colors ${a?.id===t.id?"border-primary bg-sky":"border-border bg-card hover:bg-muted/50"}`,children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-muted-foreground",children:y(t.kind)}),e.jsx("div",{className:"font-medium text-sm mt-0.5 truncate",children:t.subject})]},t.id))]}),e.jsx("section",{children:a?e.jsxs("div",{className:"rounded-2xl border border-border bg-card p-6 space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2 flex-wrap",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-muted-foreground",children:y(a.kind)}),e.jsxs("div",{className:"text-xs text-muted-foreground mt-0.5",children:["Last updated ",new Date(a.updated_at).toLocaleString()]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(l,{size:"sm",variant:"outline",onClick:()=>v(!0),className:"gap-1.5",children:[e.jsx(V,{className:"h-3.5 w-3.5"})," Preview"]}),e.jsxs(l,{size:"sm",variant:"outline",onClick:()=>q(a.id),className:"gap-1.5 text-destructive",children:[e.jsx(z,{className:"h-3.5 w-3.5"})," Delete"]})]})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Subject"}),e.jsx(Y,{value:a.subject,onChange:t=>i({...a,subject:t.target.value})})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Body (HTML)"}),e.jsx($,{rows:16,value:a.body_html,onChange:t=>i({...a,body_html:t.target.value}),className:"font-mono text-xs"})]}),e.jsxs("div",{className:"rounded-lg bg-muted p-3 text-xs text-muted-foreground",children:[e.jsx("div",{className:"font-semibold mb-1 text-foreground",children:"Available variables"}),e.jsx("code",{className:"block",children:"{{customer_first_name}} · {{customer_full_name}} · {{property_address}} · {{visit_date}}"}),e.jsx("code",{className:"block",children:"{{technician_name}} · {{company_name}} · {{portal_url}} · {{ai_note}}"}),e.jsx("code",{className:"block",children:"{{ph}} · {{chlorine}} · {{alkalinity}} · {{invoice_number}} · {{amount}} · {{due_date}}"})]}),e.jsx("div",{className:"flex justify-end gap-2 pt-2",children:e.jsx(l,{onClick:O,children:"Save template"})})]}):e.jsx("div",{className:"rounded-2xl border border-dashed border-border p-12 text-center text-muted-foreground",children:"Select a template to edit, or create a new one."})})]}),e.jsx(g,{open:C,onOpenChange:c,children:e.jsxs(w,{children:[e.jsx(N,{children:e.jsx(T,{children:"New template"})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(j,{children:"Type"}),e.jsx("select",{className:"w-full h-10 rounded-md border border-input bg-transparent px-3 text-sm",value:f,onChange:t=>D(t.target.value),children:S.map(t=>e.jsx("option",{value:t.value,children:t.label},t.value))}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"A sensible default subject and body will be pre-filled — you can edit after."})]}),e.jsxs(k,{children:[e.jsx(l,{variant:"outline",onClick:()=>c(!1),children:"Cancel"}),e.jsx(l,{onClick:H,children:"Create"})]})]})}),a&&e.jsx(g,{open:L,onOpenChange:v,children:e.jsxs(w,{className:"max-w-2xl",children:[e.jsx(N,{children:e.jsxs(T,{children:["Preview · ",a.subject.replace(/\{\{[^}]+\}\}/g,"[sample]")]})}),e.jsx("iframe",{title:"Template preview",sandbox:"",srcDoc:`<!doctype html><html><head><meta charset="utf-8"><base target="_blank"><style>body{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#0f172a;padding:16px;margin:0;font-size:14px;line-height:1.5}</style></head><body>${Z(a.body_html)}</body></html>`,className:"rounded-lg border border-border bg-background w-full h-[60vh]"}),e.jsx(k,{children:e.jsx(l,{variant:"outline",onClick:()=>v(!1),children:"Close"})})]})})]})}function Z(d){const x={customer_first_name:"Jane",customer_full_name:"Jane Smith",property_address:"12 Pool Lane",visit_date:new Date().toLocaleDateString(),technician_name:"Alex Rivera",company_name:"Crystal Pools",portal_url:"https://example.com/portal/sample",ai_note:"Skimmed surface, brushed walls, balanced chemistry.",ph:"7.4",chlorine:"3.0",alkalinity:"100",invoice_number:"INV-2026-0042",amount:"$185.00",due_date:"in 14 days",quote_title:"New variable-speed pump install",payment_url:"https://example.com/pay/sample"};return d.replace(/\{\{(\w+)\}\}/g,(_,n)=>x[n]??`[${n}]`)}const ne=()=>e.jsx(M,{allowed:["owner","office","superadmin","company_marketing"],children:e.jsx(Q,{})});
12export{ne as component};

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.