16 — Templates (Screen 4, `/app/testimonial-templates`)
Cursor-ready plan: request/reminder template editor with variables, preview, and send-test for email/SMS outreach.
16 — Templates (Screen 4)
Section titled “16 — Templates (Screen 4)”Source: 02-Implementation-Blueprint.md — Screen 4 - Templates (/app/testimonial-templates).
Product alignment: 01-Post-Purchase-Video-Testimonial-Collector-Plan.md — Messaging in MVP (request + reminders).
This document is a build spec only. No code changes are implied until a task references this file.
Related: 07-email-sms-request-delivery-pipeline.md (template consumption in sender worker), 13-requests-log-blueprint-screen-10.md (diagnostics), 03-Pricing-Options.md (incentive messaging context).
0) Goal (one sentence)
Section titled “0) Goal (one sentence)”Merchants can create/edit message templates for initial and reminder outreach, use trusted variables, preview final output, and run a safe send-test before turning campaigns on.
1) Route and file
Section titled “1) Route and file”| Item | Value |
|---|---|
| Suggested file | app/routes/app.testimonial-templates.jsx |
| URL | /app/testimonial-templates |
Auth: authenticate.admin(request); templates are shop-scoped.
2) Data model
Section titled “2) Data model”Use blueprint TestimonialTemplate (§5.4):
shopIdcampaignId(nullable for default/global template)channel(email,sms)templateType(initial_request,reminder_1,reminder_2)subject(email only)bodyisDefault
Retain unique constraint:
@@unique([shopId, campaignId, channel, templateType])
3) UI structure (Polaris)
Section titled “3) UI structure (Polaris)”- Template selector
- Channel: Email / SMS
- Template type: Initial / Reminder 1 / Reminder 2
- Scope: Global default vs campaign-specific (if campaigns exist)
- Editor
- Subject (shown only when
channel=email) - Body editor (plain textarea v1; rich editor optional v2)
- Subject (shown only when
- Variables help panel
{{customer_first_name}}{{shop_name}}{{product_name}}{{submission_link}}{{incentive_text}}
- Preview pane
- Render subject/body with sample data
- Actions
- Save template
- Send test
4) Template rendering rules
Section titled “4) Template rendering rules”Create a shared helper (e.g. app/lib/testimonial-template-render.server.js) used by both:
- this screen preview,
- send-test action,
- actual sending worker (
07).
4.1 Replacement behavior
Section titled “4.1 Replacement behavior”- Unknown tokens remain unchanged (helps detect typos).
- Missing value fallback:
customer_first_name->thereincentive_text-> empty string
- Collapse extra blank lines after replacement.
4.2 Validation
Section titled “4.2 Validation”- Enforce allowed token set only (warn on unsupported token patterns).
- Body required for all channels.
- Subject required only for email.
- SMS length warning (e.g. > 320 chars) but do not block save unless product requires.
5) Preview behavior
Section titled “5) Preview behavior”Use synthetic sample data in loader:
customer_first_name = "Alex"shop_name = current shop nameproduct_name = "Sample Product"submission_link = https://{app}/t/sample-tokenincentive_text = "Get 10% off after approved submission"
Preview updates live as user edits.
6) Send-test flow
Section titled “6) Send-test flow”6.1 Inputs
Section titled “6.1 Inputs”- Test recipient email (for email channel) or phone (for sms channel)
- Optional sample product name override
6.2 Action
Section titled “6.2 Action”- Intent:
send_test_template - Render selected template with sample payload.
- Send through existing provider integrations:
- Email:
app/lib/email.server.js - SMS: provider adapter used in
07
- Email:
6.3 Safety
Section titled “6.3 Safety”- Rate-limit send-test per shop/user (e.g. 10/hour).
- Mark message as test in subject/body prefix:
[TEST]. - Log send result (optional
TemplateSendTestLogtable or structured app logs).
7) Save behavior
Section titled “7) Save behavior”- Intent:
save_template - Upsert by
(shopId, campaignId, channel, templateType). - Track
updatedAt. - Return toast on success.
Optional:
- “Reset to default” button for campaign-specific template deletes custom row and falls back to global default.
8) Fallback resolution order (critical for worker)
Section titled “8) Fallback resolution order (critical for worker)”When sender pipeline requests a template:
- Campaign-specific template (
campaignId = X) - Shop-level default (
campaignId = null,isDefault=true) - Hardcoded system fallback (last resort, deterministic text)
This order must be identical in:
- send-test,
- preview,
- delivery worker.
9) Testing checklist
Section titled “9) Testing checklist”- Save email and sms templates independently.
- Subject hidden/ignored for SMS.
- Variables render correctly in preview.
- Unknown variable warning appears.
- Send-test dispatches and records success/failure.
- Campaign-specific template overrides default in resolution order.
- Cross-shop access blocked.
10) Implementation order (for Cursor)
Section titled “10) Implementation order (for Cursor)”- Build loader with selector state + existing template fetch.
- Build template render helper with strict allowed variables.
- Add save action (upsert).
- Add preview pane.
- Add send-test action + rate limit.
- Wire sender worker (
07) to shared template resolver.
11) References
Section titled “11) References”02-Implementation-Blueprint.md— Screen 4,§5.407-email-sms-request-delivery-pipeline.md— template consumption in sends/reminders13-requests-log-blueprint-screen-10.md— debugging delivery failures
12) Note on numbering
Section titled “12) Note on numbering”This folder already includes 05 through 15 plans. This file is 16-... for consistent sequence.