Skip to content

Feat announcement discussion notifier#53

Open
TrickkyRicky wants to merge 10 commits into
mainfrom
feat-announcement-discussion-notifier
Open

Feat announcement discussion notifier#53
TrickkyRicky wants to merge 10 commits into
mainfrom
feat-announcement-discussion-notifier

Conversation

@TrickkyRicky
Copy link
Copy Markdown
Contributor

@TrickkyRicky TrickkyRicky commented Apr 13, 2026

Summary

Adds automated email notifications whenever a new announcement or discussion thread is created, so platform members are immediately informed without having to check the app manually.


Email Notifications

New email templates

  • src/emails/AnnouncementNotification.tsx - email template for new announcements
  • src/emails/NewThreadNotification.tsx - email template for new discussion threads

New API routes

  • src/app/api/announcement-emails/route.ts - POST endpoint that looks up an announcement by ID, queries users filtered by the announcement's groupType (ALL, NONPROFIT, SUPPLIER, ADMIN), and sends a batch email via Resend
  • src/app/api/discussion-emails/route.ts - same pattern for discussion threads

Trigger integration

  • src/app/announcements/announcements-grid.tsx - fires POST /api/announcement-emails after a new announcement is created
  • src/app/discussion/discussion-grid.tsx - fires POST /api/discussion-emails after a new thread is created; both calls are fire-and-forget

Images


discussionProof AnnouncementProof

@TrickkyRicky TrickkyRicky requested a review from a team as a code owner April 13, 2026 04:40
Copy link
Copy Markdown

@Wesley-Baker Wesley-Baker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me.

@TrickkyRicky TrickkyRicky requested a review from mrysav April 19, 2026 00:49
Copy link
Copy Markdown
Contributor

@whao37 whao37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be some little bugs I've missed but these are the big ones I've seen. Please double check though since I might've missed reference files.

Comment on lines +9 to +48
export async function POST(req: Request) {
try {
const session = await auth();
if (!session || !session.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const { announcementId } = await req.json();

if (!announcementId) {
return NextResponse.json(
{ error: 'Announcement ID is required' },
{ status: 400 }
);
}

const announcement = await prisma.announcement.findUnique({
where: { id: announcementId },
include: {
author: { select: { name: true } },
},
});

if (!announcement) {
return NextResponse.json(
{ error: 'Announcement not found' },
{ status: 404 }
);
}

// Build role filter based on groupType; exclude users who opted out of emails
const roleFilter =
announcement.groupType === GroupType.ALL
? {}
: { role: announcement.groupType as unknown as UserRole };

const users = await prisma.user.findMany({
where: { ...roleFilter, announcementEmailOptOut: false },
select: { email: true, name: true },
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe any authenticated user can POST with an announcementId or threadId can trigger resent to every user who aren't opted out. I'm not sure if there is an admin/staff check already here but this could cause abuse from bad actors.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/api/announcement-emails: now requires role === ADMIN; any other authenticated user gets a 401.
/api/discussion-emails: now verifies the caller is the thread's author thread.author.id === session.user.id; mismatched callers get a 401.

Comment on lines +9 to +43
export async function POST(req: Request) {
try {
const session = await auth();
if (!session || !session.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const { threadId } = await req.json();

if (!threadId) {
return NextResponse.json(
{ error: 'Thread ID is required' },
{ status: 400 }
);
}

const thread = await prisma.thread.findUnique({
where: { id: threadId },
include: {
author: { select: { name: true } },
},
});

if (!thread) {
return NextResponse.json({ error: 'Thread not found' }, { status: 404 });
}

// Build role filter based on groupType; exclude users who opted out of emails
const roleFilter =
thread.groupType === GroupType.ALL ? {} : { role: thread.groupType };

const users = await prisma.user.findMany({
where: { ...roleFilter, discussionEmailOptOut: false },
select: { email: true, name: true },
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same pattern for threadId.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as abaove

Comment thread src/app/announcements/announcements-grid.tsx
Comment thread src/app/settings/email-settings-form.tsx Outdated
@TrickkyRicky TrickkyRicky requested a review from whao37 April 20, 2026 17:32
Copy link
Copy Markdown
Contributor

@whao37 whao37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixes look good to me

@mrysav
Copy link
Copy Markdown
Contributor

mrysav commented Apr 29, 2026

@TrickkyRicky To reiterate something we talked about synchronously: I think this PR looks good, but I know there's a bit more testing you want to do. Happy to approve and merge when you are ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants