import { prisma } from "@/lib/prisma"; export type AdminSearchHit = | { type: "user"; id: string; title: string; subtitle: string; href: string; } | { type: "trip"; id: string; title: string; subtitle: string; href: string; } | { type: "booking"; id: string; title: string; subtitle: string; href: string; }; /** * Resolve query admin search ke entity detail page yang paling relevan. * Dispatch berdasarkan pola input: * - email (`@`) → user lookup exact * - `midtrans--` / `manual-` → booking detail * - cuid (~25 char alphanumeric, mulai `cm`) → coba trip → booking → user * - sisanya → fuzzy search trip title/destination + user name */ export const adminSearchService = { async resolve(query: string, limit = 10): Promise { const q = query.trim(); if (q.length < 2) return []; // Email exact if (q.includes("@")) { const user = await prisma.user.findUnique({ where: { email: q.toLowerCase() }, select: { id: true, name: true, email: true }, }); if (user) { return [ { type: "user", id: user.id, title: user.name, subtitle: user.email, href: `/admin/users/${user.id}`, }, ]; } } // Midtrans / manual order_id pattern if (q.startsWith("midtrans-") || q.startsWith("manual-")) { const payment = await prisma.payment.findUnique({ where: { externalOrderId: q }, select: { bookingId: true, booking: { select: { trip: { select: { title: true } }, user: { select: { name: true, email: true } }, }, }, }, }); if (payment) { return [ { type: "booking", id: payment.bookingId, title: `Booking: ${payment.booking.trip.title}`, subtitle: `${payment.booking.user.name} · ${payment.booking.user.email}`, href: `/admin/bookings/${payment.bookingId}`, }, ]; } } // CUID pattern — try trip → booking → user if (/^cm[a-z0-9]{10,}$/i.test(q)) { const [trip, booking, user] = await Promise.all([ prisma.trip.findUnique({ where: { id: q }, select: { id: true, title: true, destination: true }, }), prisma.booking.findUnique({ where: { id: q }, select: { id: true, trip: { select: { title: true } }, user: { select: { name: true, email: true } }, }, }), prisma.user.findUnique({ where: { id: q }, select: { id: true, name: true, email: true }, }), ]); const hits: AdminSearchHit[] = []; if (trip) { hits.push({ type: "trip", id: trip.id, title: trip.title, subtitle: trip.destination, href: `/admin/trips/${trip.id}`, }); } if (booking) { hits.push({ type: "booking", id: booking.id, title: `Booking: ${booking.trip.title}`, subtitle: `${booking.user.name} · ${booking.user.email}`, href: `/admin/bookings/${booking.id}`, }); } if (user) { hits.push({ type: "user", id: user.id, title: user.name, subtitle: user.email, href: `/admin/users/${user.id}`, }); } if (hits.length > 0) return hits; } // Fuzzy: trip + user const [trips, users] = await Promise.all([ prisma.trip.findMany({ where: { OR: [ { title: { contains: q, mode: "insensitive" } }, { destination: { contains: q, mode: "insensitive" } }, ], }, select: { id: true, title: true, destination: true, location: true }, take: limit, orderBy: { date: "desc" }, }), prisma.user.findMany({ where: { OR: [ { name: { contains: q, mode: "insensitive" } }, { email: { contains: q, mode: "insensitive" } }, ], }, select: { id: true, name: true, email: true }, take: limit, orderBy: { createdAt: "desc" }, }), ]); return [ ...users.map((u) => ({ type: "user", id: u.id, title: u.name, subtitle: u.email, href: `/admin/users/${u.id}`, })), ...trips.map((t) => ({ type: "trip", id: t.id, title: t.title, subtitle: `${t.destination} · ${t.location}`, href: `/admin/trips/${t.id}`, })), ].slice(0, limit); }, };