- Create Category
+ {formCopy.createSubmit}
)
diff --git a/src/app/(dashboard)/inventory/categories/new/page.tsx b/src/app/(dashboard)/inventory/categories/new/page.tsx
index fe02f44..4e58d65 100644
--- a/src/app/(dashboard)/inventory/categories/new/page.tsx
+++ b/src/app/(dashboard)/inventory/categories/new/page.tsx
@@ -4,13 +4,17 @@ import NewCategoryForm from "../_components/new.category.form"
export default async function NewCategoryPage() {
const { dictionary } = await getI18n()
+ const copy = dictionary.inventory.categories
return (
)
}
diff --git a/src/app/(dashboard)/inventory/categories/page.tsx b/src/app/(dashboard)/inventory/categories/page.tsx
index 17fb49d..c39ea99 100644
--- a/src/app/(dashboard)/inventory/categories/page.tsx
+++ b/src/app/(dashboard)/inventory/categories/page.tsx
@@ -4,6 +4,7 @@ import Link from "next/link"
import PageHeader from "@/components/common/pageheader"
import PaginationButtons from "@/components/common/pagination"
import { Button } from "@/components/ui/button"
+import { getI18n } from "@/i18n/server"
import { CategoryService } from "@/services/category.service"
import DeleteCategoryButton from "./_components/delete.category.button"
@@ -23,18 +24,21 @@ export default async function Items(props: {
pageSize: 10,
search,
})
+ const { dictionary } = await getI18n()
+ const copy = dictionary.inventory.categories
return (
{categories.length === 0 && currentPage === 1 && (
- No Categories found.
+ {copy.list.empty}
)}
@@ -44,13 +48,13 @@ export default async function Items(props: {
|
- Name
+ {copy.list.columns.name}
|
- Items
+ {copy.list.columns.items}
|
- Actions
+ {copy.list.columns.actions}
|
@@ -68,12 +72,16 @@ export default async function Items(props: {
className="btn btn-primary"
variant="outline"
size="icon"
+ aria-label={copy.list.actions.edit}
>
{category._count.items === 0 && (
-
+
)}
diff --git a/src/components/common/pageheader.tsx b/src/components/common/pageheader.tsx
index c3b3c06..3157792 100644
--- a/src/components/common/pageheader.tsx
+++ b/src/components/common/pageheader.tsx
@@ -5,9 +5,12 @@ import Search from "@/components/common/search"
import { Button } from "@/components/ui/button"
import { getI18n } from "@/i18n/server"
+import { getPageHeaderAddLabel } from "./pageheader.utils"
+
interface PageHeaderProps {
title?: string
link?: string
+ addLabel?: string
search?: string
data: unknown[]
}
@@ -15,6 +18,7 @@ interface PageHeaderProps {
export default async function PageHeader({
title,
link,
+ addLabel,
search,
data,
}: PageHeaderProps) {
@@ -32,7 +36,7 @@ export default async function PageHeader({
diff --git a/src/components/common/pageheader.utils.ts b/src/components/common/pageheader.utils.ts
new file mode 100644
index 0000000..7d11e6b
--- /dev/null
+++ b/src/components/common/pageheader.utils.ts
@@ -0,0 +1,11 @@
+type PageHeaderAddLabelInput = {
+ addLabel?: string
+ title?: string
+}
+
+export function getPageHeaderAddLabel({
+ addLabel,
+ title,
+}: PageHeaderAddLabelInput) {
+ return addLabel ?? `Add ${title}`
+}
diff --git a/src/i18n/dictionaries/en.ts b/src/i18n/dictionaries/en.ts
index 171170f..db8e711 100644
--- a/src/i18n/dictionaries/en.ts
+++ b/src/i18n/dictionaries/en.ts
@@ -63,6 +63,57 @@ export const en = {
label: "Sign Out",
},
},
+ inventory: {
+ categories: {
+ list: {
+ title: "Categories",
+ addLabel: "Add Category",
+ empty: "No categories found.",
+ columns: {
+ name: "Name",
+ items: "Items",
+ actions: "Actions",
+ },
+ actions: {
+ edit: "Edit category",
+ delete: "Delete category",
+ },
+ },
+ new: {
+ title: "New Category",
+ },
+ edit: {
+ title: "Edit Category",
+ },
+ form: {
+ nameLabel: "Name",
+ namePlaceholder: "Category name",
+ createSubmit: "Create Category",
+ updateSubmit: "Update Category",
+ },
+ delete: {
+ label: "Delete category",
+ pending: "Deleting...",
+ unknownError: "Unknown error",
+ },
+ actions: {
+ createSuccess: "Category created successfully",
+ createFailure: "Failed to create category",
+ updateSuccess: "Category updated successfully",
+ updateFailure: "Failed to update category",
+ deleteSuccess: "Category deleted successfully",
+ deleteFailure: "Failed to delete category",
+ duplicateName: "Category already exists",
+ unchangedName: "Category name unchanged",
+ notFound: "Category not found",
+ hasItems: "Cannot delete category with items",
+ },
+ schema: {
+ nameRequired: "Name is required and must be at least 3 characters long",
+ idRequired: "ID is required",
+ },
+ },
+ },
login: {
title: "Sign In",
usernameLabel: "Username",
diff --git a/src/i18n/dictionaries/es.ts b/src/i18n/dictionaries/es.ts
index 113cf36..a73ccd2 100644
--- a/src/i18n/dictionaries/es.ts
+++ b/src/i18n/dictionaries/es.ts
@@ -65,6 +65,58 @@ export const es = {
label: "Cerrar sesión",
},
},
+ inventory: {
+ categories: {
+ list: {
+ title: "Categorías",
+ addLabel: "Agregar categoría",
+ empty: "No se encontraron categorías.",
+ columns: {
+ name: "Nombre",
+ items: "Artículos",
+ actions: "Acciones",
+ },
+ actions: {
+ edit: "Editar categoría",
+ delete: "Eliminar categoría",
+ },
+ },
+ new: {
+ title: "Nueva categoría",
+ },
+ edit: {
+ title: "Editar categoría",
+ },
+ form: {
+ nameLabel: "Nombre",
+ namePlaceholder: "Nombre de la categoría",
+ createSubmit: "Crear categoría",
+ updateSubmit: "Actualizar categoría",
+ },
+ delete: {
+ label: "Eliminar categoría",
+ pending: "Eliminando...",
+ unknownError: "Error desconocido",
+ },
+ actions: {
+ createSuccess: "Categoría creada correctamente",
+ createFailure: "Error al crear la categoría",
+ updateSuccess: "Categoría actualizada correctamente",
+ updateFailure: "Error al actualizar la categoría",
+ deleteSuccess: "Categoría eliminada correctamente",
+ deleteFailure: "Error al eliminar la categoría",
+ duplicateName: "La categoría ya existe",
+ unchangedName: "El nombre de la categoría no cambió",
+ notFound: "Categoría no encontrada",
+ hasItems: "No se puede eliminar una categoría con artículos",
+ },
+ schema: {
+ nameRequired:
+ "El nombre es obligatorio y debe tener al menos 3 caracteres",
+ idRequired: "El ID es obligatorio",
+ },
+ },
+ },
login: {
title: "Iniciar sesión",
usernameLabel: "Usuario",
diff --git a/tests/e2e/language-switcher.spec.ts b/tests/e2e/language-switcher.spec.ts
index f2f4996..4a52641 100644
--- a/tests/e2e/language-switcher.spec.ts
+++ b/tests/e2e/language-switcher.spec.ts
@@ -68,6 +68,36 @@ test.describe("language switcher", () => {
await expectLocaleCookie(page, "es")
})
+ test("renders inventory categories copy in the active locale", async ({
+ baseURL,
+ page,
+ }) => {
+ await signInAsAdmin(page, baseURL)
+ await setLocaleCookie(page, "es", baseURL)
+
+ await page.goto("/inventory/categories")
+
+ await expect(page.locator("html")).toHaveAttribute("lang", "es")
+ await expect(
+ page.getByRole("heading", { name: "Categorías" }),
+ ).toBeVisible()
+ await expect(
+ page.getByRole("link", { name: /Agregar categoría/ }),
+ ).toBeVisible()
+ await expect(page.getByText("No se encontraron categorías.")).toBeVisible()
+
+ await page.goto("/inventory/categories/new")
+
+ await expect(
+ page.getByRole("heading", { name: "Nueva categoría" }),
+ ).toBeVisible()
+ await expect(page.getByLabel("Nombre")).toBeVisible()
+ await expect(page.getByPlaceholder("Nombre de la categoría")).toBeVisible()
+ await expect(
+ page.getByRole("button", { name: "Crear categoría" }),
+ ).toBeVisible()
+ })
+
test("switches the authenticated dashboard language from the navbar", async ({
baseURL,
page,
diff --git a/tests/unit/components/common/pageheader.test.ts b/tests/unit/components/common/pageheader.test.ts
new file mode 100644
index 0000000..b9ec501
--- /dev/null
+++ b/tests/unit/components/common/pageheader.test.ts
@@ -0,0 +1,18 @@
+import { describe, expect, it } from "vitest"
+
+import { getPageHeaderAddLabel } from "@/components/common/pageheader.utils"
+
+describe("PageHeader", () => {
+ it("uses the explicit add label when provided", () => {
+ expect(
+ getPageHeaderAddLabel({
+ addLabel: "Agregar categoría",
+ title: "Categorías",
+ }),
+ ).toBe("Agregar categoría")
+ })
+
+ it("keeps the legacy fallback when no explicit add label is provided", () => {
+ expect(getPageHeaderAddLabel({ title: "Items" })).toBe("Add Items")
+ })
+})
diff --git a/tests/unit/i18n/dictionaries.test.ts b/tests/unit/i18n/dictionaries.test.ts
index 06027fb..24289b3 100644
--- a/tests/unit/i18n/dictionaries.test.ts
+++ b/tests/unit/i18n/dictionaries.test.ts
@@ -163,6 +163,109 @@ describe("i18n dictionaries", () => {
})
})
+ it("provides localized inventory category copy for English and Spanish", () => {
+ expect(getDictionary("en").inventory.categories).toEqual({
+ list: {
+ title: "Categories",
+ addLabel: "Add Category",
+ empty: "No categories found.",
+ columns: {
+ name: "Name",
+ items: "Items",
+ actions: "Actions",
+ },
+ actions: {
+ edit: "Edit category",
+ delete: "Delete category",
+ },
+ },
+ new: {
+ title: "New Category",
+ },
+ edit: {
+ title: "Edit Category",
+ },
+ form: {
+ nameLabel: "Name",
+ namePlaceholder: "Category name",
+ createSubmit: "Create Category",
+ updateSubmit: "Update Category",
+ },
+ delete: {
+ label: "Delete category",
+ pending: "Deleting...",
+ unknownError: "Unknown error",
+ },
+ actions: {
+ createSuccess: "Category created successfully",
+ createFailure: "Failed to create category",
+ updateSuccess: "Category updated successfully",
+ updateFailure: "Failed to update category",
+ deleteSuccess: "Category deleted successfully",
+ deleteFailure: "Failed to delete category",
+ duplicateName: "Category already exists",
+ unchangedName: "Category name unchanged",
+ notFound: "Category not found",
+ hasItems: "Cannot delete category with items",
+ },
+ schema: {
+ nameRequired: "Name is required and must be at least 3 characters long",
+ idRequired: "ID is required",
+ },
+ })
+
+ expect(getDictionary("es").inventory.categories).toEqual({
+ list: {
+ title: "Categorías",
+ addLabel: "Agregar categoría",
+ empty: "No se encontraron categorías.",
+ columns: {
+ name: "Nombre",
+ items: "Artículos",
+ actions: "Acciones",
+ },
+ actions: {
+ edit: "Editar categoría",
+ delete: "Eliminar categoría",
+ },
+ },
+ new: {
+ title: "Nueva categoría",
+ },
+ edit: {
+ title: "Editar categoría",
+ },
+ form: {
+ nameLabel: "Nombre",
+ namePlaceholder: "Nombre de la categoría",
+ createSubmit: "Crear categoría",
+ updateSubmit: "Actualizar categoría",
+ },
+ delete: {
+ label: "Eliminar categoría",
+ pending: "Eliminando...",
+ unknownError: "Error desconocido",
+ },
+ actions: {
+ createSuccess: "Categoría creada correctamente",
+ createFailure: "Error al crear la categoría",
+ updateSuccess: "Categoría actualizada correctamente",
+ updateFailure: "Error al actualizar la categoría",
+ deleteSuccess: "Categoría eliminada correctamente",
+ deleteFailure: "Error al eliminar la categoría",
+ duplicateName: "La categoría ya existe",
+ unchangedName: "El nombre de la categoría no cambió",
+ notFound: "Categoría no encontrada",
+ hasItems: "No se puede eliminar una categoría con artículos",
+ },
+ schema: {
+ nameRequired:
+ "El nombre es obligatorio y debe tener al menos 3 caracteres",
+ idRequired: "El ID es obligatorio",
+ },
+ })
+ })
+
it("keeps dashboard home dictionary keys aligned across locales", () => {
expect(getDictionary("en").dashboardHome).toEqual({
heading: "Dashboard",