Skip to content

Commit e190944

Browse files
committed
Fixes
1 parent ef462df commit e190944

6 files changed

Lines changed: 49 additions & 27 deletions

File tree

app/api/invitations/route.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ export async function POST(request: NextRequest) {
8484
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
8585
}
8686

87-
const { email, firstName, lastName } = await request.json();
88-
if (!email || !firstName || !lastName) {
87+
const { email, firstName, lastName, graduated } = await request.json();
88+
if (!email || !firstName || !lastName || graduated === undefined) {
8989
return NextResponse.json(
90-
{ error: "Email, firstName, and lastName are required" },
90+
{ error: "Email, firstName, lastName, and graduated are required" },
9191
{ status: 400 },
9292
);
9393
}
@@ -109,6 +109,7 @@ export async function POST(request: NextRequest) {
109109
email,
110110
firstName,
111111
lastName,
112+
graduated: Boolean(graduated),
112113
sentAt: Timestamp.now(),
113114
sentBy: tokens.decodedToken.uid,
114115
});

app/api/posts/route.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ const isDev = process.env.NODE_ENV === "development";
1212
async function sendPostNotifications(
1313
subscribers: { email: string }[],
1414
title: string,
15-
description: string
15+
description: string,
16+
authorName: string
1617
) {
1718
const feedLink = `${APP_URL}/feed`;
1819

1920
if (isDev) {
2021
console.log(`\n📬 [DEV] Post notification (not sent)`);
22+
console.log(` Author: ${authorName}`);
2123
console.log(` Title: ${title}`);
2224
console.log(` To: ${subscribers.map((u) => u.email).join(", ")}`);
2325
console.log(` Link: ${feedLink}\n`);
@@ -27,10 +29,11 @@ async function sendPostNotifications(
2729
const resend = new Resend(process.env.RESEND_API_KEY);
2830
await resend.batch.send(
2931
subscribers.map((u) => ({
30-
from: "AppDev Alumni <noreply@alumni.cornellappdev.com>",
32+
from: `${authorName} (via AppDev Alumni) <noreply@alumni.cornellappdev.com>`,
3133
to: u.email,
3234
subject: `New post: ${title}`,
3335
html: `
36+
<p><strong>${authorName}</strong> posted:</p>
3437
<h2>${title}</h2>
3538
<p>${description}</p>
3639
<p><a href="${feedLink}">View in feed</a></p>
@@ -67,12 +70,15 @@ export async function POST(request: NextRequest) {
6770
});
6871

6972
const usersSnap = await adminDb.collection("users").get();
70-
const subscribers = usersSnap.docs
71-
.map((d) => d.data())
72-
.filter((u) => u.emailNotifications === true && u.email);
73+
const allUsers = usersSnap.docs.map((d) => d.data());
74+
75+
const author = allUsers.find((u) => u.uid === tokens.decodedToken.uid);
76+
const authorName = author ? `${author.firstName} ${author.lastName}` : "Someone";
77+
78+
const subscribers = allUsers.filter((u) => u.emailNotifications === true && u.email);
7379

7480
if (subscribers.length > 0) {
75-
await sendPostNotifications(subscribers as { email: string }[], title, description);
81+
await sendPostNotifications(subscribers as { email: string }[], title, description, authorName);
7682
}
7783

7884
return NextResponse.json({ ok: true });

app/api/user/complete/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export async function POST(request: NextRequest) {
2020
const isAdmin = email === SUPER_ADMIN_EMAIL;
2121

2222
let invCode: string | null = null;
23+
let invGraduated: boolean | undefined;
2324
if (!isAdmin) {
2425
const invSnap = await adminDb
2526
.collection("invitations")
@@ -32,6 +33,7 @@ export async function POST(request: NextRequest) {
3233
);
3334
}
3435
invCode = invSnap.docs[0].id;
36+
invGraduated = invSnap.docs[0].data().graduated as boolean;
3537
}
3638

3739
const profileData: Record<string, unknown> = {
@@ -50,7 +52,8 @@ export async function POST(request: NextRequest) {
5052
if (phoneNumber) profileData.phoneNumber = normalizePhone(phoneNumber) ?? phoneNumber;
5153
if (profilePictureUrl) profileData.profilePictureUrl = profilePictureUrl;
5254
if (cityId) profileData.cityId = cityId;
53-
if (graduated !== undefined) profileData.graduated = graduated;
55+
const resolvedGraduated = invGraduated !== undefined ? invGraduated : graduated;
56+
if (resolvedGraduated !== undefined) profileData.graduated = resolvedGraduated;
5457
if (linkedinUrl) profileData.linkedinUrl = linkedinUrl;
5558
if (instagramUrl) profileData.instagramUrl = instagramUrl;
5659
if (isAdmin) profileData.role = "admin";

components/admin/InvitationList.tsx

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import { useEffect, useState } from "react";
44
import { Invitation } from "@/types";
5-
import { Button } from "@/components/ui/button";
65
import { Badge } from "@/components/ui/badge";
76
import { toast } from "sonner";
87

@@ -15,7 +14,7 @@ export function InvitationList({ refreshKey }: { refreshKey?: number }) {
1514
setLoading(true);
1615
fetch("/api/invitations")
1716
.then((r) => (r.ok ? r.json() : []))
18-
.then(setInvitations)
17+
.then((data: Invitation[]) => setInvitations(data.filter((i) => !i.usedAt)))
1918
.catch(console.error)
2019
.finally(() => setLoading(false));
2120
}, [refreshKey]);
@@ -58,20 +57,14 @@ export function InvitationList({ refreshKey }: { refreshKey?: number }) {
5857
</p>
5958
</div>
6059
<div className="flex items-center gap-3 shrink-0">
61-
{inv.usedAt ? (
62-
<Badge variant="secondary">Accepted</Badge>
63-
) : (
64-
<>
65-
<Badge variant="outline">Pending</Badge>
66-
<button
67-
className="text-sm underline text-muted-foreground hover:text-foreground disabled:opacity-50"
68-
disabled={revoking === inv.code}
69-
onClick={() => rescind(inv.code, `${inv.firstName} ${inv.lastName}`)}
70-
>
71-
{revoking === inv.code ? "Cancelling…" : "Cancel"}
72-
</button>
73-
</>
74-
)}
60+
<Badge variant="outline">Pending</Badge>
61+
<button
62+
className="text-sm underline text-muted-foreground hover:text-foreground disabled:opacity-50"
63+
disabled={revoking === inv.code}
64+
onClick={() => rescind(inv.code, `${inv.firstName} ${inv.lastName}`)}
65+
>
66+
{revoking === inv.code ? "Cancelling…" : "Cancel"}
67+
</button>
7568
</div>
7669
</div>
7770
))}

components/admin/InviteForm.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import { z } from "zod";
77
import { Button } from "@/components/ui/button";
88
import { Input } from "@/components/ui/input";
99
import { Label } from "@/components/ui/label";
10+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
1011
import { toast } from "sonner";
1112

1213
const schema = z.object({
1314
firstName: z.string().min(1, "Required"),
1415
lastName: z.string().min(1, "Required"),
1516
email: z.string().email(),
17+
graduated: z.enum(["true", "false"]),
1618
});
1719

1820
type FormData = z.infer<typeof schema>;
@@ -24,6 +26,7 @@ export function InviteForm({ onSuccess }: { onSuccess?: () => void }) {
2426
register,
2527
handleSubmit,
2628
reset,
29+
setValue,
2730
formState: { errors },
2831
} = useForm<FormData>({ resolver: zodResolver(schema) });
2932

@@ -33,7 +36,7 @@ export function InviteForm({ onSuccess }: { onSuccess?: () => void }) {
3336
const res = await fetch("/api/invitations", {
3437
method: "POST",
3538
headers: { "Content-Type": "application/json" },
36-
body: JSON.stringify(data),
39+
body: JSON.stringify({ ...data, graduated: data.graduated === "true" }),
3740
});
3841
if (!res.ok) {
3942
const body = await res.json();
@@ -84,6 +87,21 @@ export function InviteForm({ onSuccess }: { onSuccess?: () => void }) {
8487
<p className="text-sm text-destructive">{errors.email.message}</p>
8588
)}
8689
</div>
90+
<div className="space-y-1">
91+
<Label>Status</Label>
92+
<Select onValueChange={(v) => setValue("graduated", v as "true" | "false")}>
93+
<SelectTrigger>
94+
<SelectValue placeholder="Select…" />
95+
</SelectTrigger>
96+
<SelectContent>
97+
<SelectItem value="true">Alumni (graduated)</SelectItem>
98+
<SelectItem value="false">Student (current)</SelectItem>
99+
</SelectContent>
100+
</Select>
101+
{errors.graduated && (
102+
<p className="text-sm text-destructive">Required</p>
103+
)}
104+
</div>
87105
<Button type="submit" disabled={loading}>
88106
{loading ? "Sending…" : "Send invite"}
89107
</Button>

types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export interface Invitation {
5959
email: string;
6060
firstName: string;
6161
lastName: string;
62+
graduated: boolean;
6263
sentAt: string;
6364
usedAt?: string;
6465
sentBy: string;

0 commit comments

Comments
 (0)