Skip to content

Commit 6e3405d

Browse files
authored
Merge pull request #93 from UWB-ACM/pttran/feedback-emails
Pttran/feedback emails
2 parents 2210ef7 + 29198b6 commit 6e3405d

6 files changed

Lines changed: 464 additions & 9 deletions

File tree

src/app/dashboard/judging/JudgingAdminClient.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
actionAssignJudges,
77
actionAssignTables,
88
} from "@/src/util/actions/judge";
9-
import { actionSendTableAssignments } from "@/src/util/actions/actionSendTableAssignments";
9+
import { actionSendTableAssignments } from "@/src/util/actions/tableAssignments";
1010

1111
export default function JudgingAdminClient() {
1212
const [tableStatus, setTableStatus] = useState<
@@ -89,9 +89,9 @@ export default function JudgingAdminClient() {
8989
</h1>
9090

9191
<div className="bg-yellow-100 border-2 border-black p-4 mb-8 text-sm font-bold">
92-
⚠️ ATTENTION: Run table assignment first to place
93-
unassigned projects, then judge assignment to assign judges
94-
to every track for every project.
92+
⚠️ ATTENTION: Run table assignment first to place unassigned
93+
projects, then judge assignment to assign judges to every
94+
track for every project.
9595
</div>
9696

9797
<button

src/app/dashboard/notifications/BroadcastPanel.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
"use client";
22
import { useState } from "react";
33
import Link from "next/link";
4+
import { actionSendProjectFeedback } from "@/src/util/actions/projectFeedback";
45

56
export default function BroadcastPanel() {
67
const [message, setMessage] = useState<string>("");
78
const [loading, setLoading] = useState<boolean>(false);
89
const [status, setStatus] = useState<string | null>(null);
910

11+
const [emailStatus, setEmailStatus] = useState<
12+
"idle" | "loading" | "success" | "error"
13+
>("idle");
14+
1015
const sendBroadcast = async () => {
1116
if (!message.trim()) {
1217
setStatus("Message cannot be empty.");
@@ -38,8 +43,23 @@ export default function BroadcastPanel() {
3843
setLoading(false);
3944
}
4045
};
46+
47+
const sendFeedbackEmail = async () => {
48+
setEmailStatus("loading");
49+
50+
const result = await actionSendProjectFeedback();
51+
52+
if (result.success && result.failed && result.failed.length == 0) {
53+
setEmailStatus("success");
54+
} else {
55+
console.error("Sending feedback emails failed:", result);
56+
setEmailStatus("error");
57+
}
58+
};
59+
4160
return (
4261
<div className="min-h-screen w-full flex flex-col items-center justify-center p-4">
62+
{/* Broadcast using Discord */}
4363
<div className="flex flex-col text-center w-full max-w-lg mx-auto bg-white border border-black rounded-2xl shadow-sm p-6 space-y-4">
4464
<div>
4565
<h2 className="text-xl font-semibold text-black">
@@ -91,6 +111,43 @@ export default function BroadcastPanel() {
91111
</div>
92112
)}
93113
</div>
114+
115+
{/* Broadcast feedback emails */}
116+
<div className="flex flex-col text-center w-full max-w-lg mx-auto bg-white border border-black rounded-2xl shadow-sm p-6 mt-4 space-y-4">
117+
<div>
118+
<h2 className="text-xl font-semibold text-black">
119+
Send Feedback via Email
120+
</h2>
121+
<p className="text-sm text-black">
122+
Send a message to all users via email.
123+
</p>
124+
<button
125+
onClick={sendFeedbackEmail}
126+
disabled={emailStatus === "loading"}
127+
className={`py-2 px-4 w-full rounded-md bg-green-600 hover:bg-green-500 text-white duration-200 border-black border disabled:opacity-50 ${
128+
emailStatus === "loading"
129+
? "bg-blue-300 cursor-not-allowed"
130+
: "bg-blue-600 hover:bg-blue-700 text-white"
131+
}`}
132+
>
133+
{emailStatus === "loading"
134+
? "Sending..."
135+
: "Send Feedback via Email"}
136+
</button>
137+
138+
{emailStatus === "success" && (
139+
<div className="mt-3 text-sm px-3 py-2 rounded-lg bg-green-50 text-green-700 border border-green-200">
140+
Feedback emails sent successfully.
141+
</div>
142+
)}
143+
{emailStatus === "error" && (
144+
<div className="mt-3 text-sm px-3 py-2 rounded-lg bg-red-50 text-red-700 border border-red-200">
145+
Failed to send feedback emails.
146+
</div>
147+
)}
148+
</div>
149+
</div>
150+
94151
<Link
95152
href="/dashboard"
96153
className="m-10 py-2 px-4 w-full max-w-lg rounded-md bg-red-500 text-white text-center"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use server";
2+
import { sendProjectFeedbackEmails } from "../awsSes";
3+
import { ensureAdminPermission } from "../staff";
4+
import { getSession } from "../session";
5+
import { getAllProjectFeedback } from "../db/project";
6+
7+
export async function actionSendProjectFeedback() {
8+
try {
9+
const session = await getSession();
10+
await ensureAdminPermission(session);
11+
12+
const projectsWithFeedback = await getAllProjectFeedback();
13+
14+
const results = await sendProjectFeedbackEmails(projectsWithFeedback);
15+
16+
return {
17+
success: true,
18+
sent: results.allSuccessfulEmails.length,
19+
failed: results.allFailedEmails,
20+
};
21+
} catch (error) {
22+
console.error("actionSendProjectFeedback error:", error);
23+
24+
return {
25+
success: false,
26+
error: "Failed to send project feedback emails",
27+
};
28+
}
29+
}
File renamed without changes.

0 commit comments

Comments
 (0)