email service and template using resend
This commit is contained in:
@@ -10,6 +10,9 @@ import {
|
||||
type ReuploadField,
|
||||
} from "@/server/services/organizer.service";
|
||||
import { auditLog } from "@/server/services/audit-log.service";
|
||||
import { emailService } from "@/lib/email/send";
|
||||
import { organizerRepo } from "@/server/repositories/organizer.repo";
|
||||
import { userRepo } from "@/server/repositories/user.repo";
|
||||
import { submitVerificationSchema, reviewVerificationSchema } from "./schemas";
|
||||
|
||||
export async function submitVerificationAction(formData: FormData) {
|
||||
@@ -86,6 +89,10 @@ export async function reviewVerificationAction(formData: FormData) {
|
||||
? { rejectionReason: result.data.rejectionReason ?? null }
|
||||
: undefined,
|
||||
});
|
||||
|
||||
// Notif email — fire and forget, jangan blok response.
|
||||
void notifyVerificationDecision(result.data.verificationId, result.data.decision, result.data.rejectionReason);
|
||||
|
||||
revalidatePath("/admin/verifications");
|
||||
revalidatePath("/verify");
|
||||
revalidatePath("/profile");
|
||||
@@ -95,6 +102,41 @@ export async function reviewVerificationAction(formData: FormData) {
|
||||
}
|
||||
}
|
||||
|
||||
async function notifyVerificationDecision(
|
||||
verificationId: string,
|
||||
decision: "APPROVED" | "REJECTED",
|
||||
rejectionReason?: string
|
||||
) {
|
||||
const verification = await organizerRepo.findById(verificationId);
|
||||
if (!verification) return;
|
||||
const user = await userRepo.findById(verification.userId);
|
||||
if (!user) return;
|
||||
|
||||
if (decision === "APPROVED") {
|
||||
await emailService.send({
|
||||
to: user.email,
|
||||
idempotencyKey: `kyc_approved-${verificationId}`,
|
||||
template: {
|
||||
template: "kyc_approved",
|
||||
data: { userName: user.name },
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await emailService.send({
|
||||
to: user.email,
|
||||
// submissionCount supaya kalau reject berulang masing-masing dapat email.
|
||||
idempotencyKey: `kyc_rejected-${verificationId}-${verification.submissionCount}`,
|
||||
template: {
|
||||
template: "kyc_rejected",
|
||||
data: {
|
||||
userName: user.name,
|
||||
rejectionReason: rejectionReason ?? "(tidak ada alasan tercatat)",
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin reopen pengajuan REJECTED ke PENDING — supaya organizer bisa
|
||||
* di-review ulang tanpa drop & recreate row. Note wajib min 10 char untuk audit.
|
||||
@@ -159,6 +201,10 @@ export async function requestReuploadAction(
|
||||
entityId: verificationId,
|
||||
payload: { fields: valid, note: note.trim() },
|
||||
});
|
||||
|
||||
// Notif email organizer — urgent, action required.
|
||||
void notifyReuploadRequest(verificationId, valid, note.trim());
|
||||
|
||||
revalidatePath("/admin/verifications");
|
||||
revalidatePath("/verify");
|
||||
return { success: true as const };
|
||||
@@ -167,6 +213,30 @@ export async function requestReuploadAction(
|
||||
}
|
||||
}
|
||||
|
||||
async function notifyReuploadRequest(
|
||||
verificationId: string,
|
||||
fields: ReuploadField[],
|
||||
note: string
|
||||
) {
|
||||
const verification = await organizerRepo.findById(verificationId);
|
||||
if (!verification) return;
|
||||
const user = await userRepo.findById(verification.userId);
|
||||
if (!user) return;
|
||||
await emailService.send({
|
||||
to: user.email,
|
||||
// Allow re-trigger kalau admin minta lagi setelah submit ulang.
|
||||
idempotencyKey: `kyc_reupload_request-${verificationId}-${verification.submissionCount}`,
|
||||
template: {
|
||||
template: "kyc_reupload_request",
|
||||
data: {
|
||||
userName: user.name,
|
||||
fields,
|
||||
note,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Phase 4: admin verify user tanpa upload KYC (partner trusted referral).
|
||||
* Bikin row APPROVED dengan flag `isManualOverride = true`.
|
||||
|
||||
Reference in New Issue
Block a user