refactor(recipients): move mutations into use cases
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
"use server"
|
||||
|
||||
import { revalidatePath } from "next/cache"
|
||||
|
||||
import {
|
||||
type CreateRecipientFormType,
|
||||
createRecipientSchema,
|
||||
type UpdateRecipientFormType,
|
||||
updateRecipientSchema,
|
||||
} from "@/schemas/recipient.schema"
|
||||
import {
|
||||
createRecipientUseCase,
|
||||
updateRecipientUseCase,
|
||||
} from "@/use-cases/recipient.use-cases"
|
||||
|
||||
export async function createNewRecipient(formData: CreateRecipientFormType) {
|
||||
const validatedFields = createRecipientSchema.safeParse(formData)
|
||||
|
||||
if (!validatedFields.success) {
|
||||
return {
|
||||
success: false,
|
||||
errors: validatedFields.error.flatten().fieldErrors,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await createRecipientUseCase(validatedFields.data)
|
||||
|
||||
if (!result.success) {
|
||||
return result
|
||||
}
|
||||
|
||||
revalidatePath("/recipients")
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Recipient created successfully",
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Database error:", error)
|
||||
return {
|
||||
message: "Failed to create recipient",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateRecipient(formData: UpdateRecipientFormType) {
|
||||
const validatedFields = updateRecipientSchema.safeParse(formData)
|
||||
|
||||
if (!validatedFields.success) {
|
||||
return {
|
||||
success: false,
|
||||
errors: validatedFields.error.flatten().fieldErrors,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await updateRecipientUseCase(validatedFields.data)
|
||||
|
||||
if (!result.success) {
|
||||
return result
|
||||
}
|
||||
|
||||
revalidatePath("/recipients")
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Recipient updated successfully",
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Database error:", error)
|
||||
return {
|
||||
message: "Failed to update recipient",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,19 +4,18 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { SubmitButton } from "@/components/forms/submitButton"
|
||||
import { RecipientDepartment } from "@/generated/prisma/client"
|
||||
import {
|
||||
createNewRecipient,
|
||||
updateRecipient,
|
||||
} from "@/lib/actions/recipient.actions"
|
||||
} from "@/actions/recipient.actions"
|
||||
import { SubmitButton } from "@/components/forms/submitButton"
|
||||
import { RECIPIENT_DEPARTMENTS } from "@/lib/constants"
|
||||
import {
|
||||
type CreateRecipientFormType,
|
||||
recipientSchema,
|
||||
type UpdateRecipientFormType,
|
||||
} from "@/lib/schemas/recipients.schemas"
|
||||
import type { Recipient } from "@/lib/types"
|
||||
} from "@/schemas/recipient.schema"
|
||||
import type { Recipient } from "@/types"
|
||||
|
||||
interface RecipientFormProps {
|
||||
initialData?: Recipient
|
||||
@@ -130,7 +129,7 @@ export default function RecipientForm({
|
||||
className="w-full rounded-lg border px-4 py-2"
|
||||
>
|
||||
<option value="">Select a department</option>
|
||||
{Object.keys(RecipientDepartment).map((department) => (
|
||||
{Object.keys(RECIPIENT_DEPARTMENTS).map((department) => (
|
||||
<option key={department} value={department}>
|
||||
{department}
|
||||
</option>
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
"use server"
|
||||
|
||||
import { revalidatePath } from "next/cache"
|
||||
|
||||
import prisma from "@/lib/prisma"
|
||||
import {
|
||||
type CreateRecipientFormType,
|
||||
createRecipientSchema,
|
||||
type UpdateRecipientFormType,
|
||||
updateRecipientSchema,
|
||||
} from "@/lib/schemas/recipients.schemas"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
|
||||
export async function createNewRecipient(formData: CreateRecipientFormType) {
|
||||
const validatedFields = createRecipientSchema.safeParse(formData)
|
||||
|
||||
if (!validatedFields.success) {
|
||||
return {
|
||||
success: false,
|
||||
errors: validatedFields.error.flatten().fieldErrors,
|
||||
}
|
||||
}
|
||||
|
||||
const { username, firstName, lastName, department, email, phone } =
|
||||
validatedFields.data
|
||||
|
||||
try {
|
||||
const existingRecipientUsername =
|
||||
await RecipientService.findByUsername(username)
|
||||
|
||||
if (existingRecipientUsername) {
|
||||
return {
|
||||
success: false,
|
||||
errors: {
|
||||
username: ["Username already exists"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const existingRecipientEmail = await RecipientService.findByEmail(
|
||||
email || "",
|
||||
)
|
||||
|
||||
if (existingRecipientEmail) {
|
||||
return {
|
||||
success: false,
|
||||
errors: {
|
||||
email: ["Email already exists"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
await RecipientService.create({
|
||||
username: username || (firstName[0] + lastName).toLowerCase(),
|
||||
firstName,
|
||||
lastName,
|
||||
department: department,
|
||||
email: email || null,
|
||||
phone: phone || null,
|
||||
})
|
||||
|
||||
revalidatePath("/recipients")
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Recipient created successfully",
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Database error:", error)
|
||||
return {
|
||||
message: "Failed to create recipient",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateRecipient(formData: UpdateRecipientFormType) {
|
||||
const validatedFields = updateRecipientSchema.safeParse(formData)
|
||||
|
||||
if (!validatedFields.success) {
|
||||
return {
|
||||
success: false,
|
||||
errors: validatedFields.error.flatten().fieldErrors,
|
||||
}
|
||||
}
|
||||
|
||||
const { id, username, firstName, lastName, department, email, phone } =
|
||||
validatedFields.data
|
||||
|
||||
try {
|
||||
const existingRecipient = await RecipientService.findByUsername(username)
|
||||
|
||||
if (existingRecipient && existingRecipient.id !== id) {
|
||||
return {
|
||||
success: false,
|
||||
errors: {
|
||||
username: ["Username already exists"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const existingRecipientEmail = await RecipientService.findByEmail(
|
||||
email || "",
|
||||
)
|
||||
|
||||
if (existingRecipientEmail && existingRecipientEmail.id !== id) {
|
||||
return {
|
||||
success: false,
|
||||
errors: {
|
||||
email: ["Email already exists"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.recipient.update({
|
||||
where: { id },
|
||||
data: {
|
||||
username,
|
||||
firstName,
|
||||
lastName,
|
||||
department: department,
|
||||
email: email || null,
|
||||
phone: phone || null,
|
||||
},
|
||||
})
|
||||
|
||||
revalidatePath("/recipients")
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Recipient updated successfully",
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Database error:", error)
|
||||
return {
|
||||
message: "Failed to update recipient",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,17 @@ export const SIGN_IN_URL = "/login"
|
||||
|
||||
export const TOKEN_EXPIRATION_SECONDS = 60 * 60 * 2 // 2 hour
|
||||
|
||||
export const RECIPIENT_DEPARTMENTS = {
|
||||
IT: "IT",
|
||||
ENGINEERING: "ENGINEERING",
|
||||
LOGISTICS: "LOGISTICS",
|
||||
TRAFFIC: "TRAFFIC",
|
||||
DRIVER: "DRIVER",
|
||||
ADMINISTRATION: "ADMINISTRATION",
|
||||
SALES: "SALES",
|
||||
OTHER: "OTHER",
|
||||
} as const
|
||||
|
||||
export const ITEM_STATUS = {
|
||||
AVAILABLE: "AVAILABLE",
|
||||
ASSIGNED: "ASSIGNED",
|
||||
|
||||
@@ -5,15 +5,15 @@ export const recipientSchema = z.object({
|
||||
username: z
|
||||
.string()
|
||||
.min(1, {
|
||||
error: "Username is required"
|
||||
error: "Username is required",
|
||||
})
|
||||
.nonempty("Username is required"),
|
||||
firstName: z.string().min(1, {
|
||||
error: "First name is required"
|
||||
}),
|
||||
error: "First name is required",
|
||||
}),
|
||||
lastName: z.string().min(1, {
|
||||
error: "Last name is required"
|
||||
}),
|
||||
error: "Last name is required",
|
||||
}),
|
||||
department: z.enum(
|
||||
[
|
||||
"IT",
|
||||
@@ -26,7 +26,7 @@ export const recipientSchema = z.object({
|
||||
"OTHER",
|
||||
],
|
||||
{
|
||||
error: "Department is required"
|
||||
error: "Department is required",
|
||||
},
|
||||
),
|
||||
email: z.string().optional().nullable(),
|
||||
@@ -40,19 +40,39 @@ export const RecipientService = {
|
||||
return prisma.recipient.count()
|
||||
},
|
||||
|
||||
findById: async (id: string): Promise<Recipient | null> => {
|
||||
return prisma.recipient.findUnique({ where: { id } })
|
||||
findById: async (
|
||||
id: string,
|
||||
db: Prisma.TransactionClient | typeof prisma = prisma,
|
||||
): Promise<Recipient | null> => {
|
||||
return db.recipient.findUnique({ where: { id } })
|
||||
},
|
||||
|
||||
findByUsername: async (username: string): Promise<Recipient | null> => {
|
||||
return prisma.recipient.findUnique({ where: { username } })
|
||||
findByUsername: async (
|
||||
username: string,
|
||||
db: Prisma.TransactionClient | typeof prisma = prisma,
|
||||
): Promise<Recipient | null> => {
|
||||
return db.recipient.findUnique({ where: { username } })
|
||||
},
|
||||
|
||||
findByEmail: async (email: string): Promise<Recipient | null> => {
|
||||
return prisma.recipient.findUnique({ where: { email } })
|
||||
findByEmail: async (
|
||||
email: string,
|
||||
db: Prisma.TransactionClient | typeof prisma = prisma,
|
||||
): Promise<Recipient | null> => {
|
||||
return db.recipient.findUnique({ where: { email } })
|
||||
},
|
||||
|
||||
create: async (data: Prisma.RecipientCreateInput): Promise<Recipient> => {
|
||||
return prisma.recipient.create({ data })
|
||||
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 })
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
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