feat(i18n): localize inventory categories UI
This commit is contained in:
@@ -13,6 +13,7 @@ export default async function EditCategoryPage({
|
|||||||
const { categoryId } = await params
|
const { categoryId } = await params
|
||||||
const category = await CategoryService.findById(categoryId)
|
const category = await CategoryService.findById(categoryId)
|
||||||
const { dictionary } = await getI18n()
|
const { dictionary } = await getI18n()
|
||||||
|
const copy = dictionary.inventory.categories
|
||||||
|
|
||||||
if (!category) {
|
if (!category) {
|
||||||
notFound()
|
notFound()
|
||||||
@@ -21,10 +22,11 @@ export default async function EditCategoryPage({
|
|||||||
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 Category</h1>
|
<h1 className="text-2xl font-bold">{copy.edit.title}</h1>
|
||||||
</div>
|
</div>
|
||||||
<EditCategoryForm
|
<EditCategoryForm
|
||||||
category={category}
|
category={category}
|
||||||
|
formCopy={copy.form}
|
||||||
submitButtonCopy={dictionary.common.submitButton}
|
submitButtonCopy={dictionary.common.submitButton}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import type { Dictionary } from "@/i18n/dictionaries"
|
||||||
|
|
||||||
|
export type CategoryFormCopy = Dictionary["inventory"]["categories"]["form"]
|
||||||
|
export type CategoryDeleteCopy = Dictionary["inventory"]["categories"]["delete"]
|
||||||
@@ -7,10 +7,14 @@ import { toast } from "sonner"
|
|||||||
import { deleteCategoryAction } from "@/actions/category.actions"
|
import { deleteCategoryAction } from "@/actions/category.actions"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
|
import type { CategoryDeleteCopy } from "./category.copy"
|
||||||
|
|
||||||
export default function DeleteCategoryButton({
|
export default function DeleteCategoryButton({
|
||||||
categoryId,
|
categoryId,
|
||||||
|
copy,
|
||||||
}: {
|
}: {
|
||||||
categoryId: string
|
categoryId: string
|
||||||
|
copy: CategoryDeleteCopy
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [isPending, startTransition] = useTransition()
|
const [isPending, startTransition] = useTransition()
|
||||||
@@ -28,7 +32,7 @@ export default function DeleteCategoryButton({
|
|||||||
toast.success(response.message)
|
toast.success(response.message)
|
||||||
router.refresh()
|
router.refresh()
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message ?? "Unknown error")
|
toast.error(response.message ?? copy.unknownError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -42,6 +46,7 @@ export default function DeleteCategoryButton({
|
|||||||
size="icon"
|
size="icon"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
|
aria-label={isPending ? copy.pending : copy.label}
|
||||||
>
|
>
|
||||||
<Trash />
|
<Trash />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ import {
|
|||||||
updateCategorySchema,
|
updateCategorySchema,
|
||||||
} from "@/schemas/category.schema"
|
} from "@/schemas/category.schema"
|
||||||
import type { CategorySummary } from "@/types"
|
import type { CategorySummary } from "@/types"
|
||||||
|
import type { CategoryFormCopy } from "./category.copy"
|
||||||
|
|
||||||
export default function EditCategoryForm({
|
export default function EditCategoryForm({
|
||||||
category,
|
category,
|
||||||
|
formCopy,
|
||||||
submitButtonCopy,
|
submitButtonCopy,
|
||||||
}: {
|
}: {
|
||||||
category: CategorySummary
|
category: CategorySummary
|
||||||
|
formCopy: CategoryFormCopy
|
||||||
submitButtonCopy: SubmitButtonCopy
|
submitButtonCopy: SubmitButtonCopy
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -64,12 +67,12 @@ export default function EditCategoryForm({
|
|||||||
<input type="hidden" {...register("id")} />
|
<input type="hidden" {...register("id")} />
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor="name" className="mb-2 block text-lg">
|
<label htmlFor="name" className="mb-2 block text-lg">
|
||||||
Name
|
{formCopy.nameLabel}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="name"
|
id="name"
|
||||||
placeholder="Category name"
|
placeholder={formCopy.namePlaceholder}
|
||||||
{...register("name")}
|
{...register("name")}
|
||||||
className={`w-full rounded-lg border px-4 py-2 ${
|
className={`w-full rounded-lg border px-4 py-2 ${
|
||||||
errors.name ? "border-error" : ""
|
errors.name ? "border-error" : ""
|
||||||
@@ -82,7 +85,7 @@ export default function EditCategoryForm({
|
|||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSubmitting}
|
||||||
isSubmitSuccessful={isSubmitSuccessful}
|
isSubmitSuccessful={isSubmitSuccessful}
|
||||||
>
|
>
|
||||||
Update Category
|
{formCopy.updateSubmit}
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ import {
|
|||||||
type CreateCategoryFormType,
|
type CreateCategoryFormType,
|
||||||
createCategorySchema,
|
createCategorySchema,
|
||||||
} from "@/schemas/category.schema"
|
} from "@/schemas/category.schema"
|
||||||
|
import type { CategoryFormCopy } from "./category.copy"
|
||||||
|
|
||||||
export default function NewCategoryForm({
|
export default function NewCategoryForm({
|
||||||
|
formCopy,
|
||||||
submitButtonCopy,
|
submitButtonCopy,
|
||||||
}: {
|
}: {
|
||||||
|
formCopy: CategoryFormCopy
|
||||||
submitButtonCopy: SubmitButtonCopy
|
submitButtonCopy: SubmitButtonCopy
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -56,12 +59,12 @@ export default function NewCategoryForm({
|
|||||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor="name" className="mb-2 block text-lg">
|
<label htmlFor="name" className="mb-2 block text-lg">
|
||||||
Name
|
{formCopy.nameLabel}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="name"
|
id="name"
|
||||||
placeholder="Category name"
|
placeholder={formCopy.namePlaceholder}
|
||||||
{...register("name")}
|
{...register("name")}
|
||||||
className={`w-full rounded-lg border px-4 py-2 ${
|
className={`w-full rounded-lg border px-4 py-2 ${
|
||||||
errors.name ? "border-error" : ""
|
errors.name ? "border-error" : ""
|
||||||
@@ -74,7 +77,7 @@ export default function NewCategoryForm({
|
|||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSubmitting}
|
||||||
isSubmitSuccessful={isSubmitSuccessful}
|
isSubmitSuccessful={isSubmitSuccessful}
|
||||||
>
|
>
|
||||||
Create Category
|
{formCopy.createSubmit}
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,13 +4,17 @@ import NewCategoryForm from "../_components/new.category.form"
|
|||||||
|
|
||||||
export default async function NewCategoryPage() {
|
export default async function NewCategoryPage() {
|
||||||
const { dictionary } = await getI18n()
|
const { dictionary } = await getI18n()
|
||||||
|
const copy = dictionary.inventory.categories
|
||||||
|
|
||||||
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 Category</h1>
|
<h1 className="text-2xl font-bold">{copy.new.title}</h1>
|
||||||
</div>
|
</div>
|
||||||
<NewCategoryForm submitButtonCopy={dictionary.common.submitButton} />
|
<NewCategoryForm
|
||||||
|
formCopy={copy.form}
|
||||||
|
submitButtonCopy={dictionary.common.submitButton}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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 { CategoryService } from "@/services/category.service"
|
import { CategoryService } from "@/services/category.service"
|
||||||
|
|
||||||
import DeleteCategoryButton from "./_components/delete.category.button"
|
import DeleteCategoryButton from "./_components/delete.category.button"
|
||||||
@@ -23,18 +24,21 @@ export default async function Items(props: {
|
|||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
search,
|
search,
|
||||||
})
|
})
|
||||||
|
const { dictionary } = await getI18n()
|
||||||
|
const copy = dictionary.inventory.categories
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Categories"
|
title={copy.list.title}
|
||||||
|
addLabel={copy.list.addLabel}
|
||||||
link="/inventory/categories/new"
|
link="/inventory/categories/new"
|
||||||
data={categories}
|
data={categories}
|
||||||
/>
|
/>
|
||||||
{categories.length === 0 && currentPage === 1 && (
|
{categories.length === 0 && currentPage === 1 && (
|
||||||
<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">
|
||||||
No Categories found.
|
{copy.list.empty}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -44,13 +48,13 @@ export default async function Items(props: {
|
|||||||
<thead className="border-b">
|
<thead className="border-b">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="p-4">
|
<th scope="col" className="p-4">
|
||||||
Name
|
{copy.list.columns.name}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" className="p-4">
|
<th scope="col" className="p-4">
|
||||||
Items
|
{copy.list.columns.items}
|
||||||
</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>
|
||||||
@@ -68,12 +72,16 @@ export default async function Items(props: {
|
|||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
|
aria-label={copy.list.actions.edit}
|
||||||
>
|
>
|
||||||
<Pencil />
|
<Pencil />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
{category._count.items === 0 && (
|
{category._count.items === 0 && (
|
||||||
<DeleteCategoryButton categoryId={category.id} />
|
<DeleteCategoryButton
|
||||||
|
categoryId={category.id}
|
||||||
|
copy={copy.delete}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import Search from "@/components/common/search"
|
|||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { getI18n } from "@/i18n/server"
|
import { getI18n } from "@/i18n/server"
|
||||||
|
|
||||||
|
import { getPageHeaderAddLabel } from "./pageheader.utils"
|
||||||
|
|
||||||
interface PageHeaderProps {
|
interface PageHeaderProps {
|
||||||
title?: string
|
title?: string
|
||||||
link?: string
|
link?: string
|
||||||
|
addLabel?: string
|
||||||
search?: string
|
search?: string
|
||||||
data: unknown[]
|
data: unknown[]
|
||||||
}
|
}
|
||||||
@@ -15,6 +18,7 @@ interface PageHeaderProps {
|
|||||||
export default async function PageHeader({
|
export default async function PageHeader({
|
||||||
title,
|
title,
|
||||||
link,
|
link,
|
||||||
|
addLabel,
|
||||||
search,
|
search,
|
||||||
data,
|
data,
|
||||||
}: PageHeaderProps) {
|
}: PageHeaderProps) {
|
||||||
@@ -32,7 +36,7 @@ export default async function PageHeader({
|
|||||||
<div className="justify-end md:ml-auto md:flex">
|
<div className="justify-end md:ml-auto md:flex">
|
||||||
<Link href={link} passHref>
|
<Link href={link} passHref>
|
||||||
<Button className="btn btn-primary">
|
<Button className="btn btn-primary">
|
||||||
Add {title} <Plus />
|
{getPageHeaderAddLabel({ addLabel, title })} <Plus />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
type PageHeaderAddLabelInput = {
|
||||||
|
addLabel?: string
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPageHeaderAddLabel({
|
||||||
|
addLabel,
|
||||||
|
title,
|
||||||
|
}: PageHeaderAddLabelInput) {
|
||||||
|
return addLabel ?? `Add ${title}`
|
||||||
|
}
|
||||||
@@ -63,6 +63,57 @@ export const en = {
|
|||||||
label: "Sign Out",
|
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: {
|
login: {
|
||||||
title: "Sign In",
|
title: "Sign In",
|
||||||
usernameLabel: "Username",
|
usernameLabel: "Username",
|
||||||
|
|||||||
@@ -65,6 +65,58 @@ export const es = {
|
|||||||
label: "Cerrar sesión",
|
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: {
|
login: {
|
||||||
title: "Iniciar sesión",
|
title: "Iniciar sesión",
|
||||||
usernameLabel: "Usuario",
|
usernameLabel: "Usuario",
|
||||||
|
|||||||
@@ -68,6 +68,36 @@ test.describe("language switcher", () => {
|
|||||||
await expectLocaleCookie(page, "es")
|
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 ({
|
test("switches the authenticated dashboard language from the navbar", async ({
|
||||||
baseURL,
|
baseURL,
|
||||||
page,
|
page,
|
||||||
|
|||||||
@@ -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")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -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", () => {
|
it("keeps dashboard home dictionary keys aligned across locales", () => {
|
||||||
expect(getDictionary("en").dashboardHome).toEqual({
|
expect(getDictionary("en").dashboardHome).toEqual({
|
||||||
heading: "Dashboard",
|
heading: "Dashboard",
|
||||||
|
|||||||
Reference in New Issue
Block a user