178 lines
5.1 KiB
TypeScript
178 lines
5.1 KiB
TypeScript
import { prisma } from "@/lib/prisma";
|
|
import { Prisma } from "@/app/generated/prisma/client";
|
|
|
|
export const organizerRepo = {
|
|
async findByUserId(userId: string) {
|
|
return prisma.organizerVerification.findUnique({ where: { userId } });
|
|
},
|
|
|
|
async findById(id: string) {
|
|
return prisma.organizerVerification.findUnique({ where: { id } });
|
|
},
|
|
|
|
async findByNikHash(nikHash: string) {
|
|
return prisma.organizerVerification.findUnique({ where: { nikHash } });
|
|
},
|
|
|
|
async upsertForUser(
|
|
userId: string,
|
|
data: Omit<Prisma.OrganizerVerificationUncheckedCreateInput, "id" | "userId" | "createdAt" | "updatedAt">
|
|
) {
|
|
return prisma.organizerVerification.upsert({
|
|
where: { userId },
|
|
create: { userId, ...data },
|
|
update: data,
|
|
});
|
|
},
|
|
|
|
async listByStatus(
|
|
status?: "PENDING" | "APPROVED" | "REJECTED",
|
|
filters?: {
|
|
dateFrom?: Date;
|
|
dateTo?: Date;
|
|
reviewerEmail?: string;
|
|
}
|
|
) {
|
|
const where: Prisma.OrganizerVerificationWhereInput = {};
|
|
if (status) where.status = status;
|
|
if (filters?.dateFrom || filters?.dateTo) {
|
|
where.createdAt = {
|
|
...(filters.dateFrom && { gte: filters.dateFrom }),
|
|
...(filters.dateTo && { lte: filters.dateTo }),
|
|
};
|
|
}
|
|
if (filters?.reviewerEmail) {
|
|
where.reviewedBy = { email: filters.reviewerEmail };
|
|
}
|
|
return prisma.organizerVerification.findMany({
|
|
where,
|
|
orderBy: { createdAt: "desc" },
|
|
include: {
|
|
user: { select: { id: true, name: true, email: true } },
|
|
reviewedBy: { select: { id: true, name: true, email: true } },
|
|
},
|
|
});
|
|
},
|
|
|
|
async countByStatus(status: "PENDING" | "APPROVED" | "REJECTED") {
|
|
return prisma.organizerVerification.count({ where: { status } });
|
|
},
|
|
|
|
/** Verifikasi terbaru (default PENDING) untuk preview di dashboard admin. */
|
|
async listRecent(status: "PENDING" | "APPROVED" | "REJECTED", limit = 3) {
|
|
return prisma.organizerVerification.findMany({
|
|
where: { status },
|
|
orderBy: { createdAt: "desc" },
|
|
take: limit,
|
|
select: {
|
|
id: true,
|
|
fullName: true,
|
|
createdAt: true,
|
|
user: { select: { id: true, name: true, email: true } },
|
|
},
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Phase 2: minta organizer upload ulang field tertentu. Reset status ke
|
|
* PENDING tapi sengaja TIDAK clear data lama (organizer ganti field saat
|
|
* submit ulang via /verify). Service `submitVerification` auto-clear flag.
|
|
*/
|
|
async requestReupload(
|
|
id: string,
|
|
data: { fields: string[]; note: string }
|
|
) {
|
|
return prisma.organizerVerification.update({
|
|
where: { id },
|
|
data: {
|
|
status: "PENDING",
|
|
reuploadRequested: true,
|
|
reuploadFields: data.fields,
|
|
reuploadNote: data.note,
|
|
// Clear review state supaya muncul lagi di tab PENDING.
|
|
reviewedById: null,
|
|
reviewedAt: null,
|
|
},
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Phase 4: bikin verifikasi APPROVED tanpa upload KYC (manual override admin).
|
|
* Placeholder NIK & no image keys — `isManualOverride = true` jadi marker.
|
|
*/
|
|
async createManualOverride(input: {
|
|
userId: string;
|
|
adminId: string;
|
|
note: string;
|
|
fullName: string;
|
|
nikEncrypted: string;
|
|
nikHash: string;
|
|
bankName: string;
|
|
bankAccountNumber: string;
|
|
bankAccountName: string;
|
|
verifiedAt: Date;
|
|
}) {
|
|
return prisma.organizerVerification.create({
|
|
data: {
|
|
userId: input.userId,
|
|
fullName: input.fullName,
|
|
nikEncrypted: input.nikEncrypted,
|
|
nikHash: input.nikHash,
|
|
birthDate: new Date("1970-01-01"),
|
|
address: "(manual override — tidak diisi)",
|
|
ktpImageKey: "(manual override)",
|
|
livenessKey: "(manual override)",
|
|
bankName: input.bankName,
|
|
bankAccountNumber: input.bankAccountNumber,
|
|
bankAccountName: input.bankAccountName,
|
|
status: "APPROVED",
|
|
verifiedAt: input.verifiedAt,
|
|
reviewedById: input.adminId,
|
|
reviewedAt: input.verifiedAt,
|
|
isManualOverride: true,
|
|
manualOverrideById: input.adminId,
|
|
manualOverrideNote: input.note,
|
|
},
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Reopen pengajuan REJECTED ke PENDING. Simpan rejection reason lama
|
|
* sebagai catatan history (di-overwrite kalau di-reject lagi nanti).
|
|
*/
|
|
async reopen(id: string, reopenNote: string) {
|
|
return prisma.organizerVerification.update({
|
|
where: { id },
|
|
data: {
|
|
status: "PENDING",
|
|
reviewedById: null,
|
|
reviewedAt: null,
|
|
verifiedAt: null,
|
|
// Pertahankan rejectionReason lama di field, append note reopen.
|
|
rejectionReason: `[Dibuka kembali admin: ${reopenNote}]`,
|
|
},
|
|
});
|
|
},
|
|
|
|
async updateReview(
|
|
id: string,
|
|
data: {
|
|
status: "APPROVED" | "REJECTED";
|
|
rejectionReason?: string | null;
|
|
reviewedById: string;
|
|
}
|
|
) {
|
|
const now = new Date();
|
|
return prisma.organizerVerification.update({
|
|
where: { id },
|
|
data: {
|
|
status: data.status,
|
|
rejectionReason: data.rejectionReason ?? null,
|
|
reviewedById: data.reviewedById,
|
|
reviewedAt: now,
|
|
verifiedAt: data.status === "APPROVED" ? now : null,
|
|
},
|
|
});
|
|
},
|
|
};
|