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 category = await CategoryService.findById(categoryId)
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.categories
|
||||
|
||||
if (!category) {
|
||||
notFound()
|
||||
@@ -21,10 +22,11 @@ export default async function EditCategoryPage({
|
||||
return (
|
||||
<div className="flex flex-col 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>
|
||||
<EditCategoryForm
|
||||
category={category}
|
||||
formCopy={copy.form}
|
||||
submitButtonCopy={dictionary.common.submitButton}
|
||||
/>
|
||||
</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 { Button } from "@/components/ui/button"
|
||||
|
||||
import type { CategoryDeleteCopy } from "./category.copy"
|
||||
|
||||
export default function DeleteCategoryButton({
|
||||
categoryId,
|
||||
copy,
|
||||
}: {
|
||||
categoryId: string
|
||||
copy: CategoryDeleteCopy
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const [isPending, startTransition] = useTransition()
|
||||
@@ -28,7 +32,7 @@ export default function DeleteCategoryButton({
|
||||
toast.success(response.message)
|
||||
router.refresh()
|
||||
} else {
|
||||
toast.error(response.message ?? "Unknown error")
|
||||
toast.error(response.message ?? copy.unknownError)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -42,6 +46,7 @@ export default function DeleteCategoryButton({
|
||||
size="icon"
|
||||
variant="outline"
|
||||
disabled={isPending}
|
||||
aria-label={isPending ? copy.pending : copy.label}
|
||||
>
|
||||
<Trash />
|
||||
</Button>
|
||||
|
||||
@@ -14,12 +14,15 @@ import {
|
||||
updateCategorySchema,
|
||||
} from "@/schemas/category.schema"
|
||||
import type { CategorySummary } from "@/types"
|
||||
import type { CategoryFormCopy } from "./category.copy"
|
||||
|
||||
export default function EditCategoryForm({
|
||||
category,
|
||||
formCopy,
|
||||
submitButtonCopy,
|
||||
}: {
|
||||
category: CategorySummary
|
||||
formCopy: CategoryFormCopy
|
||||
submitButtonCopy: SubmitButtonCopy
|
||||
}) {
|
||||
const router = useRouter()
|
||||
@@ -64,12 +67,12 @@ export default function EditCategoryForm({
|
||||
<input type="hidden" {...register("id")} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="name" className="mb-2 block text-lg">
|
||||
Name
|
||||
{formCopy.nameLabel}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
placeholder="Category name"
|
||||
placeholder={formCopy.namePlaceholder}
|
||||
{...register("name")}
|
||||
className={`w-full rounded-lg border px-4 py-2 ${
|
||||
errors.name ? "border-error" : ""
|
||||
@@ -82,7 +85,7 @@ export default function EditCategoryForm({
|
||||
isSubmitting={isSubmitting}
|
||||
isSubmitSuccessful={isSubmitSuccessful}
|
||||
>
|
||||
Update Category
|
||||
{formCopy.updateSubmit}
|
||||
</SubmitButton>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -13,10 +13,13 @@ import {
|
||||
type CreateCategoryFormType,
|
||||
createCategorySchema,
|
||||
} from "@/schemas/category.schema"
|
||||
import type { CategoryFormCopy } from "./category.copy"
|
||||
|
||||
export default function NewCategoryForm({
|
||||
formCopy,
|
||||
submitButtonCopy,
|
||||
}: {
|
||||
formCopy: CategoryFormCopy
|
||||
submitButtonCopy: SubmitButtonCopy
|
||||
}) {
|
||||
const router = useRouter()
|
||||
@@ -56,12 +59,12 @@ export default function NewCategoryForm({
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="name" className="mb-2 block text-lg">
|
||||
Name
|
||||
{formCopy.nameLabel}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
placeholder="Category name"
|
||||
placeholder={formCopy.namePlaceholder}
|
||||
{...register("name")}
|
||||
className={`w-full rounded-lg border px-4 py-2 ${
|
||||
errors.name ? "border-error" : ""
|
||||
@@ -74,7 +77,7 @@ export default function NewCategoryForm({
|
||||
isSubmitting={isSubmitting}
|
||||
isSubmitSuccessful={isSubmitSuccessful}
|
||||
>
|
||||
Create Category
|
||||
{formCopy.createSubmit}
|
||||
</SubmitButton>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex flex-col 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>
|
||||
<NewCategoryForm submitButtonCopy={dictionary.common.submitButton} />
|
||||
<NewCategoryForm
|
||||
formCopy={copy.form}
|
||||
submitButtonCopy={dictionary.common.submitButton}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex flex-col gap-4">
|
||||
<PageHeader
|
||||
title="Categories"
|
||||
title={copy.list.title}
|
||||
addLabel={copy.list.addLabel}
|
||||
link="/inventory/categories/new"
|
||||
data={categories}
|
||||
/>
|
||||
{categories.length === 0 && currentPage === 1 && (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
No Categories found.
|
||||
{copy.list.empty}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -44,13 +48,13 @@ export default async function Items(props: {
|
||||
<thead className="border-b">
|
||||
<tr>
|
||||
<th scope="col" className="p-4">
|
||||
Name
|
||||
{copy.list.columns.name}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
Items
|
||||
{copy.list.columns.items}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
Actions
|
||||
{copy.list.columns.actions}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -68,12 +72,16 @@ export default async function Items(props: {
|
||||
className="btn btn-primary"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
aria-label={copy.list.actions.edit}
|
||||
>
|
||||
<Pencil />
|
||||
</Button>
|
||||
</Link>
|
||||
{category._count.items === 0 && (
|
||||
<DeleteCategoryButton categoryId={category.id} />
|
||||
<DeleteCategoryButton
|
||||
categoryId={category.id}
|
||||
copy={copy.delete}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user