refactor: rename Recipient to Person, remove username, add userId FK
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
]),
|
||||
)
|
||||
}
|
||||
@@ -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)),
|
||||
]),
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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 })
|
||||
},
|
||||
}
|
||||
@@ -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 })
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ export interface ImportItem {
|
||||
category?: string
|
||||
deliveryNote?: string
|
||||
assigned?: boolean
|
||||
username?: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
}
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { Person as PrismaPerson } from "@/generated/prisma/client"
|
||||
|
||||
export type Person = PrismaPerson
|
||||
@@ -1,3 +0,0 @@
|
||||
import type { Recipient as PrismaRecipient } from "@/generated/prisma/client"
|
||||
|
||||
export type Recipient = PrismaRecipient
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user