Skip to content

Commit 78d0a0e

Browse files
committed
send event invite when request approved
1 parent 23133a8 commit 78d0a0e

File tree

7 files changed

+84
-23
lines changed

7 files changed

+84
-23
lines changed

src/app/api/approval/rsvp/route.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,12 @@ export async function PUT(req: NextRequest) {
354354
receiver: updatedRequest.user,
355355
type: emailType,
356356
event: updatedRequest.event,
357+
// For approved requests, include the RSVP type for iCal generation
358+
approvalRsvpType:
359+
status === "APPROVED" &&
360+
["GOING", "MAYBE", "NOT_GOING"].includes(approvalRequest.rsvpType)
361+
? (approvalRequest.rsvpType as "GOING" | "MAYBE" | "NOT_GOING")
362+
: undefined,
357363
}).catch((error) =>
358364
console.error(
359365
`Error sending approval ${status.toLowerCase()} email: ${

src/components/Email/templates/ApprovalApproved.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,43 @@ export const ApprovalApprovedEmailTemplate: React.FC<
141141
</a>
142142
</div>
143143

144+
<div
145+
style={{
146+
backgroundColor: "rgba(16, 185, 129, 0.05)",
147+
borderRadius: "8px",
148+
padding: "16px",
149+
fontSize: "14px",
150+
marginBottom: "24px",
151+
}}
152+
>
153+
<p
154+
style={{
155+
margin: "0 0 8px 0",
156+
fontWeight: "500",
157+
}}
158+
>
159+
Quick tips:
160+
</p>
161+
<ul
162+
style={{
163+
margin: "0",
164+
paddingLeft: "20px",
165+
color: "#6b7280",
166+
listStyleType: "disc",
167+
}}
168+
>
169+
<li style={{ margin: "4px 0" }}>
170+
Add this to your calendar using the attached .ics file
171+
</li>
172+
<li style={{ margin: "4px 0" }}>
173+
You&apos;ll get a reminder 1 hour before the event
174+
</li>
175+
<li style={{ margin: "4px 0" }}>
176+
Need to change your RSVP? No worries, just visit the event page
177+
</li>
178+
</ul>
179+
</div>
180+
144181
<p style={{ margin: "24px 0 0 0", color: "#6b7280", fontSize: "14px" }}>
145182
You&apos;re all set! We&apos;ll send you reminders as the event
146183
approaches. You can always update your RSVP or view event details by

src/components/EventPage/ViewOtherRSVPs.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,11 @@ export default function ViewOtherRSVPs({
3434
<TooltipProvider key={key}>
3535
<Tooltip>
3636
<TooltipTrigger asChild>
37-
<div>
38-
<UserImage
39-
photo={getUserImage(rsvp.attendee)}
40-
size="sm"
41-
userId={rsvp.attendee.id}
42-
/>
43-
</div>
37+
<UserImage
38+
photo={getUserImage(rsvp.attendee)}
39+
size="sm"
40+
userId={rsvp.attendee.id}
41+
/>
4442
</TooltipTrigger>
4543
<TooltipContent>
4644
<p>{rsvp.attendee.nickname}</p>

src/components/ui/default-user-image.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { cn } from "src/lib/utils";
22
import type { ComponentType } from "react";
3+
import { forwardRef } from "react";
34
import {
45
CoolEmoji,
56
HeartEyesEmoji,
@@ -83,17 +84,15 @@ export const DefaultUserImage = ({
8384
);
8485
};
8586

86-
export const UserImage = ({
87-
photo,
88-
size = "md",
89-
className,
90-
userId,
91-
}: {
92-
photo?: string | null;
93-
size?: "sm" | "md" | "lg";
94-
className?: string;
95-
userId: string;
96-
}) => {
87+
export const UserImage = forwardRef<
88+
HTMLImageElement,
89+
{
90+
photo?: string | null;
91+
size?: "sm" | "md" | "lg";
92+
className?: string;
93+
userId: string;
94+
}
95+
>(({ photo, size = "md", className, userId }, ref) => {
9796
if (!photo) {
9897
// Use default profile picture instead of emoji avatar
9998
const containerClass = cn(
@@ -106,7 +105,9 @@ export const UserImage = ({
106105
const defaultImage = getDefaultProfilePicture(userId);
107106

108107
// eslint-disable-next-line @next/next/no-img-element
109-
return <img className={containerClass} src={defaultImage} alt="" />;
108+
return (
109+
<img ref={ref} className={containerClass} src={defaultImage} alt="" />
110+
);
110111
}
111112

112113
const containerClass = cn(
@@ -116,5 +117,7 @@ export const UserImage = ({
116117
);
117118

118119
// eslint-disable-next-line @next/next/no-img-element
119-
return <img className={containerClass} src={photo} alt="" />;
120-
};
120+
return <img ref={ref} className={containerClass} src={photo} alt="" />;
121+
});
122+
123+
UserImage.displayName = "UserImage";

src/lib/queue/email.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface EmailJobData {
1111
type?: EmailType;
1212
text?: string;
1313
previousRsvpType?: "GOING" | "MAYBE" | "NOT_GOING";
14+
approvalRsvpType?: "GOING" | "MAYBE" | "NOT_GOING";
1415
// Option 2: Send with pre-built email options
1516
emailOptions?: CreateEmailOptions;
1617
}

src/lib/queue/workers/email.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ export const startEmailWorker = () => {
391391
type,
392392
text,
393393
previousRsvpType,
394+
approvalRsvpType,
394395
} = job.data;
395396

396397
if (!receiver || !rawEvent || !type) {
@@ -451,7 +452,13 @@ export const startEmailWorker = () => {
451452
);
452453

453454
// Check if this email type needs iCal generation
454-
const rsvpType = emailTypeToRsvpType(validatedType);
455+
let rsvpType = emailTypeToRsvpType(validatedType);
456+
457+
// For approval-approved emails, use the explicit approval RSVP type if provided
458+
if (validatedType === "approval-approved" && approvalRsvpType) {
459+
rsvpType = approvalRsvpType as any; // Convert string to RSVP_TYPE
460+
}
461+
455462
let iCal: string | null = null;
456463

457464
// Only generate iCal for RSVP-related emails, not approval notifications

src/utils/email/send.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,29 @@ export const sendEventEmail = async ({
1919
type,
2020
text,
2121
previousRsvpType,
22+
approvalRsvpType,
2223
returnOptionsOnly = false,
2324
}: {
2425
receiver: User;
2526
type: EmailType;
2627
text?: string;
2728
event: EventWithProposerAndRsvps;
2829
previousRsvpType?: "GOING" | "MAYBE" | "NOT_GOING";
30+
approvalRsvpType?: "GOING" | "MAYBE" | "NOT_GOING";
2931
returnOptionsOnly?: boolean;
3032
}): Promise<CreateEmailOptions | { id: string | number }> => {
3133
if (!receiver.email) {
3234
throw new Error(`receiver ${receiver.id} has no email`);
3335
}
3436

3537
// Determine the method based on RSVP type and generate iCal if needed
36-
const rsvpType = emailTypeToRsvpType(type);
38+
let rsvpType = emailTypeToRsvpType(type);
39+
40+
// For approval-approved emails, use the explicit approval RSVP type if provided
41+
if (type === "approval-approved" && approvalRsvpType) {
42+
rsvpType = approvalRsvpType as any; // Convert string to RSVP_TYPE
43+
}
44+
3745
let iCal: string | null = null;
3846

3947
// Only generate iCal for RSVP-related emails, not for approval notifications
@@ -149,6 +157,7 @@ export const sendEventEmail = async ({
149157
type,
150158
text,
151159
previousRsvpType,
160+
approvalRsvpType,
152161
});
153162

154163
console.log(`Email to ${receiver.email} added to Redis queue for ${type}`);

0 commit comments

Comments
 (0)