feat(i18n): localize recipients UI

This commit is contained in:
2026-06-14 18:33:57 +02:00
parent ea37fc8d70
commit c0ae7a034a
11 changed files with 665 additions and 34 deletions
@@ -9,21 +9,25 @@ export default async function RecipientEditPage({
params: Promise<{ recipientId: string }>
}) {
const { recipientId } = await params
const recipient = await RecipientService.findById(recipientId)
const { dictionary } = await getI18n()
const copy = dictionary.inventory.recipients
const recipient = await RecipientService.findById(recipientId)
if (!recipient) {
return <div>Recipient not found</div>
return <div>{copy.edit.notFound}</div>
}
return (
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between gap-4">
<h1 className="text-2xl font-bold">Edit Recipient</h1>
<h1 className="text-2xl font-bold">{copy.edit.title}</h1>
</div>
<RecipientForm
initialData={recipient}
mode="edit"
formCopy={copy.form}
departmentCopy={copy.departments}
fallbackCopy={copy.fallback}
submitButtonCopy={dictionary.common.submitButton}
/>
</div>
@@ -1,18 +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 { formatRecipientDepartment } from "../_components/recipient.copy"
export default async function RecipientInfoPage({
params,
}: {
params: Promise<{ recipientId: string }>
}) {
const { recipientId } = await params
const { dictionary } = await getI18n()
const copy = dictionary.inventory.recipients
const recipient = await RecipientService.findById(recipientId)
const assignments = await AssignmentService.findAllByRecipient(recipientId)
if (!recipient) {
return <div>Recipient not found</div>
return <div>{copy.detail.notFound}</div>
}
return (
@@ -26,20 +31,30 @@ export default async function RecipientInfoPage({
<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">Username</span>
<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">Email</span>
<span className="text-gray-600">{copy.detail.labels.email}</span>
<span>{recipient.email}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">Phone</span>
<span className="text-gray-600">{copy.detail.labels.phone}</span>
<span>{recipient.phone}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">Department</span>
<span>{recipient.department}</span>
<span className="text-gray-600">
{copy.detail.labels.department}
</span>
<span>
{formatRecipientDepartment(
recipient.department,
copy.departments,
copy.fallback,
)}
</span>
</div>
</div>
</CardContent>
@@ -0,0 +1,24 @@
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
}
@@ -20,15 +20,28 @@ import {
} from "@/schemas/recipient.schema"
import type { Recipient } from "@/types"
import {
formatRecipientDepartment,
type RecipientDepartmentCopy,
type RecipientFallbackCopy,
type RecipientFormCopy,
} from "./recipient.copy"
interface RecipientFormProps {
initialData?: Recipient
mode?: "create" | "edit"
formCopy: RecipientFormCopy
departmentCopy: RecipientDepartmentCopy
fallbackCopy: RecipientFallbackCopy
submitButtonCopy: SubmitButtonCopy
}
export default function RecipientForm({
initialData,
mode = "create",
formCopy,
departmentCopy,
fallbackCopy,
submitButtonCopy,
}: RecipientFormProps) {
const router = useRouter()
@@ -81,12 +94,12 @@ export default function RecipientForm({
<input type="hidden" {...register("id")} />
<div>
<label htmlFor="username" className="mb-2 block text-lg">
Username
{formCopy.usernameLabel}
</label>
<input
type="text"
id="username"
placeholder="Username"
placeholder={formCopy.usernamePlaceholder}
{...register("username")}
className={`w-full rounded-lg border px-4 py-2`}
/>
@@ -96,12 +109,12 @@ export default function RecipientForm({
</div>
<div>
<label htmlFor="firstName" className="mb-2 block text-lg">
First Name
{formCopy.firstNameLabel}
</label>
<input
type="text"
id="firstName"
placeholder="First Name"
placeholder={formCopy.firstNamePlaceholder}
{...register("firstName")}
className={`w-full rounded-lg border px-4 py-2`}
/>
@@ -111,12 +124,12 @@ export default function RecipientForm({
</div>
<div>
<label htmlFor="lastName" className="mb-2 block text-lg">
Last Name
{formCopy.lastNameLabel}
</label>
<input
type="text"
id="lastName"
placeholder="Last Name"
placeholder={formCopy.lastNamePlaceholder}
{...register("lastName")}
className={`w-full rounded-lg border px-4 py-2`}
/>
@@ -126,17 +139,21 @@ export default function RecipientForm({
</div>
<div>
<label htmlFor="department" className="mb-2 block text-lg">
Department
{formCopy.departmentLabel}
</label>
<select
id="department"
{...register("department")}
className="w-full rounded-lg border px-4 py-2"
>
<option value="">Select a department</option>
<option value="">{formCopy.departmentPlaceholder}</option>
{Object.keys(RECIPIENT_DEPARTMENTS).map((department) => (
<option key={department} value={department}>
{department}
{formatRecipientDepartment(
department,
departmentCopy,
fallbackCopy,
)}
</option>
))}
</select>
@@ -146,12 +163,12 @@ export default function RecipientForm({
</div>
<div>
<label htmlFor="email" className="mb-2 block text-lg">
Email
{formCopy.emailLabel}
</label>
<input
type="text"
id="email"
placeholder="Email"
placeholder={formCopy.emailPlaceholder}
{...register("email")}
className={`w-full rounded-lg border px-4 py-2`}
/>
@@ -159,12 +176,12 @@ export default function RecipientForm({
</div>
<div>
<label htmlFor="phone" className="mb-2 block text-lg">
Phone
{formCopy.phoneLabel}
</label>
<input
type="text"
id="phone"
placeholder="Phone"
placeholder={formCopy.phonePlaceholder}
{...register("phone")}
className={`w-full rounded-lg border px-4 py-2`}
/>
@@ -175,7 +192,7 @@ export default function RecipientForm({
isSubmitting={isSubmitting}
isSubmitSuccessful={isSubmitSuccessful}
>
{mode === "create" ? "Create Recipient" : "Update Recipient"}
{mode === "create" ? formCopy.createSubmit : formCopy.updateSubmit}
</SubmitButton>
</form>
)
+5 -1
View File
@@ -4,14 +4,18 @@ import RecipientForm from "../_components/recipient.form"
export default async function NewRecipientPage() {
const { dictionary } = await getI18n()
const copy = dictionary.inventory.recipients
return (
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between gap-4">
<h1 className="text-2xl font-bold">Add Recipient</h1>
<h1 className="text-2xl font-bold">{copy.new.title}</h1>
</div>
<RecipientForm
mode="create"
formCopy={copy.form}
departmentCopy={copy.departments}
fallbackCopy={copy.fallback}
submitButtonCopy={dictionary.common.submitButton}
/>
</div>
+27 -10
View File
@@ -5,8 +5,11 @@ 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 { getI18n } from "@/i18n/server"
import { RecipientService } from "@/services/recipient.service"
import { formatRecipientDepartment } from "./_components/recipient.copy"
export default async function RecipientsPage(props: {
searchParams?: Promise<{
page?: string
@@ -22,38 +25,41 @@ export default async function RecipientsPage(props: {
pageSize: 10,
search,
})
const { dictionary } = await getI18n()
const copy = dictionary.inventory.recipients
return (
<div className="flex flex-col gap-4">
<PageHeader
title="Recipients"
title={copy.list.title}
link="/recipients/new"
addLabel={copy.list.addLabel}
data={recipients}
search={search}
/>
{recipients.length === 0 && <div>No recipients found</div>}
{recipients.length === 0 && <div>{copy.list.empty}</div>}
{recipients.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">
Username
{copy.list.columns.username}
</th>
<th scope="col" className="p-4">
Name
{copy.list.columns.name}
</th>
<th scope="col" className="p-4">
Email
{copy.list.columns.email}
</th>
<th scope="col" className="p-4">
Phone
{copy.list.columns.phone}
</th>
<th scope="col" className="p-4">
Department
{copy.list.columns.department}
</th>
<th scope="col" className="p-4">
Actions
{copy.list.columns.actions}
</th>
</tr>
</thead>
@@ -66,10 +72,20 @@ export default async function RecipientsPage(props: {
</td>
<td className="p-4">{recipient.email}</td>
<td className="p-4">{recipient.phone}</td>
<td className="p-4">{recipient.department}</td>
<td className="p-4">
{formatRecipientDepartment(
recipient.department,
copy.departments,
copy.fallback,
)}
</td>
<td className="flex items-center gap-2 p-4">
<Link href={`/recipients/${recipient.id}`} passHref>
<Button variant="outline" size="icon">
<Button
variant="outline"
size="icon"
aria-label={copy.list.actions.view}
>
<Eye />
</Button>
</Link>
@@ -78,6 +94,7 @@ export default async function RecipientsPage(props: {
className="btn btn-primary"
variant="outline"
size="icon"
aria-label={copy.list.actions.edit}
>
<Pencil />
</Button>
+64
View File
@@ -255,6 +255,70 @@ export const en = {
invalidUpdateStatus: "Invalid status",
},
},
recipients: {
list: {
title: "Recipients",
addLabel: "Add Recipient",
empty: "No recipients found.",
columns: {
username: "Username",
name: "Name",
email: "Email",
phone: "Phone",
department: "Department",
actions: "Actions",
},
actions: {
view: "View recipient",
edit: "Edit recipient",
},
},
detail: {
notFound: "Recipient not found",
labels: {
username: "Username",
email: "Email",
phone: "Phone",
department: "Department",
},
},
new: {
title: "Add Recipient",
},
edit: {
title: "Edit Recipient",
notFound: "Recipient not found",
},
form: {
usernameLabel: "Username",
usernamePlaceholder: "Username",
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 Recipient",
updateSubmit: "Update Recipient",
},
fallback: {
unknownDepartment: "Unknown department",
},
departments: {
IT: "IT",
ENGINEERING: "Engineering",
LOGISTICS: "Logistics",
TRAFFIC: "Traffic",
DRIVER: "Driver",
ADMINISTRATION: "Administration",
SALES: "Sales",
OTHER: "Other",
},
},
movements: {
list: {
title: "Movements",
+64
View File
@@ -259,6 +259,70 @@ export const es = {
invalidUpdateStatus: "Estado inválido",
},
},
recipients: {
list: {
title: "Destinatarios",
addLabel: "Agregar destinatario",
empty: "No se encontraron destinatarios.",
columns: {
username: "Usuario",
name: "Nombre",
email: "Correo electrónico",
phone: "Teléfono",
department: "Departamento",
actions: "Acciones",
},
actions: {
view: "Ver destinatario",
edit: "Editar destinatario",
},
},
detail: {
notFound: "Destinatario no encontrado",
labels: {
username: "Usuario",
email: "Correo electrónico",
phone: "Teléfono",
department: "Departamento",
},
},
new: {
title: "Agregar destinatario",
},
edit: {
title: "Editar destinatario",
notFound: "Destinatario no encontrado",
},
form: {
usernameLabel: "Usuario",
usernamePlaceholder: "Usuario",
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 destinatario",
updateSubmit: "Actualizar destinatario",
},
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",
},
},
movements: {
list: {
title: "Movimientos",
@@ -0,0 +1,125 @@
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(),
createNewRecipient: vi.fn(),
updateRecipient: vi.fn(),
push: vi.fn(),
toastError: vi.fn(),
toastSuccess: vi.fn(),
}))
vi.mock("@/i18n/server", () => ({
getI18n: mocks.getI18n,
}))
vi.mock("@/services/recipient.service", () => ({
RecipientService: {
findById: mocks.findById,
},
}))
vi.mock("@/actions/recipient.actions", () => ({
createNewRecipient: mocks.createNewRecipient,
updateRecipient: mocks.updateRecipient,
}))
vi.mock("next/navigation", () => ({
useRouter: () => ({
push: mocks.push,
}),
}))
vi.mock("sonner", () => ({
toast: {
error: mocks.toastError,
success: mocks.toastSuccess,
},
}))
describe("recipient form pages localization", () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
})
it("renders the new recipient page with localized form copy and canonical department option values", async () => {
const { default: NewRecipientPage } = await import(
"@/app/(dashboard)/recipients/new/page"
)
const html = renderToStaticMarkup(await NewRecipientPage())
expect(html).toContain("Agregar destinatario")
expect(html).toContain("Usuario")
expect(html).toContain('placeholder="Usuario"')
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>")
expect(html).toContain("Correo electrónico")
expect(html).toContain("Teléfono")
expect(html).toContain("Crear destinatario")
})
it("renders the edit recipient page with localized heading and submit text", async () => {
const { default: RecipientEditPage } = await import(
"@/app/(dashboard)/recipients/[recipientId]/edit/page"
)
mocks.findById.mockResolvedValue({
id: "recipient-1",
username: "ada",
firstName: "Ada",
lastName: "Lovelace",
email: "ada@example.test",
phone: "1234",
department: "ENGINEERING",
})
const html = renderToStaticMarkup(
await RecipientEditPage({
params: Promise.resolve({ recipientId: "recipient-1" }),
}),
)
expect(html).toContain("Editar destinatario")
expect(html).toContain("Actualizar destinatario")
expect(html).toContain('placeholder="Correo electrónico"')
expect(html).toContain(">Ingeniería</option>")
})
it("renders a localized edit-page not-found message", async () => {
const { default: RecipientEditPage } = await import(
"@/app/(dashboard)/recipients/[recipientId]/edit/page"
)
mocks.findById.mockResolvedValue(null)
const html = renderToStaticMarkup(
await RecipientEditPage({
params: Promise.resolve({ recipientId: "missing-recipient" }),
}),
)
expect(html).toContain("Destinatario no encontrado")
})
it("wires English recipient form submit copy through the new page", async () => {
const { default: NewRecipientPage } = await import(
"@/app/(dashboard)/recipients/new/page"
)
mocks.getI18n.mockResolvedValueOnce({ dictionary: en, locale: "en" })
const html = renderToStaticMarkup(await NewRecipientPage())
expect(html).toContain("Create Recipient")
})
})
@@ -0,0 +1,165 @@
import { createElement } from "react"
import { renderToStaticMarkup } from "react-dom/server"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { es } from "@/i18n/dictionaries/es"
const mocks = vi.hoisted(() => ({
findAllPaginated: vi.fn(),
findById: vi.fn(),
findAllByRecipient: vi.fn(),
getI18n: vi.fn(),
}))
vi.mock("@/i18n/server", () => ({
getI18n: mocks.getI18n,
}))
vi.mock("@/services/recipient.service", () => ({
RecipientService: {
findAllPaginated: mocks.findAllPaginated,
findById: mocks.findById,
},
}))
vi.mock("@/services/assignment.service", () => ({
AssignmentService: {
findAllByRecipient: mocks.findAllByRecipient,
},
}))
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("recipient pages localization", () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
})
it("renders the recipient list in Spanish while keeping stored department values display-only", async () => {
const { default: RecipientsPage } = await import(
"@/app/(dashboard)/recipients/page"
)
mocks.findAllPaginated.mockResolvedValue({
data: [
{
id: "recipient-1",
username: "ada",
firstName: "Ada",
lastName: "Lovelace",
email: "ada@example.test",
phone: "1234",
department: "ENGINEERING",
},
],
totalPages: 1,
})
const html = renderToStaticMarkup(
await RecipientsPage({ searchParams: Promise.resolve({}) }),
)
expect(html).toContain("Destinatarios")
expect(html).toContain("Agregar destinatario")
expect(html).toContain("Usuario")
expect(html).toContain("Nombre")
expect(html).toContain("Correo electrónico")
expect(html).toContain("Teléfono")
expect(html).toContain("Departamento")
expect(html).toContain("Acciones")
expect(html).toContain("Ada Lovelace")
expect(html).toContain("Ingeniería")
expect(html).toContain('aria-label="Ver destinatario"')
expect(html).toContain('aria-label="Editar destinatario"')
expect(html).not.toContain(">ENGINEERING<")
})
it("renders the localized recipient empty state when no recipients exist", async () => {
const { default: RecipientsPage } = await import(
"@/app/(dashboard)/recipients/page"
)
mocks.findAllPaginated.mockResolvedValue({
data: [],
totalPages: 0,
})
const html = renderToStaticMarkup(
await RecipientsPage({ searchParams: Promise.resolve({}) }),
)
expect(html).toContain("No se encontraron destinatarios.")
})
it("renders localized recipient-owned detail labels and keeps assignments copy unchanged", async () => {
const { default: RecipientInfoPage } = await import(
"@/app/(dashboard)/recipients/[recipientId]/page"
)
mocks.findById.mockResolvedValue({
id: "recipient-1",
username: "ada",
firstName: "Ada",
lastName: "Lovelace",
email: "ada@example.test",
phone: "1234",
department: "DRIVER",
})
mocks.findAllByRecipient.mockResolvedValue([
{
id: "assignment-1",
item: { name: "Laptop" },
asset: { serialNumber: "SN-001" },
quantity: 1,
},
])
const html = renderToStaticMarkup(
await RecipientInfoPage({
params: Promise.resolve({ recipientId: "recipient-1" }),
}),
)
expect(html).toContain("Usuario")
expect(html).toContain("Correo electrónico")
expect(html).toContain("Teléfono")
expect(html).toContain("Departamento")
expect(html).toContain("Chofer")
expect(html).toContain("ada")
expect(html).toContain("ada@example.test")
expect(html).toContain("Assignments")
expect(html).toContain("Laptop")
expect(html).not.toContain(">DRIVER<")
expect(html).not.toContain("Asignaciones")
})
it("renders a localized recipient detail not-found message", async () => {
const { default: RecipientInfoPage } = await import(
"@/app/(dashboard)/recipients/[recipientId]/page"
)
mocks.findById.mockResolvedValue(null)
mocks.findAllByRecipient.mockResolvedValue([])
const html = renderToStaticMarkup(
await RecipientInfoPage({
params: Promise.resolve({ recipientId: "missing-recipient" }),
}),
)
expect(html).toContain("Destinatario no encontrado")
})
})
+132
View File
@@ -559,6 +559,138 @@ describe("i18n dictionaries", () => {
})
})
it("provides localized recipient UI copy for English and Spanish", () => {
expect(getDictionary("en").inventory.recipients).toEqual({
list: {
title: "Recipients",
addLabel: "Add Recipient",
empty: "No recipients found.",
columns: {
username: "Username",
name: "Name",
email: "Email",
phone: "Phone",
department: "Department",
actions: "Actions",
},
actions: {
view: "View recipient",
edit: "Edit recipient",
},
},
detail: {
notFound: "Recipient not found",
labels: {
username: "Username",
email: "Email",
phone: "Phone",
department: "Department",
},
},
new: {
title: "Add Recipient",
},
edit: {
title: "Edit Recipient",
notFound: "Recipient not found",
},
form: {
usernameLabel: "Username",
usernamePlaceholder: "Username",
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 Recipient",
updateSubmit: "Update Recipient",
},
fallback: {
unknownDepartment: "Unknown department",
},
departments: {
IT: "IT",
ENGINEERING: "Engineering",
LOGISTICS: "Logistics",
TRAFFIC: "Traffic",
DRIVER: "Driver",
ADMINISTRATION: "Administration",
SALES: "Sales",
OTHER: "Other",
},
})
expect(getDictionary("es").inventory.recipients).toEqual({
list: {
title: "Destinatarios",
addLabel: "Agregar destinatario",
empty: "No se encontraron destinatarios.",
columns: {
username: "Usuario",
name: "Nombre",
email: "Correo electrónico",
phone: "Teléfono",
department: "Departamento",
actions: "Acciones",
},
actions: {
view: "Ver destinatario",
edit: "Editar destinatario",
},
},
detail: {
notFound: "Destinatario no encontrado",
labels: {
username: "Usuario",
email: "Correo electrónico",
phone: "Teléfono",
department: "Departamento",
},
},
new: {
title: "Agregar destinatario",
},
edit: {
title: "Editar destinatario",
notFound: "Destinatario no encontrado",
},
form: {
usernameLabel: "Usuario",
usernamePlaceholder: "Usuario",
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 destinatario",
updateSubmit: "Actualizar destinatario",
},
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",
},
})
})
it("provides localized movement UI copy for English and Spanish", () => {
expect(getDictionary("en").inventory.movements).toEqual({
list: {