feat(i18n): localize movement UI

This commit is contained in:
2026-06-14 01:20:23 +02:00
parent 7d5ab64653
commit f62cd6fb37
8 changed files with 270 additions and 17 deletions
@@ -1,3 +1,4 @@
import { formatMovementType } from "@/app/(dashboard)/movements/movement.copy"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { getI18n } from "@/i18n/server"
import { AssetService } from "@/services/asset.service"
@@ -15,6 +16,7 @@ export default async function ItemPage({
const movements = await MovementService.findAllByItemId(itemId)
const { dictionary } = await getI18n()
const copy = dictionary.inventory.items.detail
const movementCopy = dictionary.inventory.movements
if (!item) {
return <div>{copy.notFound}</div>
@@ -77,7 +79,7 @@ export default async function ItemPage({
{movements?.length > 0 && (
<Card className="rounded-sm shadow-none">
<CardHeader>
<CardTitle>Movements</CardTitle>
<CardTitle>{movementCopy.snippet.title}</CardTitle>
</CardHeader>
<CardContent>
{movements.map((movement) => (
@@ -86,11 +88,21 @@ export default async function ItemPage({
className="grid grid-cols-2 gap-x-8 gap-y-2 text-sm"
>
<div className="flex justify-between">
<span className="text-gray-600">Type</span>
<span>{movement.type}</span>
<span className="text-gray-600">
{movementCopy.snippet.labels.type}
</span>
<span>
{formatMovementType(
movement.type,
movementCopy.types,
movementCopy.fallback,
)}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">Quantity</span>
<span className="text-gray-600">
{movementCopy.snippet.labels.quantity}
</span>
<span>{movement.quantity}</span>
</div>
</div>
@@ -0,0 +1,15 @@
import type { Dictionary } from "@/i18n/dictionaries"
export type MovementTypeCopy = Dictionary["inventory"]["movements"]["types"]
export type MovementFallbackCopy =
Dictionary["inventory"]["movements"]["fallback"]
export function formatMovementType(
type: string,
typeCopy: MovementTypeCopy,
fallbackCopy: MovementFallbackCopy,
) {
return type in typeCopy
? typeCopy[type as keyof MovementTypeCopy]
: fallbackCopy.unknownType
}
+28 -13
View File
@@ -1,7 +1,10 @@
import PaginationButtons from "@/components/common/pagination"
import { getI18n } from "@/i18n/server"
import { formatDate } from "@/lib/utils"
import { MovementService } from "@/services/movement.service"
import { formatMovementType } from "./movement.copy"
export default async function MovementsPage(props: {
searchParams?: Promise<{
page?: string
@@ -13,50 +16,62 @@ export default async function MovementsPage(props: {
page: currentPage,
pageSize: 12,
})
const { dictionary } = await getI18n()
const copy = dictionary.inventory.movements
return (
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between gap-4">
<h1 className="text-2xl font-bold">Movements</h1>
<h1 className="text-2xl font-bold">{copy.list.title}</h1>
</div>
{movements.length === 0 && <div>No movements found</div>}
{movements.length === 0 && <div>{copy.list.empty}</div>}
{movements.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">
Type
{copy.list.columns.type}
</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">
Recipient
{copy.list.columns.recipient}
</th>
<th scope="col" className="p-4">
Date
{copy.list.columns.date}
</th>
</tr>
</thead>
<tbody>
{movements.map((movement) => (
<tr key={movement.id} className="border-b">
<td className="p-4">{movement.type}</td>
<td className="p-4">{movement?.item?.name}</td>
<td className="p-4">
{movement?.asset?.serialNumber || "-"}
{formatMovementType(
movement.type,
copy.types,
copy.fallback,
)}
</td>
<td className="p-4">
{movement?.item?.name || copy.fallback.missingValue}
</td>
<td className="p-4">
{movement?.asset?.serialNumber ||
copy.fallback.missingValue}
</td>
<td className="p-4">{movement.quantity}</td>
<td className="p-4">
{movement?.recipient?.firstName || "-"}{" "}
{movement?.recipient?.lastName || "-"}
{movement?.recipient
? `${movement.recipient.firstName} ${movement.recipient.lastName}`
: copy.fallback.missingValue}
</td>
<td className="p-4">{formatDate(movement.createdAt)}</td>
</tr>
+33
View File
@@ -255,6 +255,39 @@ export const en = {
invalidUpdateStatus: "Invalid status",
},
},
movements: {
list: {
title: "Movements",
empty: "No movements found",
columns: {
type: "Type",
item: "Item",
serialNumber: "Serial Number",
quantity: "Quantity",
recipient: "Recipient",
date: "Date",
},
},
snippet: {
title: "Movements",
labels: {
type: "Type",
quantity: "Quantity",
},
},
types: {
IN: "In",
OUT: "Out",
ASSIGNMENT: "Assignment",
RETURN: "Return",
ADJUSTMENT: "Adjustment",
DELETED: "Deleted",
},
fallback: {
missingValue: "-",
unknownType: "Unknown movement type",
},
},
},
login: {
title: "Sign In",
+33
View File
@@ -259,6 +259,39 @@ export const es = {
invalidUpdateStatus: "Estado inválido",
},
},
movements: {
list: {
title: "Movimientos",
empty: "No se encontraron movimientos.",
columns: {
type: "Tipo",
item: "Artículo",
serialNumber: "Número de serie",
quantity: "Cantidad",
recipient: "Destinatario",
date: "Fecha",
},
},
snippet: {
title: "Movimientos",
labels: {
type: "Tipo",
quantity: "Cantidad",
},
},
types: {
IN: "Entrada",
OUT: "Salida",
ASSIGNMENT: "Asignación",
RETURN: "Devolución",
ADJUSTMENT: "Ajuste",
DELETED: "Eliminación",
},
fallback: {
missingValue: "-",
unknownType: "Tipo de movimiento desconocido",
},
},
},
login: {
title: "Iniciar sesión",