email service and template using resend
This commit is contained in:
@@ -6,6 +6,8 @@ import { authOptions } from "@/lib/auth";
|
||||
import { isAdminEmail } from "@/lib/admin";
|
||||
import { refundService } from "@/server/services/refund.service";
|
||||
import { auditLog } from "@/server/services/audit-log.service";
|
||||
import { emailService } from "@/lib/email/send";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { createRefundSchema, refundDecisionSchema } from "./schemas";
|
||||
|
||||
async function requireAdmin() {
|
||||
@@ -51,6 +53,9 @@ export async function createRefundAction(formData: FormData) {
|
||||
reason: parsed.data.reason,
|
||||
},
|
||||
});
|
||||
|
||||
void notifyRefundCreated(refund.id);
|
||||
|
||||
revalidatePath("/admin/refunds");
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
@@ -58,6 +63,77 @@ export async function createRefundAction(formData: FormData) {
|
||||
}
|
||||
}
|
||||
|
||||
async function notifyRefundCreated(refundId: string) {
|
||||
const refund = await prisma.refund.findUnique({
|
||||
where: { id: refundId },
|
||||
include: {
|
||||
booking: {
|
||||
include: {
|
||||
user: { select: { email: true, name: true } },
|
||||
trip: { select: { title: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!refund) return;
|
||||
await emailService.send({
|
||||
to: refund.booking.user.email,
|
||||
idempotencyKey: `refund_created-${refund.id}`,
|
||||
template: {
|
||||
template: "refund_created",
|
||||
data: {
|
||||
userName: refund.booking.user.name,
|
||||
tripTitle: refund.booking.trip.title,
|
||||
amount: refund.amount,
|
||||
reason: refund.reason,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function notifyRefundDecision(
|
||||
refundId: string,
|
||||
decision: "SUCCEEDED" | "FAILED",
|
||||
adminNote: string
|
||||
) {
|
||||
const refund = await prisma.refund.findUnique({
|
||||
where: { id: refundId },
|
||||
include: {
|
||||
booking: {
|
||||
include: {
|
||||
user: { select: { email: true, name: true } },
|
||||
trip: { select: { title: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!refund) return;
|
||||
await emailService.send({
|
||||
to: refund.booking.user.email,
|
||||
idempotencyKey: `refund_${decision.toLowerCase()}-${refund.id}`,
|
||||
template:
|
||||
decision === "SUCCEEDED"
|
||||
? {
|
||||
template: "refund_succeeded",
|
||||
data: {
|
||||
userName: refund.booking.user.name,
|
||||
tripTitle: refund.booking.trip.title,
|
||||
amount: refund.amount,
|
||||
adminNote,
|
||||
},
|
||||
}
|
||||
: {
|
||||
template: "refund_failed",
|
||||
data: {
|
||||
userName: refund.booking.user.name,
|
||||
tripTitle: refund.booking.trip.title,
|
||||
amount: refund.amount,
|
||||
adminNote,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function decideRefundAction(formData: FormData) {
|
||||
const admin = await requireAdmin();
|
||||
if (!admin) return { error: "Tidak memiliki akses admin" };
|
||||
@@ -110,6 +186,13 @@ export async function decideRefundAction(formData: FormData) {
|
||||
entityId: refundId,
|
||||
payload: adminNote ? { adminNote } : undefined,
|
||||
});
|
||||
|
||||
// Notif email user kalau decision final (SUCCEEDED/FAILED) — APPROVE/REJECT
|
||||
// intermediate, refund_created sudah dikirim sebelumnya.
|
||||
if (decision === "SUCCEEDED" || decision === "FAILED") {
|
||||
void notifyRefundDecision(refundId, decision, adminNote ?? "");
|
||||
}
|
||||
|
||||
revalidatePath("/admin/refunds");
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user