Feature: Dynamic time-of-day specific positive reinforcement notifications
This commit is contained in:
parent
3a31c8a956
commit
95f0d94411
@ -11,6 +11,69 @@ if (process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY && process.env.VAPID_PRIVATE_KEY) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MESSAGES = {
|
||||||
|
morning: [
|
||||||
|
"Rise and shine! Today is a perfect day to stay on track.",
|
||||||
|
"New morning, new strength. Let's keep that streak alive!",
|
||||||
|
"Success starts with your first choice today. You've got this!",
|
||||||
|
"Your morning check-in starts now. Every smoke-free minute counts!",
|
||||||
|
"The morning is yours. Claim it by logging your progress.",
|
||||||
|
"Small steps lead to big changes. Start your day by logging in!"
|
||||||
|
],
|
||||||
|
afternoon: [
|
||||||
|
"Keep the momentum going! You're doing great this afternoon.",
|
||||||
|
"Halfway through the day and looking strong. Log your progress!",
|
||||||
|
"The afternoon slump is no match for your willpower. Stay focused!",
|
||||||
|
"Just checking in—how's your journey going? Log it now!",
|
||||||
|
"You're stronger than any craving. Keep pushing forward!",
|
||||||
|
"Consistency is key to mastery. You’re doing amazing today!"
|
||||||
|
],
|
||||||
|
evening: [
|
||||||
|
"Wind down with a win. Another day closer to your goal!",
|
||||||
|
"Evening reflection: You faced the day and won. Log your success!",
|
||||||
|
"Don't let the evening cravings win. You're almost there!",
|
||||||
|
"Sweet dreams are made of smoke-free days. Keep it up!",
|
||||||
|
"The night is calm, and so are you. Finish the day strong!",
|
||||||
|
"Final check-in for the day! Log your progress and sleep proud.",
|
||||||
|
"Progress isn't always linear, but you're showing up. Great job!",
|
||||||
|
"You survived another day. Celebrate with a quick log!"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
function getNotificationData(timeStr: string) {
|
||||||
|
// timeStr is HH:MM
|
||||||
|
const [h, m] = timeStr.split(':').map(Number);
|
||||||
|
const totalMinutes = h * 60 + m;
|
||||||
|
|
||||||
|
let category: 'morning' | 'afternoon' | 'evening';
|
||||||
|
|
||||||
|
// Morning: 5:30 (330m) - 11:00 (660m)
|
||||||
|
// Afternoon: 11:00 (660m) - 16:30 (990m)
|
||||||
|
// Evening: 16:30 (990m) - 5:30 (330m)
|
||||||
|
|
||||||
|
if (totalMinutes >= 330 && totalMinutes < 660) {
|
||||||
|
category = 'morning';
|
||||||
|
} else if (totalMinutes >= 660 && totalMinutes < 990) {
|
||||||
|
category = 'afternoon';
|
||||||
|
} else {
|
||||||
|
category = 'evening';
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = MESSAGES[category];
|
||||||
|
const message = options[Math.floor(Math.random() * options.length)];
|
||||||
|
|
||||||
|
const titles = [
|
||||||
|
"QuitTraq Motivation",
|
||||||
|
"QuitTraq Milestone",
|
||||||
|
"QuitTraq Strength",
|
||||||
|
"QuitTraq Journey",
|
||||||
|
"QuitTraq Check-in"
|
||||||
|
];
|
||||||
|
const title = titles[Math.floor(Math.random() * titles.length)];
|
||||||
|
|
||||||
|
return { title, message };
|
||||||
|
}
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
// Protect with a secret if configured
|
// Protect with a secret if configured
|
||||||
@ -37,6 +100,7 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
let shouldSend = false;
|
let shouldSend = false;
|
||||||
let notificationBody = '';
|
let notificationBody = '';
|
||||||
|
let notificationTitle = '';
|
||||||
let tag = '';
|
let tag = '';
|
||||||
|
|
||||||
if (user.frequency === 'hourly') {
|
if (user.frequency === 'hourly') {
|
||||||
@ -52,7 +116,9 @@ export async function GET(request: NextRequest) {
|
|||||||
if (userTimeString >= startStr && userTimeString <= endStr && currentM === startM) {
|
if (userTimeString >= startStr && userTimeString <= endStr && currentM === startM) {
|
||||||
if (user.lastNotifiedDate !== currentHourKey) {
|
if (user.lastNotifiedDate !== currentHourKey) {
|
||||||
shouldSend = true;
|
shouldSend = true;
|
||||||
notificationBody = "How are you doing? Log your status to stay on track!";
|
const { title: t, message } = getNotificationData(userTimeString);
|
||||||
|
notificationBody = message;
|
||||||
|
notificationTitle = t;
|
||||||
tag = 'hourly-reminder';
|
tag = 'hourly-reminder';
|
||||||
|
|
||||||
// Update to match current Hour Key so we don't send again this hour
|
// Update to match current Hour Key so we don't send again this hour
|
||||||
@ -69,7 +135,9 @@ export async function GET(request: NextRequest) {
|
|||||||
if (userTimeString === user.reminderTime) {
|
if (userTimeString === user.reminderTime) {
|
||||||
if (user.lastNotifiedDate !== userDateString) {
|
if (user.lastNotifiedDate !== userDateString) {
|
||||||
shouldSend = true;
|
shouldSend = true;
|
||||||
notificationBody = "Time to log your daily usage! Every day counts.";
|
const { title: t, message } = getNotificationData(userTimeString);
|
||||||
|
notificationBody = message;
|
||||||
|
notificationTitle = t;
|
||||||
tag = 'daily-reminder';
|
tag = 'daily-reminder';
|
||||||
|
|
||||||
await updateLastNotifiedD1(user.userId, userDateString);
|
await updateLastNotifiedD1(user.userId, userDateString);
|
||||||
@ -78,9 +146,8 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldSend) {
|
if (shouldSend) {
|
||||||
const title = user.frequency === 'hourly' ? 'QuitTraq Hourly Check-in' : 'QuitTraq Reminder';
|
|
||||||
const payload = JSON.stringify({
|
const payload = JSON.stringify({
|
||||||
title: title,
|
title: notificationTitle,
|
||||||
body: notificationBody,
|
body: notificationBody,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
url: '/'
|
url: '/'
|
||||||
|
|||||||
@ -247,14 +247,14 @@ export function UnifiedQuitPlanCard({
|
|||||||
if (!showNicotine && !showWeed) return null;
|
if (!showNicotine && !showWeed) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="backdrop-blur-xl shadow-xl border-white/10 overflow-hidden">
|
<Card className="backdrop-blur-2xl shadow-2xl border-white/10 overflow-hidden bg-white/5">
|
||||||
<CardHeader className="pb-3 border-b border-white/5 bg-white/5">
|
<CardHeader className="pb-1 pt-6 px-4 sm:px-6">
|
||||||
<CardTitle className="flex items-center gap-2 text-sm sm:text-base font-black uppercase tracking-widest opacity-80">
|
<CardTitle className="flex items-center gap-2 text-sm sm:text-base font-black uppercase tracking-widest opacity-60">
|
||||||
<TrendingDown className="h-5 w-5 text-primary" />
|
<TrendingDown className="h-4 w-4 text-primary" />
|
||||||
Quit Journey Plan
|
Quit Journey Plan
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="pt-4 p-3 sm:p-6">
|
<CardContent className="pt-2 p-2 sm:p-4">
|
||||||
{showNicotine && (
|
{showNicotine && (
|
||||||
<SubstancePlanSection
|
<SubstancePlanSection
|
||||||
substance="nicotine"
|
substance="nicotine"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user