refactor: rename recipients route to people, update all frontend references
This commit is contained in:
@@ -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",
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,4 +342,4 @@ export async function importItems(formData: ImportFormType) {
|
||||
success: true,
|
||||
message: "Items imported successfully!",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,4 +94,4 @@ export async function updatePerson(formData: UpdatePersonFormType) {
|
||||
message: copy.actions.updateFailure,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,4 @@ export function localizePersonFieldErrors(
|
||||
messages.map((message) => localizePersonMessage(message, copy)),
|
||||
]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { AssetService } from "@/services/asset.service"
|
||||
import { ItemService } from "@/services/item.service"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import Card from "./_components/card"
|
||||
|
||||
@@ -10,7 +10,7 @@ export default async function Home() {
|
||||
const copy = dictionary.dashboardHome
|
||||
const totalItems = await ItemService.findAllItemsCount()
|
||||
const totalAssets = await AssetService.findAllAssetsCount()
|
||||
const totalRecipients = await RecipientService.findAllRecipientsCount()
|
||||
const totalPeople = await PersonService.findAllPeopleCount()
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
@@ -65,10 +65,10 @@ export default async function Home() {
|
||||
}
|
||||
/>
|
||||
<Card
|
||||
title={copy.cards.recipients.title}
|
||||
total={totalRecipients}
|
||||
countLabel={copy.cards.recipients.countLabel}
|
||||
href="/recipients"
|
||||
title={copy.cards.people.title}
|
||||
total={totalPeople}
|
||||
countLabel={copy.cards.people.countLabel}
|
||||
href="/people"
|
||||
icon={
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { UpdateAssignmentFormType } from "@/schemas/assignment.schema"
|
||||
import { AssetService } from "@/services/asset.service"
|
||||
import { AssignmentService } from "@/services/assignment.service"
|
||||
import { ItemService } from "@/services/item.service"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
import type { Item } from "@/types"
|
||||
|
||||
import AssignmentForm from "../../_components/edit.assignment.form"
|
||||
@@ -14,7 +14,7 @@ export default async function EditAssignmentPage({
|
||||
}) {
|
||||
const { assignmentId } = await params
|
||||
const assignment = await AssignmentService.findById(assignmentId)
|
||||
const recipients = await RecipientService.findAll()
|
||||
const people = await PersonService.findAll()
|
||||
const items = await ItemService.findAllWithStock()
|
||||
const assets = await AssetService.findAll()
|
||||
const { dictionary } = await getI18n()
|
||||
@@ -37,7 +37,7 @@ export default async function EditAssignmentPage({
|
||||
<h1 className="text-2xl font-bold">{copy.edit.title}</h1>
|
||||
</div>
|
||||
<AssignmentForm
|
||||
recipients={recipients}
|
||||
recipients={people}
|
||||
items={items}
|
||||
assets={assets}
|
||||
initialData={assignment as UpdateAssignmentFormType}
|
||||
|
||||
@@ -15,13 +15,13 @@ import {
|
||||
buildUpdateAssignmentSchema,
|
||||
type UpdateAssignmentFormType,
|
||||
} from "@/schemas/assignment.schema"
|
||||
import type { Asset, Item, Recipient } from "@/types"
|
||||
import type { Asset, Item, Person } from "@/types"
|
||||
|
||||
type AssignmentFormCopy = Dictionary["inventory"]["assignments"]["form"]
|
||||
type AssignmentSchemaCopy = Dictionary["inventory"]["assignments"]["schema"]
|
||||
|
||||
interface Props {
|
||||
recipients: Recipient[]
|
||||
recipients: Person[]
|
||||
items: Item[]
|
||||
assets: Asset[]
|
||||
initialData: UpdateAssignmentFormType
|
||||
|
||||
@@ -15,13 +15,13 @@ import {
|
||||
buildCreateAssignmentSchema,
|
||||
type CreateAssignmentFormType,
|
||||
} from "@/schemas/assignment.schema"
|
||||
import type { Asset, Item, Recipient } from "@/types"
|
||||
import type { Asset, Item, Person } from "@/types"
|
||||
|
||||
type AssignmentFormCopy = Dictionary["inventory"]["assignments"]["form"]
|
||||
type AssignmentSchemaCopy = Dictionary["inventory"]["assignments"]["schema"]
|
||||
|
||||
interface Props {
|
||||
recipients: Recipient[]
|
||||
recipients: Person[]
|
||||
items: Item[]
|
||||
assets: Asset[]
|
||||
formCopy: AssignmentFormCopy
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { AssetService } from "@/services/asset.service"
|
||||
import { ItemService } from "@/services/item.service"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import AssignmentForm from "../_components/new.assignment.form"
|
||||
|
||||
export default async function NewAssignmentPage() {
|
||||
const recipients = await RecipientService.findAll()
|
||||
const people = await PersonService.findAll()
|
||||
const items = await ItemService.findAllWithStock()
|
||||
const assets = await AssetService.findAllAvailable()
|
||||
const { dictionary } = await getI18n()
|
||||
@@ -18,7 +18,7 @@ export default async function NewAssignmentPage() {
|
||||
<h1 className="text-2xl font-bold">{copy.new.title}</h1>
|
||||
</div>
|
||||
<AssignmentForm
|
||||
recipients={recipients}
|
||||
recipients={people}
|
||||
items={items}
|
||||
assets={assets}
|
||||
formCopy={copy.form}
|
||||
|
||||
@@ -63,7 +63,7 @@ export default async function AssignmentsPage(props: {
|
||||
<tr key={assignment.id} className="border-b">
|
||||
<td className="p-4">
|
||||
<Link
|
||||
href={`/recipients/${assignment?.recipient?.id}`}
|
||||
href={`/people/${assignment?.recipient?.id}`}
|
||||
className="hover:underline"
|
||||
>
|
||||
{assignment?.recipient?.firstName}{" "}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { AssetService } from "@/services/asset.service"
|
||||
import { ItemService } from "@/services/item.service"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
import type { AssetWithAssignment } from "@/types"
|
||||
|
||||
import EditAssetForm from "../../_components/edit.asset.form"
|
||||
@@ -15,7 +15,7 @@ export default async function EditAssetPage({
|
||||
}) {
|
||||
const { assetId } = await params
|
||||
const items = await ItemService.findAll()
|
||||
const recipients = await RecipientService.findAll()
|
||||
const people = await PersonService.findAll()
|
||||
const asset = await AssetService.findById(assetId)
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.assets
|
||||
@@ -31,7 +31,7 @@ export default async function EditAssetPage({
|
||||
</div>
|
||||
<EditAssetForm
|
||||
items={items}
|
||||
recipients={recipients}
|
||||
recipients={people}
|
||||
asset={asset as unknown as AssetWithAssignment}
|
||||
formCopy={copy.form}
|
||||
schemaCopy={copy.schema}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import type {
|
||||
AssetWithAssignment,
|
||||
Item,
|
||||
Recipient,
|
||||
Person,
|
||||
UpdateAssetStatus,
|
||||
} from "@/types"
|
||||
|
||||
@@ -31,7 +31,7 @@ import type {
|
||||
interface EditAssetFormProps {
|
||||
asset: AssetWithAssignment
|
||||
items: Item[]
|
||||
recipients: Recipient[]
|
||||
recipients: Person[]
|
||||
formCopy: AssetFormCopy
|
||||
schemaCopy: AssetSchemaCopy
|
||||
statusCopy: AssetStatusCopy
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
buildCreateAssetSchema,
|
||||
type CreateAssetFormType,
|
||||
} from "@/schemas/asset.schema"
|
||||
import type { ItemWithoutStock, Recipient } from "@/types"
|
||||
import type { ItemWithoutStock, Person } from "@/types"
|
||||
|
||||
import type {
|
||||
AssetFormCopy,
|
||||
@@ -25,7 +25,7 @@ import type {
|
||||
|
||||
interface NewAssetFormProps {
|
||||
items: ItemWithoutStock[]
|
||||
recipients: Recipient[]
|
||||
recipients: Person[]
|
||||
formCopy: AssetFormCopy
|
||||
schemaCopy: AssetSchemaCopy
|
||||
statusCopy: AssetStatusCopy
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { ItemService } from "@/services/item.service"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import NewAssetForm from "../_components/new.asset.form"
|
||||
|
||||
export default async function NewAssetPage() {
|
||||
const items = await ItemService.findAllAssignable()
|
||||
const recipients = await RecipientService.findAll()
|
||||
const people = await PersonService.findAll()
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.assets
|
||||
|
||||
@@ -19,7 +19,7 @@ export default async function NewAssetPage() {
|
||||
</div>
|
||||
<NewAssetForm
|
||||
items={items}
|
||||
recipients={recipients}
|
||||
recipients={people}
|
||||
formCopy={copy.form}
|
||||
schemaCopy={copy.schema}
|
||||
statusCopy={copy.status}
|
||||
|
||||
+10
-10
@@ -1,19 +1,19 @@
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import RecipientForm from "../../_components/recipient.form"
|
||||
import PersonForm from "../../_components/person.form"
|
||||
|
||||
export default async function RecipientEditPage({
|
||||
export default async function PersonEditPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ recipientId: string }>
|
||||
params: Promise<{ personId: string }>
|
||||
}) {
|
||||
const { recipientId } = await params
|
||||
const { personId } = await params
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.recipients
|
||||
const recipient = await RecipientService.findById(recipientId)
|
||||
const copy = dictionary.inventory.people
|
||||
const person = await PersonService.findById(personId)
|
||||
|
||||
if (!recipient) {
|
||||
if (!person) {
|
||||
return <div>{copy.edit.notFound}</div>
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ export default async function RecipientEditPage({
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<h1 className="text-2xl font-bold">{copy.edit.title}</h1>
|
||||
</div>
|
||||
<RecipientForm
|
||||
initialData={recipient}
|
||||
<PersonForm
|
||||
initialData={person}
|
||||
mode="edit"
|
||||
formCopy={copy.form}
|
||||
schemaCopy={copy.schema}
|
||||
+14
-22
@@ -1,23 +1,23 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { AssignmentService } from "@/services/assignment.service"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import { formatRecipientDepartment } from "../_components/recipient.copy"
|
||||
import { formatPersonDepartment } from "../_components/person.copy"
|
||||
|
||||
export default async function RecipientInfoPage({
|
||||
export default async function PersonInfoPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ recipientId: string }>
|
||||
params: Promise<{ personId: string }>
|
||||
}) {
|
||||
const { recipientId } = await params
|
||||
const { personId } = await params
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.recipients
|
||||
const copy = dictionary.inventory.people
|
||||
const assignmentCopy = dictionary.inventory.assignments
|
||||
const recipient = await RecipientService.findById(recipientId)
|
||||
const assignments = await AssignmentService.findAllByRecipient(recipientId)
|
||||
const person = await PersonService.findById(personId)
|
||||
const assignments = await AssignmentService.findAllByPerson(personId)
|
||||
|
||||
if (!recipient) {
|
||||
if (!person) {
|
||||
return <div>{copy.detail.notFound}</div>
|
||||
}
|
||||
|
||||
@@ -25,33 +25,25 @@ export default async function RecipientInfoPage({
|
||||
<div className="grid gap-6">
|
||||
<Card className="rounded-sm shadow-none">
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
{`${recipient.firstName} ${recipient.lastName}`}
|
||||
</CardTitle>
|
||||
<CardTitle>{`${person.firstName} ${person.lastName}`}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">
|
||||
{copy.detail.labels.username}
|
||||
</span>
|
||||
<span>{recipient.username}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">{copy.detail.labels.email}</span>
|
||||
<span>{recipient.email}</span>
|
||||
<span>{person.email}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">{copy.detail.labels.phone}</span>
|
||||
<span>{recipient.phone}</span>
|
||||
<span>{person.phone}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">
|
||||
{copy.detail.labels.department}
|
||||
</span>
|
||||
<span>
|
||||
{formatRecipientDepartment(
|
||||
recipient.department,
|
||||
{formatPersonDepartment(
|
||||
person.department,
|
||||
copy.departments,
|
||||
copy.fallback,
|
||||
)}
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { Dictionary } from "@/i18n/dictionaries"
|
||||
|
||||
export type PersonListCopy = Dictionary["inventory"]["people"]["list"]
|
||||
export type PersonDetailCopy = Dictionary["inventory"]["people"]["detail"]
|
||||
export type PersonFormCopy = Dictionary["inventory"]["people"]["form"]
|
||||
export type PersonDepartmentCopy =
|
||||
Dictionary["inventory"]["people"]["departments"]
|
||||
export type PersonFallbackCopy = Dictionary["inventory"]["people"]["fallback"]
|
||||
|
||||
export function formatPersonDepartment(
|
||||
department: string | null | undefined,
|
||||
departmentCopy: PersonDepartmentCopy,
|
||||
fallbackCopy: PersonFallbackCopy,
|
||||
) {
|
||||
if (!department) {
|
||||
return fallbackCopy.unknownDepartment
|
||||
}
|
||||
|
||||
return department in departmentCopy
|
||||
? departmentCopy[department as keyof PersonDepartmentCopy]
|
||||
: fallbackCopy.unknownDepartment
|
||||
}
|
||||
+32
-55
@@ -5,42 +5,39 @@ import { useRouter } from "next/navigation"
|
||||
import { useMemo } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { toast } from "sonner"
|
||||
import {
|
||||
createNewRecipient,
|
||||
updateRecipient,
|
||||
} from "@/actions/recipient.actions"
|
||||
import { createNewPerson, updatePerson } from "@/actions/person.actions"
|
||||
import {
|
||||
SubmitButton,
|
||||
type SubmitButtonCopy,
|
||||
} from "@/components/forms/submitButton"
|
||||
import { RECIPIENT_DEPARTMENTS } from "@/lib/constants"
|
||||
import { PERSON_DEPARTMENTS } from "@/lib/constants"
|
||||
import {
|
||||
buildCreateRecipientSchema,
|
||||
buildUpdateRecipientSchema,
|
||||
type CreateRecipientFormType,
|
||||
type RecipientSchemaCopy,
|
||||
type UpdateRecipientFormType,
|
||||
} from "@/schemas/recipient.schema"
|
||||
import type { Recipient } from "@/types"
|
||||
buildCreatePersonSchema,
|
||||
buildUpdatePersonSchema,
|
||||
type CreatePersonFormType,
|
||||
type PersonSchemaCopy,
|
||||
type UpdatePersonFormType,
|
||||
} from "@/schemas/person.schema"
|
||||
import type { Person } from "@/types"
|
||||
|
||||
import {
|
||||
formatRecipientDepartment,
|
||||
type RecipientDepartmentCopy,
|
||||
type RecipientFallbackCopy,
|
||||
type RecipientFormCopy,
|
||||
} from "./recipient.copy"
|
||||
formatPersonDepartment,
|
||||
type PersonDepartmentCopy,
|
||||
type PersonFallbackCopy,
|
||||
type PersonFormCopy,
|
||||
} from "./person.copy"
|
||||
|
||||
interface RecipientFormProps {
|
||||
initialData?: Recipient
|
||||
interface PersonFormProps {
|
||||
initialData?: Person
|
||||
mode?: "create" | "edit"
|
||||
formCopy: RecipientFormCopy
|
||||
schemaCopy: RecipientSchemaCopy
|
||||
departmentCopy: RecipientDepartmentCopy
|
||||
fallbackCopy: RecipientFallbackCopy
|
||||
formCopy: PersonFormCopy
|
||||
schemaCopy: PersonSchemaCopy
|
||||
departmentCopy: PersonDepartmentCopy
|
||||
fallbackCopy: PersonFallbackCopy
|
||||
submitButtonCopy: SubmitButtonCopy
|
||||
}
|
||||
|
||||
export default function RecipientForm({
|
||||
export default function PersonForm({
|
||||
initialData,
|
||||
mode = "create",
|
||||
formCopy,
|
||||
@@ -48,13 +45,13 @@ export default function RecipientForm({
|
||||
departmentCopy,
|
||||
fallbackCopy,
|
||||
submitButtonCopy,
|
||||
}: RecipientFormProps) {
|
||||
}: PersonFormProps) {
|
||||
const router = useRouter()
|
||||
const schema = useMemo(
|
||||
() =>
|
||||
mode === "create"
|
||||
? buildCreateRecipientSchema(schemaCopy)
|
||||
: buildUpdateRecipientSchema(schemaCopy),
|
||||
? buildCreatePersonSchema(schemaCopy)
|
||||
: buildUpdatePersonSchema(schemaCopy),
|
||||
[mode, schemaCopy],
|
||||
)
|
||||
|
||||
@@ -63,11 +60,10 @@ export default function RecipientForm({
|
||||
handleSubmit,
|
||||
setError,
|
||||
formState: { errors, isSubmitting, isSubmitSuccessful },
|
||||
} = useForm<CreateRecipientFormType>({
|
||||
} = useForm<CreatePersonFormType>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
id: initialData?.id || "",
|
||||
username: initialData?.username || "",
|
||||
firstName: initialData?.firstName || "",
|
||||
lastName: initialData?.lastName || "",
|
||||
department: initialData?.department || "OTHER",
|
||||
@@ -76,16 +72,16 @@ export default function RecipientForm({
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = async (formData: CreateRecipientFormType) => {
|
||||
const onSubmit = async (formData: CreatePersonFormType) => {
|
||||
const response =
|
||||
mode === "create"
|
||||
? await createNewRecipient(formData)
|
||||
: await updateRecipient(formData as UpdateRecipientFormType)
|
||||
? await createNewPerson(formData)
|
||||
: await updatePerson(formData as UpdatePersonFormType)
|
||||
|
||||
if (response?.errors) {
|
||||
Object.entries(response.errors).forEach(([fieldName, messages]) => {
|
||||
messages.forEach((msg: string) => {
|
||||
setError(fieldName as keyof CreateRecipientFormType, {
|
||||
setError(fieldName as keyof CreatePersonFormType, {
|
||||
type: "server",
|
||||
message: msg,
|
||||
})
|
||||
@@ -97,28 +93,13 @@ export default function RecipientForm({
|
||||
|
||||
if (response?.success) {
|
||||
toast.success(response.message)
|
||||
router.push("/recipients")
|
||||
router.push("/people")
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<input type="hidden" {...register("id")} />
|
||||
<div>
|
||||
<label htmlFor="username" className="mb-2 block text-lg">
|
||||
{formCopy.usernameLabel}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
placeholder={formCopy.usernamePlaceholder}
|
||||
{...register("username")}
|
||||
className={`w-full rounded-lg border px-4 py-2`}
|
||||
/>
|
||||
{errors?.username && (
|
||||
<p className="text-error">{errors.username.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="firstName" className="mb-2 block text-lg">
|
||||
{formCopy.firstNameLabel}
|
||||
@@ -159,13 +140,9 @@ export default function RecipientForm({
|
||||
className="w-full rounded-lg border px-4 py-2"
|
||||
>
|
||||
<option value="">{formCopy.departmentPlaceholder}</option>
|
||||
{Object.keys(RECIPIENT_DEPARTMENTS).map((department) => (
|
||||
{Object.keys(PERSON_DEPARTMENTS).map((department) => (
|
||||
<option key={department} value={department}>
|
||||
{formatRecipientDepartment(
|
||||
department,
|
||||
departmentCopy,
|
||||
fallbackCopy,
|
||||
)}
|
||||
{formatPersonDepartment(department, departmentCopy, fallbackCopy)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
+4
-4
@@ -1,17 +1,17 @@
|
||||
import { getI18n } from "@/i18n/server"
|
||||
|
||||
import RecipientForm from "../_components/recipient.form"
|
||||
import PersonForm from "../_components/person.form"
|
||||
|
||||
export default async function NewRecipientPage() {
|
||||
export default async function NewPersonPage() {
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.recipients
|
||||
const copy = dictionary.inventory.people
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<h1 className="text-2xl font-bold">{copy.new.title}</h1>
|
||||
</div>
|
||||
<RecipientForm
|
||||
<PersonForm
|
||||
mode="create"
|
||||
formCopy={copy.form}
|
||||
schemaCopy={copy.schema}
|
||||
@@ -4,13 +4,13 @@ import Link from "next/link"
|
||||
import PageHeader from "@/components/common/pageheader"
|
||||
import PaginationButtons from "@/components/common/pagination"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import type { Recipient } from "@/generated/prisma/client"
|
||||
import type { Person } from "@/generated/prisma/client"
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { RecipientService } from "@/services/recipient.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import { formatRecipientDepartment } from "./_components/recipient.copy"
|
||||
import { formatPersonDepartment } from "./_components/person.copy"
|
||||
|
||||
export default async function RecipientsPage(props: {
|
||||
export default async function PeoplePage(props: {
|
||||
searchParams?: Promise<{
|
||||
page?: string
|
||||
search?: string
|
||||
@@ -19,33 +19,29 @@ export default async function RecipientsPage(props: {
|
||||
const searchParams = await props.searchParams
|
||||
const currentPage = searchParams?.page ? parseInt(searchParams.page, 10) : 1
|
||||
const search = searchParams?.search || ""
|
||||
const { data: recipients, totalPages } =
|
||||
await RecipientService.findAllPaginated({
|
||||
page: currentPage,
|
||||
pageSize: 10,
|
||||
search,
|
||||
})
|
||||
const { data: people, totalPages } = await PersonService.findAllPaginated({
|
||||
page: currentPage,
|
||||
pageSize: 10,
|
||||
search,
|
||||
})
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.recipients
|
||||
const copy = dictionary.inventory.people
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<PageHeader
|
||||
title={copy.list.title}
|
||||
link="/recipients/new"
|
||||
link="/people/new"
|
||||
addLabel={copy.list.addLabel}
|
||||
data={recipients}
|
||||
data={people}
|
||||
search={search}
|
||||
/>
|
||||
{recipients.length === 0 && <div>{copy.list.empty}</div>}
|
||||
{recipients.length > 0 && (
|
||||
{people.length === 0 && <div>{copy.list.empty}</div>}
|
||||
{people.length > 0 && (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="text-muted-foreground w-full text-left text-sm">
|
||||
<thead className="border-b">
|
||||
<tr>
|
||||
<th scope="col" className="p-4">
|
||||
{copy.list.columns.username}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
{copy.list.columns.name}
|
||||
</th>
|
||||
@@ -64,23 +60,22 @@ export default async function RecipientsPage(props: {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{recipients.map((recipient: Recipient) => (
|
||||
<tr key={recipient.id} className="border-b">
|
||||
<td className="p-4">{recipient.username}</td>
|
||||
{people.map((person: Person) => (
|
||||
<tr key={person.id} className="border-b">
|
||||
<td className="p-4">
|
||||
{`${recipient.firstName} ${recipient.lastName}`}
|
||||
{`${person.firstName} ${person.lastName}`}
|
||||
</td>
|
||||
<td className="p-4">{recipient.email}</td>
|
||||
<td className="p-4">{recipient.phone}</td>
|
||||
<td className="p-4">{person.email}</td>
|
||||
<td className="p-4">{person.phone}</td>
|
||||
<td className="p-4">
|
||||
{formatRecipientDepartment(
|
||||
recipient.department,
|
||||
{formatPersonDepartment(
|
||||
person.department,
|
||||
copy.departments,
|
||||
copy.fallback,
|
||||
)}
|
||||
</td>
|
||||
<td className="flex items-center gap-2 p-4">
|
||||
<Link href={`/recipients/${recipient.id}`} passHref>
|
||||
<Link href={`/people/${person.id}`} passHref>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
@@ -89,7 +84,7 @@ export default async function RecipientsPage(props: {
|
||||
<Eye />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={`/recipients/${recipient.id}/edit`} passHref>
|
||||
<Link href={`/people/${person.id}/edit`} passHref>
|
||||
<Button
|
||||
className="btn btn-primary"
|
||||
variant="outline"
|
||||
@@ -105,7 +100,7 @@ export default async function RecipientsPage(props: {
|
||||
</tbody>
|
||||
<tfoot className="border-t">
|
||||
<tr>
|
||||
<td colSpan={6} className="p-4 text-center text-sm">
|
||||
<td colSpan={5} className="p-4 text-center text-sm">
|
||||
<PaginationButtons totalPages={totalPages} />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { Dictionary } from "@/i18n/dictionaries"
|
||||
|
||||
export type RecipientListCopy = Dictionary["inventory"]["recipients"]["list"]
|
||||
export type RecipientDetailCopy =
|
||||
Dictionary["inventory"]["recipients"]["detail"]
|
||||
export type RecipientFormCopy = Dictionary["inventory"]["recipients"]["form"]
|
||||
export type RecipientDepartmentCopy =
|
||||
Dictionary["inventory"]["recipients"]["departments"]
|
||||
export type RecipientFallbackCopy =
|
||||
Dictionary["inventory"]["recipients"]["fallback"]
|
||||
|
||||
export function formatRecipientDepartment(
|
||||
department: string | null | undefined,
|
||||
departmentCopy: RecipientDepartmentCopy,
|
||||
fallbackCopy: RecipientFallbackCopy,
|
||||
) {
|
||||
if (!department) {
|
||||
return fallbackCopy.unknownDepartment
|
||||
}
|
||||
|
||||
return department in departmentCopy
|
||||
? departmentCopy[department as keyof RecipientDepartmentCopy]
|
||||
: fallbackCopy.unknownDepartment
|
||||
}
|
||||
@@ -37,8 +37,8 @@ const items: { key: keyof AddMenuCopy; href: string }[] = [
|
||||
href: "/inventory/assets/new",
|
||||
},
|
||||
{
|
||||
key: "recipient",
|
||||
href: "/recipients/new",
|
||||
key: "person",
|
||||
href: "/people/new",
|
||||
},
|
||||
{
|
||||
key: "assignment",
|
||||
|
||||
@@ -74,8 +74,8 @@ const items: SidebarItem[] = [
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "recipients",
|
||||
url: "/recipients",
|
||||
labelKey: "people",
|
||||
url: "/people",
|
||||
icon: User,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -37,6 +37,7 @@ export const en = {
|
||||
categories: "Categories",
|
||||
assets: "Assets",
|
||||
recipients: "Recipients",
|
||||
people: "People",
|
||||
movements: "Movements",
|
||||
assignments: "Assignments",
|
||||
users: "Users",
|
||||
@@ -51,6 +52,7 @@ export const en = {
|
||||
item: "Item",
|
||||
asset: "Asset",
|
||||
recipient: "Recipient",
|
||||
person: "Person",
|
||||
assignment: "Assignment",
|
||||
},
|
||||
resetDatabase: {
|
||||
@@ -610,8 +612,12 @@ export const en = {
|
||||
title: "Total Recipients",
|
||||
countLabel: "Total",
|
||||
},
|
||||
people: {
|
||||
title: "Total People",
|
||||
countLabel: "Total",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export type Dictionary = typeof en
|
||||
export type Dictionary = typeof en
|
||||
|
||||
@@ -39,6 +39,7 @@ export const es = {
|
||||
categories: "Categorías",
|
||||
assets: "Activos",
|
||||
recipients: "Destinatarios",
|
||||
people: "Personas",
|
||||
movements: "Movimientos",
|
||||
assignments: "Asignaciones",
|
||||
users: "Usuarios",
|
||||
@@ -53,6 +54,7 @@ export const es = {
|
||||
item: "Artículo",
|
||||
asset: "Activo",
|
||||
recipient: "Destinatario",
|
||||
person: "Persona",
|
||||
assignment: "Asignación",
|
||||
},
|
||||
resetDatabase: {
|
||||
@@ -615,6 +617,10 @@ export const es = {
|
||||
title: "Total de destinatarios",
|
||||
countLabel: "Total",
|
||||
},
|
||||
people: {
|
||||
title: "Total de personas",
|
||||
countLabel: "Total",
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies Dictionary
|
||||
} satisfies Dictionary
|
||||
|
||||
@@ -71,4 +71,4 @@ export const updatePersonSchema = buildUpdatePersonSchema(
|
||||
defaultPersonSchemaCopy,
|
||||
)
|
||||
|
||||
export type UpdatePersonFormType = z.infer<typeof updatePersonSchema>
|
||||
export type UpdatePersonFormType = z.infer<typeof updatePersonSchema>
|
||||
|
||||
@@ -143,4 +143,4 @@ export const AssignmentService = {
|
||||
data,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Prisma, Person } from "@/generated/prisma/client"
|
||||
import type { Person, Prisma } from "@/generated/prisma/client"
|
||||
import { paginate } from "@/lib/paginate"
|
||||
import prisma from "@/lib/prisma"
|
||||
|
||||
@@ -68,4 +68,4 @@ export const PersonService = {
|
||||
): Promise<Person> => {
|
||||
return db.person.update({ where: { id }, data })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export type AssignmentWithRecipientItemAsset = Assignment & {
|
||||
recipient: Person | null
|
||||
item: Item | null
|
||||
asset: Asset | null
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
import type { Person as PrismaPerson } from "@/generated/prisma/client"
|
||||
|
||||
export type Person = PrismaPerson
|
||||
export type Person = PrismaPerson
|
||||
|
||||
@@ -106,7 +106,9 @@ export async function updatePersonUseCase(
|
||||
department,
|
||||
email: email || null,
|
||||
phone: phone || null,
|
||||
...(userId ? { user: { connect: { id: userId } } } : { userId: null }),
|
||||
...(userId
|
||||
? { user: { connect: { id: userId } } }
|
||||
: { userId: null }),
|
||||
},
|
||||
tx,
|
||||
)
|
||||
@@ -124,4 +126,4 @@ export async function updatePersonUseCase(
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type {
|
||||
PrismaClient,
|
||||
PersonDepartment,
|
||||
PrismaClient,
|
||||
UserRole,
|
||||
} from "@/generated/prisma/client"
|
||||
|
||||
@@ -90,4 +90,4 @@ export async function createTestItem(
|
||||
category: { connect: { id: categoryId } },
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,4 +187,4 @@ describe("person use-cases", () => {
|
||||
expect(nameResults.data).toHaveLength(1)
|
||||
expect(nameResults.data[0].firstName).toBe("Bob")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -23,8 +23,8 @@ vi.mock("@/i18n/server", () => ({
|
||||
getI18n: mocks.getI18n,
|
||||
}))
|
||||
|
||||
vi.mock("@/services/recipient.service", () => ({
|
||||
RecipientService: {
|
||||
vi.mock("@/services/person.service", () => ({
|
||||
PersonService: {
|
||||
findAll: mocks.findAllRecipients,
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { renderToStaticMarkup } from "react-dom/server"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
import { en } from "@/i18n/dictionaries/en"
|
||||
import { es } from "@/i18n/dictionaries/es"
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getI18n: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
createNewPerson: vi.fn(),
|
||||
updatePerson: vi.fn(),
|
||||
push: vi.fn(),
|
||||
toastError: vi.fn(),
|
||||
toastSuccess: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock("@/i18n/server", () => ({
|
||||
getI18n: mocks.getI18n,
|
||||
}))
|
||||
|
||||
vi.mock("@/services/person.service", () => ({
|
||||
PersonService: {
|
||||
findById: mocks.findById,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/actions/person.actions", () => ({
|
||||
createNewPerson: mocks.createNewPerson,
|
||||
updatePerson: mocks.updatePerson,
|
||||
}))
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({
|
||||
push: mocks.push,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock("sonner", () => ({
|
||||
toast: {
|
||||
error: mocks.toastError,
|
||||
success: mocks.toastSuccess,
|
||||
},
|
||||
}))
|
||||
|
||||
describe("person form pages", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
|
||||
})
|
||||
|
||||
it("renders the new person page with Person form copy and no username field", async () => {
|
||||
const { default: NewPersonPage } = await import(
|
||||
"@/app/(dashboard)/people/new/page"
|
||||
)
|
||||
|
||||
const html = renderToStaticMarkup(await NewPersonPage())
|
||||
|
||||
// Person form, not Recipient
|
||||
expect(html).toContain("Agregar persona")
|
||||
// No username label or placeholder
|
||||
expect(html).not.toContain("Usuario")
|
||||
expect(html).not.toContain('placeholder="Usuario"')
|
||||
// Has expected person form fields
|
||||
expect(html).toContain("Nombre")
|
||||
expect(html).toContain("Apellido")
|
||||
expect(html).toContain("Selecciona un departamento")
|
||||
expect(html).toContain('option value="ENGINEERING"')
|
||||
expect(html).toContain(">Ingeniería</option>")
|
||||
// Has department options from PERSON_DEPARTMENTS
|
||||
expect(html).toContain("Correo electrónico")
|
||||
expect(html).toContain("Teléfono")
|
||||
expect(html).toContain("Crear persona")
|
||||
})
|
||||
|
||||
it("renders the edit person page with Person heading and no username", async () => {
|
||||
const { default: PersonEditPage } = await import(
|
||||
"@/app/(dashboard)/people/[personId]/edit/page"
|
||||
)
|
||||
|
||||
mocks.findById.mockResolvedValue({
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "ENGINEERING",
|
||||
})
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await PersonEditPage({
|
||||
params: Promise.resolve({ personId: "person-1" }),
|
||||
}),
|
||||
)
|
||||
|
||||
expect(html).toContain("Editar persona")
|
||||
expect(html).toContain("Actualizar persona")
|
||||
expect(html).not.toContain("Usuario")
|
||||
})
|
||||
|
||||
it("renders a Person not-found message on edit page", async () => {
|
||||
const { default: PersonEditPage } = await import(
|
||||
"@/app/(dashboard)/people/[personId]/edit/page"
|
||||
)
|
||||
|
||||
mocks.findById.mockResolvedValue(null)
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await PersonEditPage({
|
||||
params: Promise.resolve({ personId: "missing-person" }),
|
||||
}),
|
||||
)
|
||||
|
||||
expect(html).toContain("Persona no encontrada")
|
||||
})
|
||||
|
||||
it("wires English Person form submit copy through the new page", async () => {
|
||||
const { default: NewPersonPage } = await import(
|
||||
"@/app/(dashboard)/people/new/page"
|
||||
)
|
||||
|
||||
mocks.getI18n.mockResolvedValueOnce({ dictionary: en, locale: "en" })
|
||||
|
||||
const html = renderToStaticMarkup(await NewPersonPage())
|
||||
|
||||
expect(html).toContain("Create Person")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,81 @@
|
||||
import { createElement } from "react"
|
||||
import { renderToStaticMarkup } from "react-dom/server"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
import { en } from "@/i18n/dictionaries/en"
|
||||
import { es } from "@/i18n/dictionaries/es"
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getI18n: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
personForm: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock("@/i18n/server", () => ({
|
||||
getI18n: mocks.getI18n,
|
||||
}))
|
||||
|
||||
vi.mock("@/services/person.service", () => ({
|
||||
PersonService: {
|
||||
findById: mocks.findById,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/app/(dashboard)/people/_components/person.form", () => ({
|
||||
default: (props: unknown) => {
|
||||
mocks.personForm(props)
|
||||
return createElement("div", null, "Person form")
|
||||
},
|
||||
}))
|
||||
|
||||
describe("person form schema wiring", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it("passes server-resolved Person schema copy into the new person form boundary", async () => {
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
|
||||
|
||||
const { default: NewPersonPage } = await import(
|
||||
"@/app/(dashboard)/people/new/page"
|
||||
)
|
||||
|
||||
renderToStaticMarkup(await NewPersonPage())
|
||||
|
||||
expect(mocks.personForm).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
mode: "create",
|
||||
schemaCopy: es.inventory.people.schema,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("passes server-resolved Person schema copy into the edit person form boundary", async () => {
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: en, locale: "en" })
|
||||
mocks.findById.mockResolvedValue({
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
})
|
||||
|
||||
const { default: PersonEditPage } = await import(
|
||||
"@/app/(dashboard)/people/[personId]/edit/page"
|
||||
)
|
||||
|
||||
renderToStaticMarkup(
|
||||
await PersonEditPage({
|
||||
params: Promise.resolve({ personId: "person-1" }),
|
||||
}),
|
||||
)
|
||||
|
||||
expect(mocks.personForm).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
mode: "edit",
|
||||
schemaCopy: en.inventory.people.schema,
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,163 @@
|
||||
import { createElement } from "react"
|
||||
import { renderToStaticMarkup } from "react-dom/server"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
import { en } from "@/i18n/dictionaries/en"
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
findAllPaginated: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
findAllByPerson: vi.fn(),
|
||||
getI18n: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock("@/i18n/server", () => ({
|
||||
getI18n: mocks.getI18n,
|
||||
}))
|
||||
|
||||
vi.mock("@/services/person.service", () => ({
|
||||
PersonService: {
|
||||
findAllPaginated: mocks.findAllPaginated,
|
||||
findById: mocks.findById,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/services/assignment.service", () => ({
|
||||
AssignmentService: {
|
||||
findAllByPerson: mocks.findAllByPerson,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/components/common/pageheader", () => ({
|
||||
default: ({ title, addLabel }: { title: string; addLabel?: string }) =>
|
||||
createElement(
|
||||
"header",
|
||||
null,
|
||||
[title, addLabel].filter(Boolean).join(" | "),
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock("@/components/common/pagination", () => ({
|
||||
default: ({ totalPages }: { totalPages: number }) =>
|
||||
createElement("nav", { "aria-label": "Pagination" }, totalPages),
|
||||
}))
|
||||
|
||||
describe("person pages", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: en, locale: "en" })
|
||||
})
|
||||
|
||||
it("renders the person list page with Person data and no username column", async () => {
|
||||
const { default: PeoplePage } = await import(
|
||||
"@/app/(dashboard)/people/page"
|
||||
)
|
||||
|
||||
mocks.findAllPaginated.mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "ENGINEERING",
|
||||
},
|
||||
],
|
||||
totalPages: 1,
|
||||
})
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await PeoplePage({ searchParams: Promise.resolve({}) }),
|
||||
)
|
||||
|
||||
// Uses Person copy (inventory.people), not Recipient copy
|
||||
expect(html).toContain("People")
|
||||
expect(html).toContain("Add Person")
|
||||
// No username column — username header must not appear
|
||||
expect(html).not.toContain("Username")
|
||||
// No standalone username cell — only name, email, phone, department columns
|
||||
expect(html).not.toContain(">ada<")
|
||||
// Name and other fields rendered
|
||||
expect(html).toContain("Ada Lovelace")
|
||||
expect(html).toContain("Engineering")
|
||||
// Links point to /people, not /recipients
|
||||
expect(html).toContain("/people/person-1")
|
||||
expect(html).toContain("/people/person-1/edit")
|
||||
})
|
||||
|
||||
it("renders the person list empty state from Person copy", async () => {
|
||||
const { default: PeoplePage } = await import(
|
||||
"@/app/(dashboard)/people/page"
|
||||
)
|
||||
|
||||
mocks.findAllPaginated.mockResolvedValue({
|
||||
data: [],
|
||||
totalPages: 0,
|
||||
})
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await PeoplePage({ searchParams: Promise.resolve({}) }),
|
||||
)
|
||||
|
||||
expect(html).toContain("No people found.")
|
||||
})
|
||||
|
||||
it("renders person detail page without username and uses PersonService + AssignmentService.findAllByPerson", async () => {
|
||||
const { default: PersonInfoPage } = await import(
|
||||
"@/app/(dashboard)/people/[personId]/page"
|
||||
)
|
||||
|
||||
mocks.findById.mockResolvedValue({
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "DRIVER",
|
||||
})
|
||||
mocks.findAllByPerson.mockResolvedValue([
|
||||
{
|
||||
id: "assignment-1",
|
||||
item: { name: "Laptop" },
|
||||
asset: { serialNumber: "SN-001" },
|
||||
quantity: 1,
|
||||
},
|
||||
])
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await PersonInfoPage({
|
||||
params: Promise.resolve({ personId: "person-1" }),
|
||||
}),
|
||||
)
|
||||
|
||||
// No username label or value
|
||||
expect(html).not.toContain("Username")
|
||||
expect(html).not.toContain(">ada<")
|
||||
// Person detail fields
|
||||
expect(html).toContain("Email")
|
||||
expect(html).toContain("Phone")
|
||||
expect(html).toContain("Department")
|
||||
expect(html).toContain("ada@example.test")
|
||||
expect(html).toContain("Driver")
|
||||
// Embedded assignments
|
||||
expect(html).toContain("Laptop")
|
||||
})
|
||||
|
||||
it("renders person detail not-found from Person copy", async () => {
|
||||
const { default: PersonInfoPage } = await import(
|
||||
"@/app/(dashboard)/people/[personId]/page"
|
||||
)
|
||||
|
||||
mocks.findById.mockResolvedValue(null)
|
||||
mocks.findAllByPerson.mockResolvedValue([])
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await PersonInfoPage({
|
||||
params: Promise.resolve({ personId: "missing-person" }),
|
||||
}),
|
||||
)
|
||||
|
||||
expect(html).toContain("Person not found")
|
||||
})
|
||||
})
|
||||
@@ -53,6 +53,7 @@ describe("i18n dictionaries", () => {
|
||||
categories: "Categories",
|
||||
assets: "Assets",
|
||||
recipients: "Recipients",
|
||||
people: "People",
|
||||
movements: "Movements",
|
||||
assignments: "Assignments",
|
||||
users: "Users",
|
||||
@@ -67,6 +68,7 @@ describe("i18n dictionaries", () => {
|
||||
item: "Item",
|
||||
asset: "Asset",
|
||||
recipient: "Recipient",
|
||||
person: "Person",
|
||||
assignment: "Assignment",
|
||||
},
|
||||
resetDatabase: {
|
||||
@@ -88,6 +90,7 @@ describe("i18n dictionaries", () => {
|
||||
categories: "Categorías",
|
||||
assets: "Activos",
|
||||
recipients: "Destinatarios",
|
||||
people: "Personas",
|
||||
movements: "Movimientos",
|
||||
assignments: "Asignaciones",
|
||||
users: "Usuarios",
|
||||
@@ -102,6 +105,7 @@ describe("i18n dictionaries", () => {
|
||||
item: "Artículo",
|
||||
asset: "Activo",
|
||||
recipient: "Destinatario",
|
||||
person: "Persona",
|
||||
assignment: "Asignación",
|
||||
},
|
||||
resetDatabase: {
|
||||
@@ -940,6 +944,10 @@ describe("i18n dictionaries", () => {
|
||||
title: "Total Recipients",
|
||||
countLabel: "Total",
|
||||
},
|
||||
people: {
|
||||
title: "Total People",
|
||||
countLabel: "Total",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -958,6 +966,10 @@ describe("i18n dictionaries", () => {
|
||||
title: "Total de destinatarios",
|
||||
countLabel: "Total",
|
||||
},
|
||||
people: {
|
||||
title: "Total de personas",
|
||||
countLabel: "Total",
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user