post-completion flow: survey, referral program, review ask

- Migration 081: referral_codes, referral_uses, exit_surveys tables
- API: POST /api/v1/survey, POST /api/v1/referral/check, GET /api/v1/referral/:email
- Worker: completion_emails.py — sends completion + 24h follow-up (survey + referral)
- Survey page: /survey/?order=X&rating=N — star rating, feedback, Google review ask
- Referral: REF-FIRSTNAME codes, $25 credit per referred order, no limit
- Low ratings (1-3 stars) trigger Telegram alert for admin follow-up
- Cron: every 15 minutes
This commit is contained in:
justin 2026-05-30 21:22:14 -05:00
parent 6b20ba7f08
commit ad3d189b2b
6 changed files with 655 additions and 0 deletions

View file

@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex">
<title>How Did We Do? | Performance West</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<script>
window.__PW_API = (function() {
var h = window.location.hostname;
if (h === "localhost" || h === "127.0.0.1") return "http://" + h + ":3001";
if (h === "dev.performancewest.net") return "https://api.dev.performancewest.net";
return "https://api.performancewest.net";
})();
</script>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, system-ui, sans-serif; background: #f8fafc; min-height: 100vh; display: flex; flex-direction: column; }
.header { background: #1a2744; padding: 20px; text-align: center; }
.header img { height: 40px; }
.main { flex: 1; padding: 24px; max-width: 520px; margin: 0 auto; width: 100%; }
.card { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 32px; margin-bottom: 16px; }
h1 { font-size: 22px; color: #1a2744; margin-bottom: 8px; text-align: center; }
.subtitle { font-size: 15px; color: #64748b; text-align: center; margin-bottom: 24px; }
.stars { display: flex; justify-content: center; gap: 8px; margin-bottom: 24px; }
.star { font-size: 40px; cursor: pointer; transition: transform 0.1s; opacity: 0.3; }
.star.active { opacity: 1; }
.star:hover { transform: scale(1.2); }
textarea { width: 100%; padding: 12px; border: 1px solid #d1d5db; border-radius: 8px; font-size: 14px; resize: vertical; min-height: 80px; font-family: inherit; }
.btn { display: block; width: 100%; padding: 14px; background: #f97316; color: #fff; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; margin-top: 16px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.review-box { background: #f0fdf4; border: 2px solid #86efac; border-radius: 12px; padding: 24px; text-align: center; margin-top: 16px; }
.referral-box { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 12px; padding: 20px; margin-top: 16px; }
.referral-code { background: #fff; border: 2px dashed #3b82f6; border-radius: 8px; padding: 12px; text-align: center; margin: 12px 0; }
.referral-code span { font-size: 24px; font-weight: 700; color: #1e3a5f; letter-spacing: 1px; }
.footer { padding: 16px; text-align: center; font-size: 11px; color: #94a3b8; border-top: 1px solid #e2e8f0; }
.hidden { display: none; }
</style>
</head>
<body>
<div class="header">
<img src="/images/logo.png" alt="Performance West">
</div>
<div class="main">
<!-- Survey form -->
<div id="survey-form" class="card">
<h1>How was your experience?</h1>
<p class="subtitle">Your feedback helps us serve truckers better.</p>
<div class="stars" id="star-row">
<span class="star" data-rating="1"></span>
<span class="star" data-rating="2"></span>
<span class="star" data-rating="3"></span>
<span class="star" data-rating="4"></span>
<span class="star" data-rating="5"></span>
</div>
<p id="rating-label" style="text-align:center;font-size:14px;color:#64748b;margin-bottom:16px"></p>
<label style="font-size:14px;font-weight:600;color:#374151;display:block;margin-bottom:6px">Anything you'd like us to know? <span style="color:#94a3b8">(optional)</span></label>
<textarea id="feedback" placeholder="What went well? What could we improve?"></textarea>
<button type="button" id="submit-btn" class="btn" disabled>Submit Feedback</button>
</div>
<!-- Thank you + review ask (shown after submit if rating >= 4) -->
<div id="thank-you" class="hidden">
<div class="card" style="text-align:center">
<div style="font-size:48px;margin-bottom:12px">🙏</div>
<h1>Thank you for your feedback!</h1>
<p class="subtitle">We appreciate you taking the time.</p>
</div>
<div id="review-ask" class="review-box hidden">
<h3 style="font-size:16px;color:#166534;margin-bottom:8px">Glad you had a great experience!</h3>
<p style="font-size:14px;color:#374151;margin-bottom:16px">Would you mind leaving us a quick Google review? It helps other truckers find us and takes about 30 seconds.</p>
<a id="google-review-link" href="#" target="_blank" style="display:inline-block;padding:12px 28px;background:#059669;color:#fff;font-weight:600;border-radius:8px;text-decoration:none;font-size:15px">Leave a Google Review →</a>
</div>
<div id="referral-section" class="referral-box hidden">
<h3 style="font-size:16px;color:#1e3a5f;margin-bottom:6px">Know another trucker who needs help?</h3>
<p style="font-size:14px;color:#1d4ed8;margin-bottom:4px">Share your referral code and earn <strong>$25 credit</strong> for each order they place.</p>
<div class="referral-code">
<p style="font-size:11px;color:#64748b;margin-bottom:4px">Your referral code:</p>
<span id="referral-code-display"></span>
</div>
<p style="font-size:12px;color:#64748b">They enter this code at checkout. You earn $25 per order — no limit.</p>
</div>
</div>
</div>
<div class="footer">
Performance West Inc. &middot; (888) 411-0383 &middot; performancewest.net
</div>
<script>
var API = window.__PW_API;
var params = new URLSearchParams(window.location.search);
var orderNumber = params.get("order") || "";
var preRating = parseInt(params.get("rating") || "0");
var selectedRating = 0;
var stars = document.querySelectorAll(".star");
var ratingLabel = document.getElementById("rating-label");
var submitBtn = document.getElementById("submit-btn");
var labels = ["", "Poor", "Fair", "Good", "Great", "Excellent"];
function setRating(r) {
selectedRating = r;
stars.forEach(function(s, i) {
s.classList.toggle("active", i < r);
});
ratingLabel.textContent = labels[r] || "";
submitBtn.disabled = false;
}
stars.forEach(function(s) {
s.addEventListener("click", function() {
setRating(parseInt(s.dataset.rating));
});
});
// Pre-select from URL param
if (preRating >= 1 && preRating <= 5) {
setRating(preRating);
}
submitBtn.addEventListener("click", function() {
if (!selectedRating || !orderNumber) return;
submitBtn.disabled = true;
submitBtn.textContent = "Submitting...";
fetch(API + "/api/v1/survey", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
order_number: orderNumber,
rating: selectedRating,
feedback: document.getElementById("feedback").value,
would_recommend: selectedRating >= 4,
}),
})
.then(function(r) { return r.json(); })
.then(function(d) {
document.getElementById("survey-form").classList.add("hidden");
document.getElementById("thank-you").classList.remove("hidden");
// Show Google review if high rating
if (d.show_review && d.google_review_url) {
var reviewEl = document.getElementById("review-ask");
reviewEl.classList.remove("hidden");
document.getElementById("google-review-link").href = d.google_review_url;
}
// Show referral code
if (d.referral_code) {
document.getElementById("referral-section").classList.remove("hidden");
document.getElementById("referral-code-display").textContent = d.referral_code;
}
})
.catch(function() {
submitBtn.textContent = "Submit Feedback";
submitBtn.disabled = false;
alert("Something went wrong. Please try again.");
});
});
</script>
</body>
</html>