feat(i18n): localize inventory assignments UI
This commit is contained in:
@@ -18,9 +18,10 @@ export default async function EditAssignmentPage({
|
||||
const items = await ItemService.findAllWithStock()
|
||||
const assets = await AssetService.findAll()
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.assignments
|
||||
|
||||
if (!assignment) {
|
||||
return <div>Assignment not found</div>
|
||||
return <div>{copy.edit.notFound}</div>
|
||||
}
|
||||
|
||||
let assignmentItem: Item = {} as Item
|
||||
@@ -31,12 +32,16 @@ export default async function EditAssignmentPage({
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<h1 className="text-2xl font-bold">{copy.edit.title}</h1>
|
||||
</div>
|
||||
<AssignmentForm
|
||||
recipients={recipients}
|
||||
items={items}
|
||||
assets={assets}
|
||||
initialData={assignment as UpdateAssignmentFormType}
|
||||
formCopy={copy.form}
|
||||
submitButtonCopy={dictionary.common.submitButton}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -9,17 +9,21 @@ import {
|
||||
SubmitButton,
|
||||
type SubmitButtonCopy,
|
||||
} from "@/components/forms/submitButton"
|
||||
import type { Dictionary } from "@/i18n/dictionaries"
|
||||
import {
|
||||
type UpdateAssignmentFormType,
|
||||
updateAssignmentSchema,
|
||||
} from "@/schemas/assignment.schema"
|
||||
import type { Asset, Item, Recipient } from "@/types"
|
||||
|
||||
type AssignmentFormCopy = Dictionary["inventory"]["assignments"]["form"]
|
||||
|
||||
interface Props {
|
||||
recipients: Recipient[]
|
||||
items: Item[]
|
||||
assets: Asset[]
|
||||
initialData: UpdateAssignmentFormType
|
||||
formCopy: AssignmentFormCopy
|
||||
submitButtonCopy: SubmitButtonCopy
|
||||
}
|
||||
|
||||
@@ -28,6 +32,7 @@ export default function EditAssignmentForm({
|
||||
items,
|
||||
assets,
|
||||
initialData,
|
||||
formCopy,
|
||||
submitButtonCopy,
|
||||
}: Props) {
|
||||
const router = useRouter()
|
||||
@@ -71,7 +76,7 @@ export default function EditAssignmentForm({
|
||||
<input type="hidden" {...register("id")} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="recipientId" className="mb-2 block text-lg">
|
||||
Recipient
|
||||
{formCopy.recipientLabel}
|
||||
</label>
|
||||
<select
|
||||
id="recipientId"
|
||||
@@ -92,7 +97,7 @@ export default function EditAssignmentForm({
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="itemId" className="mb-2 block text-lg">
|
||||
Item
|
||||
{formCopy.itemLabel}
|
||||
</label>
|
||||
<select
|
||||
id="itemId"
|
||||
@@ -111,7 +116,7 @@ export default function EditAssignmentForm({
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="assetId" className="mb-2 block text-lg">
|
||||
Asset
|
||||
{formCopy.assetLabel}
|
||||
</label>
|
||||
<select
|
||||
id="assetId"
|
||||
@@ -120,7 +125,7 @@ export default function EditAssignmentForm({
|
||||
errors.assetId ? "border-error" : ""
|
||||
}`}
|
||||
>
|
||||
<option value="">Select an asset</option>
|
||||
<option value="">{formCopy.assetPlaceholder}</option>
|
||||
{itemId
|
||||
? assets.map((asset) => (
|
||||
<option key={asset.id} value={asset.id}>
|
||||
@@ -135,7 +140,7 @@ export default function EditAssignmentForm({
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="quantity" className="mb-2 block text-lg">
|
||||
Quantity
|
||||
{formCopy.quantityLabel}
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
@@ -143,6 +148,7 @@ export default function EditAssignmentForm({
|
||||
disabled={!itemId || assets.length > 0}
|
||||
min={1}
|
||||
max={itemId ? items.find((item) => item.id === itemId)?.stock : 0}
|
||||
placeholder={formCopy.quantityPlaceholder}
|
||||
defaultValue={1}
|
||||
{...register("quantity")}
|
||||
className={`w-full rounded-lg border px-4 py-2 ${
|
||||
@@ -159,7 +165,7 @@ export default function EditAssignmentForm({
|
||||
isSubmitSuccessful={isSubmitSuccessful}
|
||||
disabled={!itemId || (assets.length > 0 && !assetId)}
|
||||
>
|
||||
Update Assignment
|
||||
{formCopy.updateSubmit}
|
||||
</SubmitButton>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -10,16 +10,20 @@ import {
|
||||
SubmitButton,
|
||||
type SubmitButtonCopy,
|
||||
} from "@/components/forms/submitButton"
|
||||
import type { Dictionary } from "@/i18n/dictionaries"
|
||||
import {
|
||||
type CreateAssignmentFormType,
|
||||
createAssignmentSchema,
|
||||
} from "@/schemas/assignment.schema"
|
||||
import type { Asset, Item, Recipient } from "@/types"
|
||||
|
||||
type AssignmentFormCopy = Dictionary["inventory"]["assignments"]["form"]
|
||||
|
||||
interface Props {
|
||||
recipients: Recipient[]
|
||||
items: Item[]
|
||||
assets: Asset[]
|
||||
formCopy: AssignmentFormCopy
|
||||
submitButtonCopy: SubmitButtonCopy
|
||||
}
|
||||
|
||||
@@ -27,6 +31,7 @@ export default function CreateAssignmentForm({
|
||||
recipients,
|
||||
items,
|
||||
assets,
|
||||
formCopy,
|
||||
submitButtonCopy,
|
||||
}: Props) {
|
||||
const router = useRouter()
|
||||
@@ -69,7 +74,7 @@ export default function CreateAssignmentForm({
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="recipientId" className="mb-2 block text-lg">
|
||||
Recipient
|
||||
{formCopy.recipientLabel}
|
||||
</label>
|
||||
<select
|
||||
id="recipientId"
|
||||
@@ -78,7 +83,7 @@ export default function CreateAssignmentForm({
|
||||
errors.recipientId ? "border-error" : ""
|
||||
}`}
|
||||
>
|
||||
<option value="">Select a recipient</option>
|
||||
<option value="">{formCopy.recipientPlaceholder}</option>
|
||||
{recipients.map((recipient) => (
|
||||
<option key={recipient.id} value={recipient.id}>
|
||||
{recipient.firstName} {recipient.lastName}
|
||||
@@ -91,7 +96,7 @@ export default function CreateAssignmentForm({
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="itemId" className="mb-2 block text-lg">
|
||||
Item
|
||||
{formCopy.itemLabel}
|
||||
</label>
|
||||
<select
|
||||
id="itemId"
|
||||
@@ -100,7 +105,7 @@ export default function CreateAssignmentForm({
|
||||
errors.itemId ? "border-error" : ""
|
||||
}`}
|
||||
>
|
||||
<option value="">Select an item</option>
|
||||
<option value="">{formCopy.itemPlaceholder}</option>
|
||||
{items.map((item) => (
|
||||
<option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
@@ -112,7 +117,7 @@ export default function CreateAssignmentForm({
|
||||
{itemId && itemAssets.length !== 0 && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="assetId" className="mb-2 block text-lg">
|
||||
Asset
|
||||
{formCopy.assetLabel}
|
||||
</label>
|
||||
<select
|
||||
id="assetId"
|
||||
@@ -124,7 +129,7 @@ export default function CreateAssignmentForm({
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<option value="">Select an asset</option>
|
||||
<option value="">{formCopy.assetPlaceholder}</option>
|
||||
{itemId
|
||||
? itemAssets.map((asset) => (
|
||||
<option key={asset.id} value={asset.id}>
|
||||
@@ -140,7 +145,7 @@ export default function CreateAssignmentForm({
|
||||
)}
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="quantity" className="mb-2 block text-lg">
|
||||
Quantity
|
||||
{formCopy.quantityLabel}
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
@@ -148,6 +153,7 @@ export default function CreateAssignmentForm({
|
||||
disabled={!itemId || itemAssets.length > 0}
|
||||
min={1}
|
||||
max={itemId ? items.find((item) => item.id === itemId)?.stock : 0}
|
||||
placeholder={formCopy.quantityPlaceholder}
|
||||
defaultValue={1}
|
||||
{...register("quantity")}
|
||||
className={`w-full rounded-lg border px-4 py-2 ${
|
||||
@@ -166,7 +172,7 @@ export default function CreateAssignmentForm({
|
||||
isSubmitSuccessful={isSubmitSuccessful}
|
||||
disabled={!itemId || (itemAssets.length > 0 && !assetId)}
|
||||
>
|
||||
Create Assignment
|
||||
{formCopy.createSubmit}
|
||||
</SubmitButton>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -10,8 +10,10 @@ import type { ReturnAssignmentFormType } from "@/schemas/assignment.schema"
|
||||
|
||||
export default function ReturnButton({
|
||||
assignmentId,
|
||||
ariaLabel,
|
||||
}: {
|
||||
assignmentId: string
|
||||
ariaLabel: string
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const [isPending, startTransition] = useTransition()
|
||||
@@ -42,6 +44,7 @@ export default function ReturnButton({
|
||||
className="btn btn-error"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
aria-label={ariaLabel}
|
||||
disabled={isPending}
|
||||
>
|
||||
<ArrowLeft />
|
||||
|
||||
@@ -10,13 +10,20 @@ export default async function NewAssignmentPage() {
|
||||
const items = await ItemService.findAllWithStock()
|
||||
const assets = await AssetService.findAllAvailable()
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.assignments
|
||||
|
||||
return (
|
||||
<AssignmentForm
|
||||
recipients={recipients}
|
||||
items={items}
|
||||
assets={assets}
|
||||
submitButtonCopy={dictionary.common.submitButton}
|
||||
/>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<h1 className="text-2xl font-bold">{copy.new.title}</h1>
|
||||
</div>
|
||||
<AssignmentForm
|
||||
recipients={recipients}
|
||||
items={items}
|
||||
assets={assets}
|
||||
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 { AssignmentService } from "@/services/assignment.service"
|
||||
|
||||
import ReturnButton from "./_components/return.button"
|
||||
@@ -22,35 +23,38 @@ export default async function AssignmentsPage(props: {
|
||||
page: currentPage,
|
||||
search,
|
||||
})
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.assignments
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<PageHeader
|
||||
title="Assignments"
|
||||
title={copy.list.title}
|
||||
link="/assignments/new"
|
||||
search={search}
|
||||
data={assignments}
|
||||
addLabel={copy.list.addLabel}
|
||||
/>
|
||||
{assignments.length === 0 && <div>No assignments found</div>}
|
||||
{assignments.length === 0 && <div>{copy.list.empty}</div>}
|
||||
{assignments.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">
|
||||
Recipient
|
||||
{copy.list.columns.recipient}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
Item
|
||||
{copy.list.columns.item}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
Serial Number
|
||||
{copy.list.columns.serialNumber}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
Quantity
|
||||
{copy.list.columns.quantity}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
Actions
|
||||
{copy.list.columns.actions}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -75,7 +79,8 @@ export default async function AssignmentsPage(props: {
|
||||
</Link>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
{assignment?.asset?.serialNumber || "N/A"}
|
||||
{assignment?.asset?.serialNumber ||
|
||||
copy.fallback.missingValue}
|
||||
</td>
|
||||
<td className="p-4">{assignment?.quantity}</td>
|
||||
<td className="p-4">
|
||||
@@ -84,11 +89,17 @@ export default async function AssignmentsPage(props: {
|
||||
href={`/assignments/${assignment.id}/edit`}
|
||||
passHref
|
||||
>
|
||||
<Button variant="outline">
|
||||
<Button
|
||||
variant="outline"
|
||||
aria-label={copy.list.actions.edit}
|
||||
>
|
||||
<Pencil />
|
||||
</Button>
|
||||
</Link>
|
||||
<ReturnButton assignmentId={assignment.id} />
|
||||
<ReturnButton
|
||||
assignmentId={assignment.id}
|
||||
ariaLabel={copy.list.actions.return}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -13,6 +13,7 @@ export default async function RecipientInfoPage({
|
||||
const { recipientId } = await params
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.recipients
|
||||
const assignmentCopy = dictionary.inventory.assignments
|
||||
const recipient = await RecipientService.findById(recipientId)
|
||||
const assignments = await AssignmentService.findAllByRecipient(recipientId)
|
||||
|
||||
@@ -62,7 +63,7 @@ export default async function RecipientInfoPage({
|
||||
{assignments.length > 0 && (
|
||||
<Card className="rounded-sm shadow-none">
|
||||
<CardHeader>
|
||||
<CardTitle>Assignments</CardTitle>
|
||||
<CardTitle>{assignmentCopy.list.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-y-2 text-sm">
|
||||
|
||||
@@ -255,6 +255,46 @@ export const en = {
|
||||
invalidUpdateStatus: "Invalid status",
|
||||
},
|
||||
},
|
||||
assignments: {
|
||||
list: {
|
||||
title: "Assignments",
|
||||
addLabel: "Add Assignment",
|
||||
empty: "No assignments found.",
|
||||
columns: {
|
||||
recipient: "Recipient",
|
||||
item: "Item",
|
||||
serialNumber: "Serial Number",
|
||||
quantity: "Quantity",
|
||||
actions: "Actions",
|
||||
},
|
||||
actions: {
|
||||
edit: "Edit assignment",
|
||||
return: "Return assignment",
|
||||
},
|
||||
},
|
||||
new: {
|
||||
title: "New Assignment",
|
||||
},
|
||||
edit: {
|
||||
title: "Edit Assignment",
|
||||
notFound: "Assignment not found",
|
||||
},
|
||||
form: {
|
||||
recipientLabel: "Recipient",
|
||||
recipientPlaceholder: "Select a recipient",
|
||||
itemLabel: "Item",
|
||||
itemPlaceholder: "Select an item",
|
||||
assetLabel: "Asset",
|
||||
assetPlaceholder: "Select an asset",
|
||||
quantityLabel: "Quantity",
|
||||
quantityPlaceholder: "1",
|
||||
createSubmit: "Create Assignment",
|
||||
updateSubmit: "Update Assignment",
|
||||
},
|
||||
fallback: {
|
||||
missingValue: "N/A",
|
||||
},
|
||||
},
|
||||
recipients: {
|
||||
list: {
|
||||
title: "Recipients",
|
||||
|
||||
@@ -259,6 +259,46 @@ export const es = {
|
||||
invalidUpdateStatus: "Estado inválido",
|
||||
},
|
||||
},
|
||||
assignments: {
|
||||
list: {
|
||||
title: "Asignaciones",
|
||||
addLabel: "Agregar asignación",
|
||||
empty: "No se encontraron asignaciones.",
|
||||
columns: {
|
||||
recipient: "Destinatario",
|
||||
item: "Artículo",
|
||||
serialNumber: "Número de serie",
|
||||
quantity: "Cantidad",
|
||||
actions: "Acciones",
|
||||
},
|
||||
actions: {
|
||||
edit: "Editar asignación",
|
||||
return: "Devolver asignación",
|
||||
},
|
||||
},
|
||||
new: {
|
||||
title: "Nueva asignación",
|
||||
},
|
||||
edit: {
|
||||
title: "Editar asignación",
|
||||
notFound: "Asignación no encontrada",
|
||||
},
|
||||
form: {
|
||||
recipientLabel: "Destinatario",
|
||||
recipientPlaceholder: "Selecciona un destinatario",
|
||||
itemLabel: "Artículo",
|
||||
itemPlaceholder: "Selecciona un artículo",
|
||||
assetLabel: "Activo",
|
||||
assetPlaceholder: "Selecciona un activo",
|
||||
quantityLabel: "Cantidad",
|
||||
quantityPlaceholder: "1",
|
||||
createSubmit: "Crear asignación",
|
||||
updateSubmit: "Actualizar asignación",
|
||||
},
|
||||
fallback: {
|
||||
missingValue: "No disponible",
|
||||
},
|
||||
},
|
||||
recipients: {
|
||||
list: {
|
||||
title: "Destinatarios",
|
||||
|
||||
Reference in New Issue
Block a user