feat(i18n): localize inventory assets UI

This commit is contained in:
2026-06-13 17:07:51 +02:00
parent c67e86c91b
commit 3d6b13dc1c
10 changed files with 367 additions and 38 deletions
@@ -18,20 +18,23 @@ export default async function EditAssetPage({
const recipients = await RecipientService.findAll() const recipients = await RecipientService.findAll()
const asset = await AssetService.findById(assetId) const asset = await AssetService.findById(assetId)
const { dictionary } = await getI18n() const { dictionary } = await getI18n()
const copy = dictionary.inventory.assets
if (!asset) { if (!asset) {
return <div>Asset not found</div> return <div>{copy.edit.notFound}</div>
} }
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="flex items-center justify-between gap-4"> <div className="flex items-center justify-between gap-4">
<h1 className="text-2xl font-bold">Edit Asset</h1> <h1 className="text-2xl font-bold">{copy.edit.title}</h1>
</div> </div>
<EditAssetForm <EditAssetForm
items={items} items={items}
recipients={recipients} recipients={recipients}
asset={asset as unknown as AssetWithAssignment} asset={asset as unknown as AssetWithAssignment}
formCopy={copy.form}
statusCopy={copy.status}
submitButtonCopy={dictionary.common.submitButton} submitButtonCopy={dictionary.common.submitButton}
/> />
</div> </div>
@@ -0,0 +1,6 @@
import type { Dictionary } from "@/i18n/dictionaries"
export type AssetListCopy = Dictionary["inventory"]["assets"]["list"]
export type AssetFormCopy = Dictionary["inventory"]["assets"]["form"]
export type AssetStatusCopy = Dictionary["inventory"]["assets"]["status"]
export type AssetFallbackCopy = Dictionary["inventory"]["assets"]["fallback"]
@@ -21,10 +21,14 @@ import type {
UpdateAssetStatus, UpdateAssetStatus,
} from "@/types" } from "@/types"
import type { AssetFormCopy, AssetStatusCopy } from "./asset.copy"
interface EditAssetFormProps { interface EditAssetFormProps {
asset: AssetWithAssignment asset: AssetWithAssignment
items: Item[] items: Item[]
recipients: Recipient[] recipients: Recipient[]
formCopy: AssetFormCopy
statusCopy: AssetStatusCopy
submitButtonCopy: SubmitButtonCopy submitButtonCopy: SubmitButtonCopy
} }
@@ -32,6 +36,8 @@ export default function EditAssetForm({
asset, asset,
items, items,
recipients, recipients,
formCopy,
statusCopy,
submitButtonCopy, submitButtonCopy,
}: EditAssetFormProps) { }: EditAssetFormProps) {
const router = useRouter() const router = useRouter()
@@ -83,15 +89,15 @@ export default function EditAssetForm({
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}> <form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
<input type="hidden" {...register("id")} /> <input type="hidden" {...register("id")} />
<div> <div>
<label htmlFor="categoryId" className="mb-2 block text-lg"> <label htmlFor="itemId" className="mb-2 block text-lg">
Item {formCopy.itemLabel}
</label> </label>
<select <select
id="itemId" id="itemId"
{...register("itemId")} {...register("itemId")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
> >
<option value="">Select a item:</option> <option value="">{formCopy.itemPlaceholder}</option>
{items?.map((item) => ( {items?.map((item) => (
<option key={item.id} value={item.id}> <option key={item.id} value={item.id}>
{item.name} {item.name}
@@ -104,12 +110,12 @@ export default function EditAssetForm({
</div> </div>
<div> <div>
<label htmlFor="serialNumber" className="mb-2 block text-lg"> <label htmlFor="serialNumber" className="mb-2 block text-lg">
Serial Number {formCopy.serialNumberLabel}
</label> </label>
<input <input
type="text" type="text"
id="serialNumber" id="serialNumber"
placeholder="Serial number" placeholder={formCopy.serialNumberPlaceholder}
{...register("serialNumber")} {...register("serialNumber")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
/> />
@@ -119,12 +125,12 @@ export default function EditAssetForm({
</div> </div>
<div> <div>
<label htmlFor="deliveryNote" className="mb-2 block text-lg"> <label htmlFor="deliveryNote" className="mb-2 block text-lg">
Delivery Note {formCopy.deliveryNoteLabel}
</label> </label>
<input <input
type="text" type="text"
id="deliveryNote" id="deliveryNote"
placeholder="Delivery note" placeholder={formCopy.deliveryNotePlaceholder}
{...register("deliveryNote")} {...register("deliveryNote")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
/> />
@@ -134,17 +140,17 @@ export default function EditAssetForm({
</div> </div>
<div> <div>
<label htmlFor="status" className="mb-2 block text-lg"> <label htmlFor="status" className="mb-2 block text-lg">
Status {formCopy.statusLabel}
</label> </label>
<select <select
id="status" id="status"
{...register("status")} {...register("status")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
> >
<option value="">Select a status</option> <option value="">{formCopy.statusPlaceholder}</option>
{Object.values(ITEM_STATUS).map((status) => ( {Object.values(ITEM_STATUS).map((status) => (
<option key={status} value={status}> <option key={status} value={status}>
{status} {statusCopy[status]}
</option> </option>
))} ))}
</select> </select>
@@ -155,14 +161,14 @@ export default function EditAssetForm({
{status === "ASSIGNED" && ( {status === "ASSIGNED" && (
<div> <div>
<label htmlFor="recipientId" className="mb-2 block text-lg"> <label htmlFor="recipientId" className="mb-2 block text-lg">
Recipient {formCopy.recipientLabel}
</label> </label>
<select <select
id="recipientId" id="recipientId"
{...register("recipientId")} {...register("recipientId")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
> >
<option value="">Select a Recipient</option> <option value="">{formCopy.recipientPlaceholder}</option>
{recipients?.map((recipient) => ( {recipients?.map((recipient) => (
<option key={recipient.id} value={recipient.id}> <option key={recipient.id} value={recipient.id}>
{recipient.firstName} {recipient.lastName} {recipient.firstName} {recipient.lastName}
@@ -179,7 +185,7 @@ export default function EditAssetForm({
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
isSubmitSuccessful={isSubmitSuccessful} isSubmitSuccessful={isSubmitSuccessful}
> >
Update Asset {formCopy.updateSubmit}
</SubmitButton> </SubmitButton>
</form> </form>
) )
@@ -16,15 +16,21 @@ import {
} from "@/schemas/asset.schema" } from "@/schemas/asset.schema"
import type { ItemWithoutStock, Recipient } from "@/types" import type { ItemWithoutStock, Recipient } from "@/types"
import type { AssetFormCopy, AssetStatusCopy } from "./asset.copy"
interface NewAssetFormProps { interface NewAssetFormProps {
items: ItemWithoutStock[] items: ItemWithoutStock[]
recipients: Recipient[] recipients: Recipient[]
formCopy: AssetFormCopy
statusCopy: AssetStatusCopy
submitButtonCopy: SubmitButtonCopy submitButtonCopy: SubmitButtonCopy
} }
export default function NewAssetForm({ export default function NewAssetForm({
items, items,
recipients, recipients,
formCopy,
statusCopy,
submitButtonCopy, submitButtonCopy,
}: NewAssetFormProps) { }: NewAssetFormProps) {
const router = useRouter() const router = useRouter()
@@ -71,15 +77,15 @@ export default function NewAssetForm({
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}> <form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
<input type="hidden" {...register("id")} /> <input type="hidden" {...register("id")} />
<div> <div>
<label htmlFor="categoryId" className="mb-2 block text-lg"> <label htmlFor="itemId" className="mb-2 block text-lg">
Item {formCopy.itemLabel}
</label> </label>
<select <select
id="itemId" id="itemId"
{...register("itemId")} {...register("itemId")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
> >
<option value="">Select a item:</option> <option value="">{formCopy.itemPlaceholder}</option>
{items?.map((item) => ( {items?.map((item) => (
<option key={item.id} value={item.id}> <option key={item.id} value={item.id}>
{item.name} {item.name}
@@ -92,12 +98,12 @@ export default function NewAssetForm({
</div> </div>
<div> <div>
<label htmlFor="serialNumber" className="mb-2 block text-lg"> <label htmlFor="serialNumber" className="mb-2 block text-lg">
Serial Number {formCopy.serialNumberLabel}
</label> </label>
<input <input
type="text" type="text"
id="serialNumber" id="serialNumber"
placeholder="Serial number" placeholder={formCopy.serialNumberPlaceholder}
{...register("serialNumber")} {...register("serialNumber")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
/> />
@@ -107,12 +113,12 @@ export default function NewAssetForm({
</div> </div>
<div> <div>
<label htmlFor="deliveryNote" className="mb-2 block text-lg"> <label htmlFor="deliveryNote" className="mb-2 block text-lg">
Delivery Note {formCopy.deliveryNoteLabel}
</label> </label>
<input <input
type="text" type="text"
id="deliveryNote" id="deliveryNote"
placeholder="Delivery note" placeholder={formCopy.deliveryNotePlaceholder}
{...register("deliveryNote")} {...register("deliveryNote")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
/> />
@@ -122,17 +128,17 @@ export default function NewAssetForm({
</div> </div>
<div> <div>
<label htmlFor="status" className="mb-2 block text-lg"> <label htmlFor="status" className="mb-2 block text-lg">
Status {formCopy.statusLabel}
</label> </label>
<select <select
id="status" id="status"
{...register("status")} {...register("status")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
> >
<option value="">Select a status</option> <option value="">{formCopy.statusPlaceholder}</option>
{Object.values(ITEM_STATUS).map((status) => ( {Object.values(ITEM_STATUS).map((status) => (
<option key={status} value={status}> <option key={status} value={status}>
{status} {statusCopy[status]}
</option> </option>
))} ))}
</select> </select>
@@ -143,14 +149,14 @@ export default function NewAssetForm({
{status === "ASSIGNED" && ( {status === "ASSIGNED" && (
<div> <div>
<label htmlFor="recipientId" className="mb-2 block text-lg"> <label htmlFor="recipientId" className="mb-2 block text-lg">
Recipient {formCopy.recipientLabel}
</label> </label>
<select <select
id="recipientId" id="recipientId"
{...register("recipientId")} {...register("recipientId")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
> >
<option value="">Select a Recipient</option> <option value="">{formCopy.recipientPlaceholder}</option>
{recipients?.map((recipient) => ( {recipients?.map((recipient) => (
<option key={recipient.id} value={recipient.id}> <option key={recipient.id} value={recipient.id}>
{recipient.firstName} {recipient.lastName} {recipient.firstName} {recipient.lastName}
@@ -167,7 +173,7 @@ export default function NewAssetForm({
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
isSubmitSuccessful={isSubmitSuccessful} isSubmitSuccessful={isSubmitSuccessful}
> >
Create Asset {formCopy.createSubmit}
</SubmitButton> </SubmitButton>
</form> </form>
) )
@@ -10,15 +10,18 @@ export default async function NewAssetPage() {
const items = await ItemService.findAllAssignable() const items = await ItemService.findAllAssignable()
const recipients = await RecipientService.findAll() const recipients = await RecipientService.findAll()
const { dictionary } = await getI18n() const { dictionary } = await getI18n()
const copy = dictionary.inventory.assets
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="flex items-center justify-between gap-4"> <div className="flex items-center justify-between gap-4">
<h1 className="text-2xl font-bold">New Asset</h1> <h1 className="text-2xl font-bold">{copy.new.title}</h1>
</div> </div>
<NewAssetForm <NewAssetForm
items={items} items={items}
recipients={recipients} recipients={recipients}
formCopy={copy.form}
statusCopy={copy.status}
submitButtonCopy={dictionary.common.submitButton} submitButtonCopy={dictionary.common.submitButton}
/> />
</div> </div>
+38 -9
View File
@@ -4,8 +4,24 @@ import Link from "next/link"
import PageHeader from "@/components/common/pageheader" import PageHeader from "@/components/common/pageheader"
import PaginationButtons from "@/components/common/pagination" import PaginationButtons from "@/components/common/pagination"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { getI18n } from "@/i18n/server"
import { AssetService } from "@/services/asset.service" import { AssetService } from "@/services/asset.service"
import type {
AssetFallbackCopy,
AssetStatusCopy,
} from "./_components/asset.copy"
function formatAssetStatus(
status: string,
statusCopy: AssetStatusCopy,
fallbackCopy: AssetFallbackCopy,
) {
return status in statusCopy
? statusCopy[status as keyof AssetStatusCopy]
: fallbackCopy.unknownStatus
}
export default async function AssetsPage(props: { export default async function AssetsPage(props: {
searchParams?: Promise<{ searchParams?: Promise<{
page?: string page?: string
@@ -21,19 +37,22 @@ export default async function AssetsPage(props: {
pageSize: 10, pageSize: 10,
search, search,
}) })
const { dictionary } = await getI18n()
const copy = dictionary.inventory.assets
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<PageHeader <PageHeader
title="Assets" title={copy.list.title}
link="/inventory/assets/new" link="/inventory/assets/new"
data={assets} data={assets}
search={search} search={search}
addLabel={copy.list.addLabel}
/> />
{assets.length === 0 && currentPage === 1 && ( {assets.length === 0 && currentPage === 1 && (
<div className="flex gap-4"> <div className="flex gap-4">
<div className="flex items-center justify-between gap-4"> <div className="flex items-center justify-between gap-4">
No Assets found. {copy.list.empty}
</div> </div>
</div> </div>
)} )}
@@ -43,19 +62,19 @@ export default async function AssetsPage(props: {
<thead className="border-b"> <thead className="border-b">
<tr> <tr>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
Item Name {copy.list.columns.item}
</th> </th>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
Category {copy.list.columns.category}
</th> </th>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
Serial Number {copy.list.columns.serialNumber}
</th> </th>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
Status {copy.list.columns.status}
</th> </th>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
Actions {copy.list.columns.actions}
</th> </th>
</tr> </tr>
</thead> </thead>
@@ -65,10 +84,20 @@ export default async function AssetsPage(props: {
<td className="p-4">{asset.item?.name}</td> <td className="p-4">{asset.item?.name}</td>
<td className="p-4">{asset.item?.category?.name}</td> <td className="p-4">{asset.item?.category?.name}</td>
<td className="p-4">{asset.serialNumber}</td> <td className="p-4">{asset.serialNumber}</td>
<td className="p-4">{asset.status}</td> <td className="p-4">
{formatAssetStatus(
asset.status,
copy.status,
copy.fallback,
)}
</td>
<td className="flex items-center gap-2 p-4"> <td className="flex items-center gap-2 p-4">
<Link href={`/inventory/assets/${asset.id}/edit`} passHref> <Link href={`/inventory/assets/${asset.id}/edit`} passHref>
<Button variant="outline" size="icon"> <Button
variant="outline"
size="icon"
aria-label={copy.list.actions.edit}
>
<Pencil /> <Pencil />
</Button> </Button>
</Link> </Link>
+50
View File
@@ -182,6 +182,56 @@ export const en = {
itemRequired: "Item is required", itemRequired: "Item is required",
}, },
}, },
assets: {
list: {
title: "Assets",
addLabel: "Add Asset",
empty: "No assets found.",
columns: {
item: "Item",
category: "Category",
serialNumber: "Serial Number",
status: "Status",
actions: "Actions",
},
actions: {
edit: "Edit asset",
},
},
new: {
title: "New Asset",
},
edit: {
title: "Edit Asset",
notFound: "Asset not found",
},
form: {
itemLabel: "Item",
itemPlaceholder: "Select an item",
serialNumberLabel: "Serial Number",
serialNumberPlaceholder: "Serial number",
deliveryNoteLabel: "Delivery Note",
deliveryNotePlaceholder: "Delivery note",
statusLabel: "Status",
statusPlaceholder: "Select a status",
recipientLabel: "Recipient",
recipientPlaceholder: "Select a recipient",
createSubmit: "Create Asset",
updateSubmit: "Update Asset",
},
status: {
AVAILABLE: "Available",
ASSIGNED: "Assigned",
RESERVED: "Reserved",
IN_REPAIR: "In repair",
BROKEN: "Broken",
STOLEN: "Stolen",
DISPOSED: "Disposed",
},
fallback: {
unknownStatus: "Unknown status",
},
},
}, },
login: { login: {
title: "Sign In", title: "Sign In",
+50
View File
@@ -185,6 +185,56 @@ export const es = {
itemRequired: "El artículo es obligatorio", itemRequired: "El artículo es obligatorio",
}, },
}, },
assets: {
list: {
title: "Activos",
addLabel: "Agregar activo",
empty: "No se encontraron activos.",
columns: {
item: "Artículo",
category: "Categoría",
serialNumber: "Número de serie",
status: "Estado",
actions: "Acciones",
},
actions: {
edit: "Editar activo",
},
},
new: {
title: "Nuevo activo",
},
edit: {
title: "Editar activo",
notFound: "Activo no encontrado",
},
form: {
itemLabel: "Artículo",
itemPlaceholder: "Selecciona un artículo",
serialNumberLabel: "Número de serie",
serialNumberPlaceholder: "Número de serie",
deliveryNoteLabel: "Remito",
deliveryNotePlaceholder: "Remito",
statusLabel: "Estado",
statusPlaceholder: "Selecciona un estado",
recipientLabel: "Destinatario",
recipientPlaceholder: "Selecciona un destinatario",
createSubmit: "Crear activo",
updateSubmit: "Actualizar activo",
},
status: {
AVAILABLE: "Disponible",
ASSIGNED: "Asignado",
RESERVED: "Reservado",
IN_REPAIR: "En reparación",
BROKEN: "Roto",
STOLEN: "Robado",
DISPOSED: "Dado de baja",
},
fallback: {
unknownStatus: "Estado desconocido",
},
},
}, },
login: { login: {
title: "Iniciar sesión", title: "Iniciar sesión",
+72
View File
@@ -0,0 +1,72 @@
import { expect, type Page, test } from "@playwright/test"
async function setLocaleCookie(
page: Page,
locale: "en" | "es",
baseURL?: string,
) {
await page.context().addCookies([
{
name: "stock-manager-locale",
value: locale,
url: baseURL ?? "http://127.0.0.1:3100",
},
])
}
async function signInAsAdmin(page: Page, baseURL?: string) {
await setLocaleCookie(page, "en", baseURL)
await page.goto("/login")
await page.getByLabel("Username").fill("admin")
await page.getByLabel("Password").fill("admin-password")
await page.getByRole("button", { name: "Sign In" }).click()
await expect(page).toHaveURL("/")
}
test.describe("inventory assets localization", () => {
test("renders asset list and new form UI/status copy in Spanish", async ({
baseURL,
page,
}) => {
await signInAsAdmin(page, baseURL)
await setLocaleCookie(page, "es", baseURL)
await page.goto("/inventory/assets")
await expect(page.locator("html")).toHaveAttribute("lang", "es")
await expect(page.getByRole("heading", { name: "Activos" })).toBeVisible()
await expect(
page.getByRole("link", { name: /Agregar activo/ }),
).toBeVisible()
await expect(page.getByText("No se encontraron activos.")).toBeVisible()
await page.goto("/inventory/assets/new")
await expect(
page.getByRole("heading", { name: "Nuevo activo" }),
).toBeVisible()
await expect(page.getByLabel("Artículo")).toBeVisible()
await expect(page.locator("select#itemId")).toContainText(
"Selecciona un artículo",
)
await expect(page.getByLabel("Número de serie")).toBeVisible()
await expect(page.getByPlaceholder("Número de serie")).toBeVisible()
await expect(page.getByLabel("Remito")).toBeVisible()
await expect(page.getByPlaceholder("Remito")).toBeVisible()
await expect(page.getByLabel("Estado")).toBeVisible()
await expect(page.locator("select#status")).toContainText("Disponible")
await expect(page.locator("select#status")).toContainText("Asignado")
await expect(page.locator("option[value='AVAILABLE']")).toHaveText(
"Disponible",
)
await expect(page.locator("option[value='ASSIGNED']")).toHaveText(
"Asignado",
)
await expect(
page.getByRole("button", { name: "Crear activo" }),
).toBeVisible()
await expect(
page.getByRole("button", { name: /Eliminar activo/i }),
).toHaveCount(0)
})
})
+104
View File
@@ -408,6 +408,110 @@ describe("i18n dictionaries", () => {
}) })
}) })
it("provides localized inventory asset UI copy for English and Spanish", () => {
expect(getDictionary("en").inventory.assets).toEqual({
list: {
title: "Assets",
addLabel: "Add Asset",
empty: "No assets found.",
columns: {
item: "Item",
category: "Category",
serialNumber: "Serial Number",
status: "Status",
actions: "Actions",
},
actions: {
edit: "Edit asset",
},
},
new: {
title: "New Asset",
},
edit: {
title: "Edit Asset",
notFound: "Asset not found",
},
form: {
itemLabel: "Item",
itemPlaceholder: "Select an item",
serialNumberLabel: "Serial Number",
serialNumberPlaceholder: "Serial number",
deliveryNoteLabel: "Delivery Note",
deliveryNotePlaceholder: "Delivery note",
statusLabel: "Status",
statusPlaceholder: "Select a status",
recipientLabel: "Recipient",
recipientPlaceholder: "Select a recipient",
createSubmit: "Create Asset",
updateSubmit: "Update Asset",
},
status: {
AVAILABLE: "Available",
ASSIGNED: "Assigned",
RESERVED: "Reserved",
IN_REPAIR: "In repair",
BROKEN: "Broken",
STOLEN: "Stolen",
DISPOSED: "Disposed",
},
fallback: {
unknownStatus: "Unknown status",
},
})
expect(getDictionary("es").inventory.assets).toEqual({
list: {
title: "Activos",
addLabel: "Agregar activo",
empty: "No se encontraron activos.",
columns: {
item: "Artículo",
category: "Categoría",
serialNumber: "Número de serie",
status: "Estado",
actions: "Acciones",
},
actions: {
edit: "Editar activo",
},
},
new: {
title: "Nuevo activo",
},
edit: {
title: "Editar activo",
notFound: "Activo no encontrado",
},
form: {
itemLabel: "Artículo",
itemPlaceholder: "Selecciona un artículo",
serialNumberLabel: "Número de serie",
serialNumberPlaceholder: "Número de serie",
deliveryNoteLabel: "Remito",
deliveryNotePlaceholder: "Remito",
statusLabel: "Estado",
statusPlaceholder: "Selecciona un estado",
recipientLabel: "Destinatario",
recipientPlaceholder: "Selecciona un destinatario",
createSubmit: "Crear activo",
updateSubmit: "Actualizar activo",
},
status: {
AVAILABLE: "Disponible",
ASSIGNED: "Asignado",
RESERVED: "Reservado",
IN_REPAIR: "En reparación",
BROKEN: "Roto",
STOLEN: "Robado",
DISPOSED: "Dado de baja",
},
fallback: {
unknownStatus: "Estado desconocido",
},
})
})
it("keeps dashboard home dictionary keys aligned across locales", () => { it("keeps dashboard home dictionary keys aligned across locales", () => {
expect(getDictionary("en").dashboardHome).toEqual({ expect(getDictionary("en").dashboardHome).toEqual({
heading: "Dashboard", heading: "Dashboard",