130 lines
3.4 KiB
TypeScript
130 lines
3.4 KiB
TypeScript
import { prisma } from "@/lib/prisma";
|
|
import type { ParticipantStatus } from "@/app/generated/prisma/client";
|
|
|
|
export const participantRepo = {
|
|
async findByTripAndUser(tripId: string, userId: string) {
|
|
return prisma.tripParticipant.findUnique({
|
|
where: { tripId_userId: { tripId, userId } },
|
|
});
|
|
},
|
|
|
|
async findById(id: string) {
|
|
return prisma.tripParticipant.findUnique({ where: { id } });
|
|
},
|
|
|
|
async create(tripId: string, userId: string) {
|
|
return prisma.tripParticipant.create({
|
|
data: { tripId, userId, status: "PENDING" },
|
|
});
|
|
},
|
|
|
|
async setStatus(id: string, status: ParticipantStatus) {
|
|
return prisma.tripParticipant.update({
|
|
where: { id },
|
|
data: { status },
|
|
});
|
|
},
|
|
|
|
async setStatusAndClearPayment(id: string, status: ParticipantStatus) {
|
|
return prisma.tripParticipant.update({
|
|
where: { id },
|
|
data: {
|
|
status,
|
|
markedPaidAt: null,
|
|
paymentConfirmedAt: null,
|
|
},
|
|
});
|
|
},
|
|
|
|
async countByTrip(tripId: string) {
|
|
return prisma.tripParticipant.count({
|
|
where: { tripId, status: { not: "CANCELLED" } },
|
|
});
|
|
},
|
|
|
|
async cancel(tripId: string, userId: string) {
|
|
return prisma.tripParticipant.update({
|
|
where: { tripId_userId: { tripId, userId } },
|
|
data: {
|
|
status: "CANCELLED",
|
|
markedPaidAt: null,
|
|
paymentConfirmedAt: null,
|
|
},
|
|
});
|
|
},
|
|
|
|
async reactivate(tripId: string, userId: string) {
|
|
return prisma.tripParticipant.update({
|
|
where: { tripId_userId: { tripId, userId } },
|
|
data: {
|
|
status: "PENDING",
|
|
markedPaidAt: null,
|
|
paymentConfirmedAt: null,
|
|
},
|
|
});
|
|
},
|
|
|
|
async markPaidByUser(tripId: string, userId: string) {
|
|
return prisma.tripParticipant.update({
|
|
where: { tripId_userId: { tripId, userId } },
|
|
data: { markedPaidAt: new Date() },
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Satu baris update atomik — aman dari double-submit / race saat tandai bayar.
|
|
*/
|
|
async tryMarkPaidByUser(tripId: string, userId: string) {
|
|
return prisma.tripParticipant.updateMany({
|
|
where: {
|
|
tripId,
|
|
userId,
|
|
status: { not: "CANCELLED" },
|
|
markedPaidAt: null,
|
|
paymentConfirmedAt: null,
|
|
},
|
|
data: { markedPaidAt: new Date() },
|
|
});
|
|
},
|
|
|
|
async confirmPaymentByOrganizer(participantId: string) {
|
|
return prisma.tripParticipant.update({
|
|
where: { id: participantId },
|
|
data: { paymentConfirmedAt: new Date() },
|
|
});
|
|
},
|
|
|
|
/** Konfirmasi pembayaran hanya jika masih eligible — idempotent terhadap double klik. */
|
|
async tryConfirmPaymentByOrganizer(participantId: string) {
|
|
return prisma.tripParticipant.updateMany({
|
|
where: {
|
|
id: participantId,
|
|
markedPaidAt: { not: null },
|
|
paymentConfirmedAt: null,
|
|
status: { not: "CANCELLED" },
|
|
},
|
|
data: { paymentConfirmedAt: new Date() },
|
|
});
|
|
},
|
|
|
|
/** Partisipasi user beserta trip (untuk profil & riwayat). */
|
|
async findWithTripForProfile(userId: string) {
|
|
return prisma.tripParticipant.findMany({
|
|
where: { userId },
|
|
include: {
|
|
trip: {
|
|
include: {
|
|
organizer: { select: { id: true, name: true } },
|
|
images: { orderBy: { order: "asc" }, take: 1 },
|
|
reviews: {
|
|
where: { userId },
|
|
select: { id: true, rating: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
},
|
|
};
|