refactor: rename Recipient to Person, remove username, add userId FK

This commit is contained in:
2026-06-16 10:04:24 +02:00
parent befe1f3f82
commit d67f31cf54
27 changed files with 751 additions and 628 deletions
+19 -24
View File
@@ -12,14 +12,14 @@ import { getAuthenticatedUserId } from "@/services/auth.service"
import { CategoryService } from "@/services/category.service"
import { ItemService } from "@/services/item.service"
import { MovementService } from "@/services/movement.service"
import { RecipientService } from "@/services/recipient.service"
import { PersonService } from "@/services/person.service"
import type {
Asset,
Assignment,
Category,
ImportItem,
Item,
Recipient,
Person,
} from "@/types"
export async function importItems(formData: ImportFormType) {
@@ -123,7 +123,7 @@ export async function importItems(formData: ImportFormType) {
file: [
"Only one of category or categoryId is allowed, you must select one of them",
],
},
}
}
}
@@ -153,7 +153,6 @@ export async function importItems(formData: ImportFormType) {
category,
deliveryNote,
assigned,
username,
firstName,
lastName,
} = row
@@ -178,10 +177,6 @@ export async function importItems(formData: ImportFormType) {
importErrors.push(`Row ${index + 2}: Delivery note must be a string`)
}
if (username && typeof username !== "string") {
importErrors.push(`Row ${index + 2}: Username must be a string`)
}
if (firstName && typeof firstName !== "string") {
importErrors.push(`Row ${index + 2}: First name must be a string`)
}
@@ -214,7 +209,6 @@ export async function importItems(formData: ImportFormType) {
category: row.category?.trim() || "",
deliveryNote: row.deliveryNote?.trim() || "",
assigned: row.assigned?.trim() === "true",
username: row.username?.trim() || "",
firstName: row.firstName?.trim() || "",
lastName: row.lastName?.trim() || "",
})
@@ -229,7 +223,6 @@ export async function importItems(formData: ImportFormType) {
category,
deliveryNote,
assigned,
username,
firstName,
lastName,
} = item
@@ -238,7 +231,7 @@ export async function importItems(formData: ImportFormType) {
let newItem: Item | null = null
let newAsset: Asset | null = null
let newCategory: Category | null = null
let newRecipient: Recipient | null = null
let newPerson: Person | null = null
let newAssignment: Assignment | null = null
const existingCategory = categoryId
@@ -290,14 +283,16 @@ export async function importItems(formData: ImportFormType) {
}
if (assigned && firstName && lastName) {
const finalUsername =
username || `${firstName.toLowerCase()[0]}${lastName.toLowerCase()}`
const existingRecipient =
await RecipientService.findByUsername(finalUsername)
const existingPerson = firstName
? await PersonService.findAllPaginated({
search: firstName,
page: 0,
pageSize: 1,
})
: null
if (!existingRecipient) {
newRecipient = await RecipientService.create({
username: finalUsername,
if (!existingPerson || existingPerson.data.length === 0) {
newPerson = await PersonService.create({
firstName,
lastName,
email: undefined,
@@ -305,7 +300,7 @@ export async function importItems(formData: ImportFormType) {
department: "OTHER",
})
} else {
newRecipient = existingRecipient
newPerson = existingPerson.data[0]
}
newAssignment = await AssignmentService.create({
@@ -313,7 +308,7 @@ export async function importItems(formData: ImportFormType) {
notes: deliveryNote || "",
itemId: newItem?.id || "",
assetId: newAsset?.id || "",
recipientId: newRecipient?.id || "",
recipientId: newPerson?.id || "",
assignmentDate: new Date(),
createdBy: userId,
})
@@ -324,15 +319,15 @@ export async function importItems(formData: ImportFormType) {
quantity: stock || 1,
type: assigned ? "ASSIGNMENT" : "IN",
itemId: newItem?.id || undefined,
recipientId: newRecipient?.id || undefined,
recipientId: newPerson?.id || undefined,
}
if (newAssignment?.id) {
movementData.assignmentId = newAssignment.id
}
if (newRecipient?.id) {
movementData.recipientId = newRecipient.id
if (newPerson?.id) {
movementData.recipientId = newPerson.id
}
await MovementService.create({
@@ -347,4 +342,4 @@ export async function importItems(formData: ImportFormType) {
success: true,
message: "Items imported successfully!",
}
}
}
@@ -4,22 +4,22 @@ import { revalidatePath } from "next/cache"
import { flattenError } from "zod"
import { getI18n } from "@/i18n/server"
import {
buildCreateRecipientSchema,
buildUpdateRecipientSchema,
type CreateRecipientFormType,
type UpdateRecipientFormType,
} from "@/schemas/recipient.schema"
buildCreatePersonSchema,
buildUpdatePersonSchema,
type CreatePersonFormType,
type UpdatePersonFormType,
} from "@/schemas/person.schema"
import {
createRecipientUseCase,
updateRecipientUseCase,
} from "@/use-cases/recipient.use-cases"
createPersonUseCase,
updatePersonUseCase,
} from "@/use-cases/person.use-cases"
import { localizeRecipientFieldErrors } from "./recipient.messages"
import { localizePersonFieldErrors } from "./person.messages"
export async function createNewRecipient(formData: CreateRecipientFormType) {
export async function createNewPerson(formData: CreatePersonFormType) {
const { dictionary } = await getI18n()
const copy = dictionary.inventory.recipients
const validatedFields = buildCreateRecipientSchema(copy.schema).safeParse(
const copy = dictionary.inventory.people
const validatedFields = buildCreatePersonSchema(copy.schema).safeParse(
formData,
)
@@ -31,17 +31,17 @@ export async function createNewRecipient(formData: CreateRecipientFormType) {
}
try {
const result = await createRecipientUseCase(validatedFields.data)
const result = await createPersonUseCase(validatedFields.data)
if (!result.success) {
return {
...result,
errors: localizeRecipientFieldErrors(result.errors, copy.actions),
errors: localizePersonFieldErrors(result.errors, copy.actions),
message: copy.actions.createFailure,
}
}
revalidatePath("/recipients")
revalidatePath("/people")
return {
success: true,
@@ -56,10 +56,10 @@ export async function createNewRecipient(formData: CreateRecipientFormType) {
}
}
export async function updateRecipient(formData: UpdateRecipientFormType) {
export async function updatePerson(formData: UpdatePersonFormType) {
const { dictionary } = await getI18n()
const copy = dictionary.inventory.recipients
const validatedFields = buildUpdateRecipientSchema(copy.schema).safeParse(
const copy = dictionary.inventory.people
const validatedFields = buildUpdatePersonSchema(copy.schema).safeParse(
formData,
)
@@ -71,17 +71,17 @@ export async function updateRecipient(formData: UpdateRecipientFormType) {
}
try {
const result = await updateRecipientUseCase(validatedFields.data)
const result = await updatePersonUseCase(validatedFields.data)
if (!result.success) {
return {
...result,
errors: localizeRecipientFieldErrors(result.errors, copy.actions),
errors: localizePersonFieldErrors(result.errors, copy.actions),
message: copy.actions.updateFailure,
}
}
revalidatePath("/recipients")
revalidatePath("/people")
return {
success: true,
@@ -94,4 +94,4 @@ export async function updateRecipient(formData: UpdateRecipientFormType) {
message: copy.actions.updateFailure,
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
import type { Dictionary } from "@/i18n/dictionaries"
type PersonActionCopy = Dictionary["inventory"]["people"]["actions"]
type FieldErrors = Record<string, string[]>
const personErrorMessageKeys = {
"Email already exists": "duplicateEmail",
} as const satisfies Record<string, keyof PersonActionCopy>
function isPersonErrorMessage(
message: string,
): message is keyof typeof personErrorMessageKeys {
return message in personErrorMessageKeys
}
function localizePersonMessage(
message: string,
copy: PersonActionCopy,
): string {
if (!isPersonErrorMessage(message)) return message
return copy[personErrorMessageKeys[message]]
}
export function localizePersonFieldErrors(
errors: FieldErrors | undefined,
copy: PersonActionCopy,
): FieldErrors | undefined {
if (!errors) return undefined
return Object.fromEntries(
Object.entries(errors).map(([field, messages]) => [
field,
messages.map((message) => localizePersonMessage(message, copy)),
]),
)
}
-39
View File
@@ -1,39 +0,0 @@
import type { Dictionary } from "@/i18n/dictionaries"
type RecipientActionCopy = Dictionary["inventory"]["recipients"]["actions"]
type FieldErrors = Record<string, string[]>
const recipientErrorMessageKeys = {
"Username already exists": "duplicateUsername",
"Email already exists": "duplicateEmail",
} as const satisfies Record<string, keyof RecipientActionCopy>
function isRecipientErrorMessage(
message: string,
): message is keyof typeof recipientErrorMessageKeys {
return message in recipientErrorMessageKeys
}
function localizeRecipientMessage(
message: string,
copy: RecipientActionCopy,
): string {
if (!isRecipientErrorMessage(message)) return message
return copy[recipientErrorMessageKeys[message]]
}
export function localizeRecipientFieldErrors(
errors: FieldErrors | undefined,
copy: RecipientActionCopy,
): FieldErrors | undefined {
if (!errors) return undefined
return Object.fromEntries(
Object.entries(errors).map(([field, messages]) => [
field,
messages.map((message) => localizeRecipientMessage(message, copy)),
]),
)
}
+75 -1
View File
@@ -318,6 +318,80 @@ export const en = {
idRequired: "Assignment ID is required",
},
},
people: {
list: {
title: "People",
addLabel: "Add Person",
empty: "No people found.",
columns: {
name: "Name",
email: "Email",
phone: "Phone",
department: "Department",
actions: "Actions",
},
actions: {
view: "View person",
edit: "Edit person",
},
},
detail: {
notFound: "Person not found",
labels: {
email: "Email",
phone: "Phone",
department: "Department",
},
},
new: {
title: "Add Person",
},
edit: {
title: "Edit Person",
notFound: "Person not found",
},
form: {
firstNameLabel: "First Name",
firstNamePlaceholder: "First name",
lastNameLabel: "Last Name",
lastNamePlaceholder: "Last name",
departmentLabel: "Department",
departmentPlaceholder: "Select a department",
emailLabel: "Email",
emailPlaceholder: "Email",
phoneLabel: "Phone",
phonePlaceholder: "Phone",
createSubmit: "Create Person",
updateSubmit: "Update Person",
},
fallback: {
unknownDepartment: "Unknown department",
},
departments: {
IT: "IT",
ENGINEERING: "Engineering",
LOGISTICS: "Logistics",
TRAFFIC: "Traffic",
DRIVER: "Driver",
ADMINISTRATION: "Administration",
SALES: "Sales",
OTHER: "Other",
},
actions: {
createSuccess: "Person created successfully",
createFailure: "Failed to create person",
updateSuccess: "Person updated successfully",
updateFailure: "Failed to update person",
duplicateEmail: "Email already exists",
},
schema: {
firstNameRequired: "First name is required",
lastNameRequired: "Last name is required",
departmentRequired: "Department is required",
emailInvalid: "Email format is invalid",
idRequired: "ID is required",
},
},
recipients: {
list: {
title: "Recipients",
@@ -540,4 +614,4 @@ export const en = {
},
}
export type Dictionary = typeof en
export type Dictionary = typeof en
+75 -1
View File
@@ -323,6 +323,80 @@ export const es = {
idRequired: "El ID de asignación es obligatorio",
},
},
people: {
list: {
title: "Personas",
addLabel: "Agregar persona",
empty: "No se encontraron personas.",
columns: {
name: "Nombre",
email: "Correo electrónico",
phone: "Teléfono",
department: "Departamento",
actions: "Acciones",
},
actions: {
view: "Ver persona",
edit: "Editar persona",
},
},
detail: {
notFound: "Persona no encontrada",
labels: {
email: "Correo electrónico",
phone: "Teléfono",
department: "Departamento",
},
},
new: {
title: "Agregar persona",
},
edit: {
title: "Editar persona",
notFound: "Persona no encontrada",
},
form: {
firstNameLabel: "Nombre",
firstNamePlaceholder: "Nombre",
lastNameLabel: "Apellido",
lastNamePlaceholder: "Apellido",
departmentLabel: "Departamento",
departmentPlaceholder: "Selecciona un departamento",
emailLabel: "Correo electrónico",
emailPlaceholder: "Correo electrónico",
phoneLabel: "Teléfono",
phonePlaceholder: "Teléfono",
createSubmit: "Crear persona",
updateSubmit: "Actualizar persona",
},
fallback: {
unknownDepartment: "Departamento desconocido",
},
departments: {
IT: "IT",
ENGINEERING: "Ingeniería",
LOGISTICS: "Logística",
TRAFFIC: "Tráfico",
DRIVER: "Chofer",
ADMINISTRATION: "Administración",
SALES: "Ventas",
OTHER: "Otro",
},
actions: {
createSuccess: "Persona creada correctamente",
createFailure: "Error al crear la persona",
updateSuccess: "Persona actualizada correctamente",
updateFailure: "Error al actualizar la persona",
duplicateEmail: "El correo electrónico ya existe",
},
schema: {
firstNameRequired: "El nombre es obligatorio",
lastNameRequired: "El apellido es obligatorio",
departmentRequired: "El departamento es obligatorio",
emailInvalid: "El correo electrónico no es válido",
idRequired: "El ID es obligatorio",
},
},
recipients: {
list: {
title: "Destinatarios",
@@ -543,4 +617,4 @@ export const es = {
},
},
},
} satisfies Dictionary
} satisfies Dictionary
+1 -1
View File
@@ -8,7 +8,7 @@ export const SIGN_IN_URL = "/login"
export const TOKEN_EXPIRATION_SECONDS = 60 * 60 * 2 // 2 hour
export const RECIPIENT_DEPARTMENTS = {
export const PERSON_DEPARTMENTS = {
IT: "IT",
ENGINEERING: "ENGINEERING",
LOGISTICS: "LOGISTICS",
+74
View File
@@ -0,0 +1,74 @@
import { z } from "zod"
import type { Dictionary } from "@/i18n/dictionaries"
export type PersonSchemaCopy = Dictionary["inventory"]["people"]["schema"]
const defaultPersonSchemaCopy: PersonSchemaCopy = {
firstNameRequired: "First name is required",
lastNameRequired: "Last name is required",
departmentRequired: "Department is required",
emailInvalid: "Email format is invalid",
idRequired: "ID is required",
}
const personDepartments = [
"IT",
"ENGINEERING",
"TRAFFIC",
"DRIVER",
"LOGISTICS",
"ADMINISTRATION",
"SALES",
"OTHER",
] as const
function buildPersonBaseSchema(copy: PersonSchemaCopy) {
return z.object({
id: z.string().optional(),
firstName: z.string().min(1, {
error: copy.firstNameRequired,
}),
lastName: z.string().min(1, {
error: copy.lastNameRequired,
}),
department: z.enum(personDepartments, {
error: copy.departmentRequired,
}),
email: z.string().optional().nullable(),
phone: z.string().optional().nullable(),
userId: z.string().uuid().optional().nullable(),
})
}
export const personSchema = buildPersonBaseSchema(defaultPersonSchemaCopy)
export function buildCreatePersonSchema(copy: PersonSchemaCopy) {
return buildPersonBaseSchema(copy).superRefine((data, ctx) => {
if (data.email && !z.string().email().safeParse(data.email).success) {
ctx.addIssue({
code: "custom",
message: copy.emailInvalid,
path: ["email"],
})
}
})
}
export const createPersonSchema = buildCreatePersonSchema(
defaultPersonSchemaCopy,
)
export type CreatePersonFormType = z.infer<typeof createPersonSchema>
export function buildUpdatePersonSchema(copy: PersonSchemaCopy) {
return buildPersonBaseSchema(copy).extend({
id: z.string().nonempty(copy.idRequired),
})
}
export const updatePersonSchema = buildUpdatePersonSchema(
defaultPersonSchemaCopy,
)
export type UpdatePersonFormType = z.infer<typeof updatePersonSchema>
-80
View File
@@ -1,80 +0,0 @@
import { z } from "zod"
import type { Dictionary } from "@/i18n/dictionaries"
export type RecipientSchemaCopy =
Dictionary["inventory"]["recipients"]["schema"]
const defaultRecipientSchemaCopy: RecipientSchemaCopy = {
usernameRequired: "Username is required",
firstNameRequired: "First name is required",
lastNameRequired: "Last name is required",
departmentRequired: "Department is required",
emailInvalid: "Email format is invalid",
idRequired: "ID is required",
}
const recipientDepartments = [
"IT",
"ENGINEERING",
"TRAFFIC",
"DRIVER",
"LOGISTICS",
"ADMINISTRATION",
"SALES",
"OTHER",
] as const
function buildRecipientBaseSchema(copy: RecipientSchemaCopy) {
return z.object({
id: z.string().optional(),
username: z.string().min(1, {
error: copy.usernameRequired,
}),
firstName: z.string().min(1, {
error: copy.firstNameRequired,
}),
lastName: z.string().min(1, {
error: copy.lastNameRequired,
}),
department: z.enum(recipientDepartments, {
error: copy.departmentRequired,
}),
email: z.string().optional().nullable(),
phone: z.string().optional().nullable(),
})
}
export const recipientSchema = buildRecipientBaseSchema(
defaultRecipientSchemaCopy,
)
export function buildCreateRecipientSchema(copy: RecipientSchemaCopy) {
return buildRecipientBaseSchema(copy).superRefine((data, ctx) => {
if (data.email && !z.string().email().safeParse(data.email).success) {
ctx.addIssue({
code: "custom",
message: copy.emailInvalid,
path: ["email"],
})
}
})
}
export const createRecipientSchema = buildCreateRecipientSchema(
defaultRecipientSchemaCopy,
)
export type CreateRecipientFormType = z.infer<typeof createRecipientSchema>
export function buildUpdateRecipientSchema(copy: RecipientSchemaCopy) {
return buildRecipientBaseSchema(copy).extend({
id: z.string().nonempty(copy.idRequired),
})
}
export const updateRecipientSchema = buildUpdateRecipientSchema(
defaultRecipientSchemaCopy,
)
export type UpdateRecipientFormType = z.infer<typeof updateRecipientSchema>
+2 -2
View File
@@ -81,7 +81,7 @@ export const AssignmentService = {
},
})
},
findAllByRecipient: async (
findAllByPerson: async (
recipientId: string,
): Promise<AssignmentWithRecipientItemAsset[]> => {
return prisma.assignment.findMany({
@@ -143,4 +143,4 @@ export const AssignmentService = {
data,
})
},
}
}
+71
View File
@@ -0,0 +1,71 @@
import type { Prisma, Person } from "@/generated/prisma/client"
import { paginate } from "@/lib/paginate"
import prisma from "@/lib/prisma"
export const PersonService = {
findAll: async (): Promise<Person[]> => {
return prisma.person.findMany({
orderBy: {
firstName: "asc",
},
})
},
findAllPaginated: async ({
page = 0,
pageSize,
search,
}: {
page?: number
pageSize?: number
search?: string
}) => {
return paginate<Person>({
model: prisma.person,
page,
pageSize,
where: {
...(search
? {
OR: [
{ email: { contains: search, mode: "insensitive" } },
{ firstName: { contains: search, mode: "insensitive" } },
{ lastName: { contains: search, mode: "insensitive" } },
],
}
: {}),
},
})
},
findAllPeopleCount: async (): Promise<number> => {
return prisma.person.count()
},
findById: async (
id: string,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Person | null> => {
return db.person.findUnique({ where: { id } })
},
findByEmail: async (
email: string,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Person | null> => {
return db.person.findUnique({ where: { email } })
},
create: async (
data: Prisma.PersonCreateInput,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Person> => {
return db.person.create({ data })
},
update: async (
id: string,
data: Prisma.PersonUpdateInput,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Person> => {
return db.person.update({ where: { id }, data })
},
}
-78
View File
@@ -1,78 +0,0 @@
import type { Prisma, Recipient } from "@/generated/prisma/client"
import { paginate } from "@/lib/paginate"
import prisma from "@/lib/prisma"
export const RecipientService = {
findAll: async (): Promise<Recipient[]> => {
return prisma.recipient.findMany({
orderBy: {
firstName: "asc",
},
})
},
findAllPaginated: async ({
page = 0,
pageSize,
search,
}: {
page?: number
pageSize?: number
search?: string
}) => {
return paginate<Recipient>({
model: prisma.recipient,
page,
pageSize,
where: {
...(search
? {
OR: [
{ username: { contains: search, mode: "insensitive" } },
{ firstName: { contains: search, mode: "insensitive" } },
{ lastName: { contains: search, mode: "insensitive" } },
],
}
: {}),
},
})
},
findAllRecipientsCount: async (): Promise<number> => {
return prisma.recipient.count()
},
findById: async (
id: string,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Recipient | null> => {
return db.recipient.findUnique({ where: { id } })
},
findByUsername: async (
username: string,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Recipient | null> => {
return db.recipient.findUnique({ where: { username } })
},
findByEmail: async (
email: string,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Recipient | null> => {
return db.recipient.findUnique({ where: { email } })
},
create: async (
data: Prisma.RecipientCreateInput,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Recipient> => {
return db.recipient.create({ data })
},
update: async (
id: string,
data: Prisma.RecipientUpdateInput,
db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Recipient> => {
return db.recipient.update({ where: { id }, data })
},
}
+3 -3
View File
@@ -2,7 +2,7 @@ import type { Assignment as PrismaAssignment } from "@/generated/prisma/client"
import type { Asset } from "./asset"
import type { Item } from "./item"
import type { Recipient } from "./recipient"
import type { Person } from "./person"
export type Assignment = PrismaAssignment
@@ -10,7 +10,7 @@ export type AssignmentSummary = Pick<Assignment, "id" | "quantity">
export type AssignmentWithRecipientItemAsset = Assignment & {
returnDate: Date | null
recipient: Recipient | null
recipient: Person | null
item: Item | null
asset: Asset | null
}
}
-1
View File
@@ -6,7 +6,6 @@ export interface ImportItem {
category?: string
deliveryNote?: string
assigned?: boolean
username?: string
firstName?: string
lastName?: string
}
+1 -1
View File
@@ -5,5 +5,5 @@ export * from "./import"
export * from "./item"
export * from "./movement"
export * from "./paginate"
export * from "./recipient"
export * from "./person"
export * from "./user"
+3
View File
@@ -0,0 +1,3 @@
import type { Person as PrismaPerson } from "@/generated/prisma/client"
export type Person = PrismaPerson
-3
View File
@@ -1,3 +0,0 @@
import type { Recipient as PrismaRecipient } from "@/generated/prisma/client"
export type Recipient = PrismaRecipient
+127
View File
@@ -0,0 +1,127 @@
import { Prisma } from "@/generated/prisma/client"
import prisma from "@/lib/prisma"
import type {
CreatePersonFormType,
UpdatePersonFormType,
} from "@/schemas/person.schema"
import { PersonService } from "@/services/person.service"
type FieldErrors = Record<string, string[]>
type PersonUseCaseResult =
| {
success: true
}
| {
success: false
errors: FieldErrors
}
function personError(errors: FieldErrors): PersonUseCaseResult {
return {
success: false,
errors,
}
}
function uniqueErrorFor(error: unknown): FieldErrors | null {
if (
!(error instanceof Prisma.PrismaClientKnownRequestError) ||
error.code !== "P2002"
) {
return null
}
const target = Array.isArray(error.meta?.target) ? error.meta.target : []
if (target.includes("email")) {
return { email: ["Email already exists"] }
}
return { email: ["Email already exists"] }
}
export async function createPersonUseCase(
input: CreatePersonFormType,
): Promise<PersonUseCaseResult> {
const { firstName, lastName, department, email, phone, userId } = input
try {
return await prisma.$transaction(async (tx) => {
if (email) {
const existingPersonEmail = await PersonService.findByEmail(email, tx)
if (existingPersonEmail) {
return personError({ email: ["Email already exists"] })
}
}
await PersonService.create(
{
firstName,
lastName,
department,
email: email || null,
phone: phone || null,
...(userId ? { user: { connect: { id: userId } } } : {}),
},
tx,
)
return {
success: true,
}
})
} catch (error) {
const errors = uniqueErrorFor(error)
if (errors) {
return personError(errors)
}
throw error
}
}
export async function updatePersonUseCase(
input: UpdatePersonFormType,
): Promise<PersonUseCaseResult> {
const { id, firstName, lastName, department, email, phone, userId } = input
try {
return await prisma.$transaction(async (tx) => {
if (email) {
const existingPersonEmail = await PersonService.findByEmail(email, tx)
if (existingPersonEmail && existingPersonEmail.id !== id) {
return personError({ email: ["Email already exists"] })
}
}
await PersonService.update(
id,
{
firstName,
lastName,
department,
email: email || null,
phone: phone || null,
...(userId ? { user: { connect: { id: userId } } } : { userId: null }),
},
tx,
)
return {
success: true,
}
})
} catch (error) {
const errors = uniqueErrorFor(error)
if (errors) {
return personError(errors)
}
throw error
}
}
-155
View File
@@ -1,155 +0,0 @@
import { Prisma } from "@/generated/prisma/client"
import prisma from "@/lib/prisma"
import type {
CreateRecipientFormType,
UpdateRecipientFormType,
} from "@/schemas/recipient.schema"
import { RecipientService } from "@/services/recipient.service"
type FieldErrors = Record<string, string[]>
type RecipientUseCaseResult =
| {
success: true
}
| {
success: false
errors: FieldErrors
}
function recipientError(errors: FieldErrors): RecipientUseCaseResult {
return {
success: false,
errors,
}
}
function uniqueErrorFor(error: unknown): FieldErrors | null {
if (
!(error instanceof Prisma.PrismaClientKnownRequestError) ||
error.code !== "P2002"
) {
return null
}
const target = Array.isArray(error.meta?.target) ? error.meta.target : []
if (target.includes("username")) {
return { username: ["Username already exists"] }
}
if (target.includes("email")) {
return { email: ["Email already exists"] }
}
return { username: ["Username already exists"] }
}
export async function createRecipientUseCase(
input: CreateRecipientFormType,
): Promise<RecipientUseCaseResult> {
const { username, firstName, lastName, department, email, phone } = input
try {
return await prisma.$transaction(async (tx) => {
const existingRecipientUsername = await RecipientService.findByUsername(
username,
tx,
)
if (existingRecipientUsername) {
return recipientError({ username: ["Username already exists"] })
}
if (email) {
const existingRecipientEmail = await RecipientService.findByEmail(
email,
tx,
)
if (existingRecipientEmail) {
return recipientError({ email: ["Email already exists"] })
}
}
await RecipientService.create(
{
username: username || (firstName[0] + lastName).toLowerCase(),
firstName,
lastName,
department,
email: email || null,
phone: phone || null,
},
tx,
)
return {
success: true,
}
})
} catch (error) {
const errors = uniqueErrorFor(error)
if (errors) {
return recipientError(errors)
}
throw error
}
}
export async function updateRecipientUseCase(
input: UpdateRecipientFormType,
): Promise<RecipientUseCaseResult> {
const { id, username, firstName, lastName, department, email, phone } = input
try {
return await prisma.$transaction(async (tx) => {
const existingRecipient = await RecipientService.findByUsername(
username,
tx,
)
if (existingRecipient && existingRecipient.id !== id) {
return recipientError({ username: ["Username already exists"] })
}
if (email) {
const existingRecipientEmail = await RecipientService.findByEmail(
email,
tx,
)
if (existingRecipientEmail && existingRecipientEmail.id !== id) {
return recipientError({ email: ["Email already exists"] })
}
}
await RecipientService.update(
id,
{
username,
firstName,
lastName,
department,
email: email || null,
phone: phone || null,
},
tx,
)
return {
success: true,
}
})
} catch (error) {
const errors = uniqueErrorFor(error)
if (errors) {
return recipientError(errors)
}
throw error
}
}