From a10d355934760225230ab24f2f07333edc4f3d98 Mon Sep 17 00:00:00 2001 From: Lewis Carhart Date: Tue, 11 Feb 2025 14:40:28 +0000 Subject: [PATCH 1/4] feat: notifications for risk tasks --- .../create-organization-action.ts | 2 +- apps/app/src/components/header.tsx | 2 - .../src/components/notification-center.tsx | 8 +- apps/app/src/env.mjs | 4 +- .../notifications/risk-task-notifications.ts | 94 +++++++ .../utils/task-email-notification.tsx | 43 +++ apps/app/src/lib/domains.ts | 12 +- apps/app/trigger.config.ts | 2 + bun.lockb | Bin 679136 -> 669088 bytes package.json | 2 +- packages/db/prisma/schema.prisma | 1 + .../email/emails/reminders/task-reminder.tsx | 109 ++++++++ packages/notifications/src/index.ts | 19 +- yarn.lock | 254 ++++-------------- 14 files changed, 319 insertions(+), 233 deletions(-) create mode 100644 apps/app/src/jobs/tasks/notifications/risk-task-notifications.ts create mode 100644 apps/app/src/jobs/tasks/notifications/utils/task-email-notification.tsx create mode 100644 packages/email/emails/reminders/task-reminder.tsx diff --git a/apps/app/src/actions/organization/create-organization-action.ts b/apps/app/src/actions/organization/create-organization-action.ts index 78bee2c03b..8bf579a056 100644 --- a/apps/app/src/actions/organization/create-organization-action.ts +++ b/apps/app/src/actions/organization/create-organization-action.ts @@ -32,7 +32,7 @@ export const createOrganizationAction = authActionClient const hasVercelConfig = Boolean( process.env.NEXT_PUBLIC_VERCEL_URL && - process.env.VERCEL_AUTH_TOKEN && + process.env.VERCEL_ACCESS_TOKEN && process.env.VERCEL_TEAM_ID && process.env.VERCEL_PROJECT_ID ); diff --git a/apps/app/src/components/header.tsx b/apps/app/src/components/header.tsx index 66d085c55a..8de627a235 100644 --- a/apps/app/src/components/header.tsx +++ b/apps/app/src/components/header.tsx @@ -3,8 +3,6 @@ import { getI18n } from "@/locales/server"; import { Button } from "@bubba/ui/button"; import { Icons } from "@bubba/ui/icons"; import { Skeleton } from "@bubba/ui/skeleton"; -import { Inbox } from "@novu/react"; -import { useSession } from "next-auth/react"; import Link from "next/link"; import { Suspense } from "react"; import { AssistantButton } from "./assistant/button"; diff --git a/apps/app/src/components/notification-center.tsx b/apps/app/src/components/notification-center.tsx index 2a264153c3..b6c3d0a6f3 100644 --- a/apps/app/src/components/notification-center.tsx +++ b/apps/app/src/components/notification-center.tsx @@ -44,17 +44,17 @@ function NotificationItem({ type: string; }) { switch (type) { - case "inapp_task_reminder": + case "task": return (
setOpen(false)} - href={`/tasks/${recordId}`} + href={`${recordId}`} >
- +
@@ -109,6 +109,8 @@ export function NotificationCenter() { } }, [hasUnseenNotifications, isOpen]); + console.log(notifications); + return ( diff --git a/apps/app/src/env.mjs b/apps/app/src/env.mjs index 7273fdb6fb..f433ea6be6 100644 --- a/apps/app/src/env.mjs +++ b/apps/app/src/env.mjs @@ -17,7 +17,7 @@ export const env = createEnv({ UPLOADTHING_SECRET: z.string(), DISCORD_WEBHOOK_URL: z.string(), TRIGGER_SECRET_KEY: z.string(), - VERCEL_AUTH_TOKEN: z.string().optional(), + VERCEL_ACCESS_TOKEN: z.string().optional(), VERCEL_TEAM_ID: z.string().optional(), VERCEL_PROJECT_ID: z.string().optional(), }, @@ -46,7 +46,7 @@ export const env = createEnv({ TRIGGER_SECRET_KEY: process.env.TRIGGER_SECRET_KEY, NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST, - VERCEL_AUTH_TOKEN: process.env.VERCEL_AUTH_TOKEN, + VERCEL_ACCESS_TOKEN: process.env.VERCEL_ACCESS_TOKEN, VERCEL_TEAM_ID: process.env.VERCEL_TEAM_ID, VERCEL_PROJECT_ID: process.env.VERCEL_PROJECT_ID, NEXT_PUBLIC_VERCEL_URL: process.env.NEXT_PUBLIC_VERCEL_URL, diff --git a/apps/app/src/jobs/tasks/notifications/risk-task-notifications.ts b/apps/app/src/jobs/tasks/notifications/risk-task-notifications.ts new file mode 100644 index 0000000000..8a4af4e58f --- /dev/null +++ b/apps/app/src/jobs/tasks/notifications/risk-task-notifications.ts @@ -0,0 +1,94 @@ +import { db } from "@bubba/db"; +import { + NotificationTypes, + TriggerEvents, + trigger, +} from "@bubba/notifications"; +import { logger, schedules } from "@trigger.dev/sdk/v3"; +import { formatDistance } from "date-fns"; + +export const sendRiskTaskNotifications = schedules.task({ + id: "send-risk-task-notifications", + run: async () => { + const now = new Date(); + const upcomingThreshold = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); + + logger.info( + `Sending risk task notifications from now: ${now} to ${upcomingThreshold}`, + ); + + const tasks = await db.riskMitigationTask.findMany({ + where: { + dueDate: { gte: now, lte: upcomingThreshold }, + status: { in: ["open", "pending"] }, + notifiedAt: null, + }, + select: { + id: true, + dueDate: true, + notifiedAt: true, + riskId: true, + title: true, + owner: { + select: { + id: true, + email: true, + name: true, + image: true, + organizationId: true, + }, + }, + }, + }); + + const notifiedTasks = []; + + for (const task of tasks) { + const owner = task.owner; + + if (!owner || !owner.email || !owner.organizationId) { + logger.warn(`Skipping task ${task.id} - owner ${owner?.id} missing email or organizationId`); + continue; + } + + const timeUntilDue = task.dueDate + ? formatDistance(task.dueDate, new Date(), { + addSuffix: true, + }) + : "soon"; + + try { + await db.riskMitigationTask.update({ + where: { id: task.id }, + data: { notifiedAt: new Date() }, + }); + + await trigger({ + name: TriggerEvents.TaskReminderInApp, + user: { + subscriberId: `${owner.organizationId}_${owner.id}`, + email: owner.email, + fullName: owner.name, + image: owner.image, + organizationId: owner.organizationId, + }, + payload: { + description: `${task.title} is due ${timeUntilDue}`, + recordId: `/risk/${task.riskId}/tasks/${task.id}`, + type: NotificationTypes.Task, + }, + }); + + notifiedTasks.push(task.id); + } catch (error) { + logger.error( + `Error processing task ${task.id} for ${owner.email}: ${error}`, + ); + } + } + + if (notifiedTasks.length) { + logger.info(`Sent notifications for tasks: ${notifiedTasks.join(", ")}`); + } + }, +}); \ No newline at end of file diff --git a/apps/app/src/jobs/tasks/notifications/utils/task-email-notification.tsx b/apps/app/src/jobs/tasks/notifications/utils/task-email-notification.tsx new file mode 100644 index 0000000000..5dd394fd0d --- /dev/null +++ b/apps/app/src/jobs/tasks/notifications/utils/task-email-notification.tsx @@ -0,0 +1,43 @@ +import TaskReminderEmail from "@bubba/email/emails/reminders/task-reminder"; +import { TriggerEvents, trigger } from "@bubba/notifications"; +import { render } from "@react-email/render"; + +interface Props { + owner: { + id: string; + fullName?: string; + email: string; + organizationId: string; + }; + task: { + recordId: string; + dueDate: string; + }; +} + +export async function sendTaskEmailNotification({ owner, task }: Props) { + const html = await render( + , + ); + + const triggerData = { + name: TriggerEvents.TaskReminderEmail, + payload: { + subject: "Task Reminder", + html, + }, + replyTo: owner.email, + user: { + subscriberId: `${owner.organizationId}_${owner.id}`, + organizationId: owner.organizationId, + email: owner.email, + fullName: owner.fullName, + }, + }; + + await trigger(triggerData); +} diff --git a/apps/app/src/lib/domains.ts b/apps/app/src/lib/domains.ts index db62848dae..9a25b8f37f 100644 --- a/apps/app/src/lib/domains.ts +++ b/apps/app/src/lib/domains.ts @@ -12,7 +12,7 @@ export const addDomainToVercel = async (domain: string) => { { method: "POST", headers: { - Authorization: `Bearer ${process.env.VERCEL_AUTH_TOKEN}`, + Authorization: `Bearer ${process.env.VERCEL_ACCESS_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ @@ -29,7 +29,7 @@ export const removeDomainFromVercelProject = async (domain: string) => { }`, { headers: { - Authorization: `Bearer ${process.env.VERCEL_AUTH_TOKEN}`, + Authorization: `Bearer ${process.env.VERCEL_ACCESS_TOKEN}`, }, method: "DELETE", }, @@ -42,7 +42,7 @@ export const removeDomainFromVercelTeam = async (domain: string) => { }`, { headers: { - Authorization: `Bearer ${process.env.VERCEL_AUTH_TOKEN}`, + Authorization: `Bearer ${process.env.VERCEL_ACCESS_TOKEN}`, }, method: "DELETE", }, @@ -59,7 +59,7 @@ export const getDomainResponse = async ( { method: "GET", headers: { - Authorization: `Bearer ${process.env.VERCEL_AUTH_TOKEN}`, + Authorization: `Bearer ${process.env.VERCEL_ACCESS_TOKEN}`, "Content-Type": "application/json", }, }, @@ -77,7 +77,7 @@ export const getConfigResponse = async ( { method: "GET", headers: { - Authorization: `Bearer ${process.env.VERCEL_AUTH_TOKEN}`, + Authorization: `Bearer ${process.env.VERCEL_ACCESS_TOKEN}`, "Content-Type": "application/json", }, }, @@ -94,7 +94,7 @@ export const verifyDomain = async ( { method: "POST", headers: { - Authorization: `Bearer ${process.env.VERCEL_AUTH_TOKEN}`, + Authorization: `Bearer ${process.env.VERCEL_ACCESS_TOKEN}`, "Content-Type": "application/json", }, }, diff --git a/apps/app/trigger.config.ts b/apps/app/trigger.config.ts index f633ce6dd6..dc475f0515 100644 --- a/apps/app/trigger.config.ts +++ b/apps/app/trigger.config.ts @@ -1,4 +1,5 @@ import { PrismaInstrumentation } from "@prisma/instrumentation"; +import { syncVercelEnvVars } from "@trigger.dev/build/extensions/core"; import { prismaExtension } from "@trigger.dev/build/extensions/prisma"; import { puppeteer } from "@trigger.dev/build/extensions/puppeteer"; import { defineConfig } from "@trigger.dev/sdk/v3"; @@ -15,6 +16,7 @@ export default defineConfig({ schema: "../../packages/db/prisma/schema.prisma", }), puppeteer(), + syncVercelEnvVars(), ], }, retries: { diff --git a/bun.lockb b/bun.lockb index 338ee6ab696677c61cd8337e41989432cf8fc80f..abdc0556d9b3fc246766c93d9a138c7981cb380c 100755 GIT binary patch delta 35922 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 delta 38746 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 S2~gD!s9-?M_9_!DHAVo$(+9Bt diff --git a/package.json b/package.json index f00f715880..763d29863c 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@biomejs/biome": "1.9.4", - "turbo": "^2.3.4", + "turbo": "^2.4.1", "typescript": "5.7.2" }, "engines": { diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index fb88ae0eae..8c57bdf6a2 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -418,6 +418,7 @@ model RiskMitigationTask { description String status RiskTaskStatus @default(open) dueDate DateTime? + notifiedAt DateTime? completedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/packages/email/emails/reminders/task-reminder.tsx b/packages/email/emails/reminders/task-reminder.tsx new file mode 100644 index 0000000000..83ba41039e --- /dev/null +++ b/packages/email/emails/reminders/task-reminder.tsx @@ -0,0 +1,109 @@ +import { + Body, + Button, + Container, + Font, + Heading, + Html, + Link, + Preview, + Section, + Tailwind, + Text, +} from "@react-email/components"; +import { Footer } from "../../components/footer"; +import { Logo } from "../../components/logo"; + +interface Props { + email?: string; + name: string; + dueDate: string; + recordId: string; +} + +export const TaskReminderEmail = ({ + email, + name, + dueDate, + recordId, +}: Props) => { + const link = `${process.env.NEXT_PUBLIC_APP_URL}${recordId}`; + + return ( + + + + + + + + + Comp AI - Task Reminder + + + + + + Task Reminder + + + + Hey {name}, you're assigned to a task that is due soon ({dueDate} + ). + +

+ +
+ + + or copy and paste this URL into your browser{" "} + + {link} + + + +
+
+ + this notification was intended for{" "} + {email}.{" "} + +
+ +
+ +