import type { Prisma } from "@prisma/client"; import { CUSTOMERS_PAGE_SIZE } from "@/lib/constants"; import type { CustomerDatePresetValue, CustomerGenderValue, } from "@/lib/constants"; import { trimOrUndefined } from "@/lib/utils"; import { db } from "@/server/db"; import { AppError } from "@/server/errors"; import type { CustomerInput, CustomerListQueryInput, } from "@/server/validation/customer"; export type CustomerListFilters = { search?: string; gender?: CustomerGenderValue; createdPreset: CustomerDatePresetValue; page: number; }; export function normalizeCustomerListFilters( input: CustomerListQueryInput, ): CustomerListFilters { return { search: trimOrUndefined(input.search), gender: input.gender, createdPreset: input.createdPreset, page: input.page, }; } function getCreatedAtRange(preset: CustomerDatePresetValue) { if (preset === "all") { return undefined; } const start = new Date(); start.setHours(0, 0, 0, 0); if (preset === "today") { return { gte: start }; } if (preset === "thisWeek") { const day = start.getDay(); const diff = (day + 6) % 7; start.setDate(start.getDate() - diff); return { gte: start }; } start.setDate(1); return { gte: start }; } function buildCustomerWhere( ownerId: string, filters: CustomerListFilters, ): Prisma.CustomerWhereInput { const clauses: Prisma.CustomerWhereInput[] = [{ ownerId }]; if (filters.search) { clauses.push({ OR: [ { firstName: { contains: filters.search, mode: "insensitive", }, }, { middleName: { contains: filters.search, mode: "insensitive", }, }, { lastName: { contains: filters.search, mode: "insensitive", }, }, { phone: { contains: filters.search, mode: "insensitive", }, }, ], }); } if (filters.gender) { clauses.push({ gender: filters.gender, }); } const createdAt = getCreatedAtRange(filters.createdPreset); if (createdAt) { clauses.push({ createdAt }); } return { AND: clauses, }; } export async function listCustomers(ownerId: string, filters: CustomerListFilters) { const where = buildCustomerWhere(ownerId, filters); const skip = (filters.page - 1) * CUSTOMERS_PAGE_SIZE; const [totalCount, items] = await db.$transaction([ db.customer.count({ where }), db.customer.findMany({ where, orderBy: { updatedAt: "desc", }, skip, take: CUSTOMERS_PAGE_SIZE, }), ]); return { items, totalCount, totalPages: Math.max(1, Math.ceil(totalCount / CUSTOMERS_PAGE_SIZE)), page: filters.page, pageSize: CUSTOMERS_PAGE_SIZE, }; } export async function createCustomer(ownerId: string, input: CustomerInput) { return db.customer.create({ data: { ownerId, firstName: input.firstName.trim(), middleName: trimOrUndefined(input.middleName), lastName: input.lastName.trim(), gender: input.gender, phone: input.phone.trim(), }, }); } export async function getCustomerByIdForOwner(customerId: string, ownerId: string) { return db.customer.findFirst({ where: { id: customerId, ownerId, }, }); } export async function requireCustomerByIdForOwner( customerId: string, ownerId: string, ) { const customer = await getCustomerByIdForOwner(customerId, ownerId); if (!customer) { throw new AppError("العميل ده مش موجود.", 404); } return customer; } export async function updateCustomer( customerId: string, ownerId: string, input: CustomerInput, ) { await requireCustomerByIdForOwner(customerId, ownerId); return db.customer.update({ where: { id: customerId, }, data: { firstName: input.firstName.trim(), middleName: trimOrUndefined(input.middleName), lastName: input.lastName.trim(), gender: input.gender, phone: input.phone.trim(), }, }); }