- {id === workstation.values.computerAssetId - ? t("workstationComputer") - : t("workstationAssets")} -
-- {asset.specification.templateName} -
--
-
- {t("workstationDetail.missingAsset")} -
- )} -diff --git a/docs/componentes-de-interfaz.md b/docs/componentes-de-interfaz.md
index 3d121c2..1853167 100644
--- a/docs/componentes-de-interfaz.md
+++ b/docs/componentes-de-interfaz.md
@@ -814,3 +814,61 @@ Una autorización pendiente se explica tanto en su paso como en el resumen, con
para registrarla cuando está permitida. Guardar solo se ofrece al final del alta. La edición
conserva las excepciones de dominio para modificar nombre/notas o archivar perfiles con
referencias que requieren revisión; el servidor mantiene la validación definitiva.
+
+
+## Paneles de consulta y ficha del puesto
+
+`DetailPanel`, en `@repo/rdx-ui/components/detail-panel`, compone las primitivas `Card`,
+`CardHeader` y `CardContent` con una cabecera uniforme: título accesible `h2` enlazado
+mediante un identificador único, icono decorativo, descripción opcional, contador y
+acciones opcionales. Recibe `title`, `icon`, `children`, `description`, `count` y `actions`;
+no conoce entidades, consultas ni traducciones. El contador conserva cero y se omite
+si el consumidor todavía no conoce el resultado. El contenido responde al ancho de su
+propio contenedor. No incorpora scroll, acciones ni estado de apertura por su cuenta.
+
+La ficha del puesto reutiliza el panel en activos, personas y perfiles. Se organiza en
+Resumen, Perfiles y Software mediante `Tabs` y `ScrollableTabsList`. El resumen destaca
+el ordenador principal y presenta el resto como tarjetas de periféricos sin duplicarlo.
+Reutiliza `AssetSummaryCard`, que acepta contenido opcional para `name` y permite enlazar
+el nombre desde el consumidor sin depender de una librería de rutas. Las tarjetas
+mantienen plantilla, icono, características y estado; la ficha completa del activo sigue
+accesible desde el nombre. Las listas usan `ul`/`li` y una o dos columnas según su ancho.
+
+El panel lateral presenta las personas con avatar y su relación; el responsable aparece
+primero y cada persona se muestra una sola vez. Los perfiles pertenecen exclusivamente
+al ordenador principal, incluidos los archivados que ya se mostraban en la ficha.
+Su pestaña reutiliza `EntityTable` con búsqueda por nombre o persona, ordenación,
+paginación, estados y avisos de revisión; omite repetir el mismo dispositivo por fila.
+La tabla conserva su estado al cambiar a otra pestaña. El software se monta al abrir
+su pestaña y permanece montado después, conservando sus filtros al cambiar de sección.
+Mantiene el panel de gestión existente, con sus acciones y restricciones.
+
+La cabecera presenta estado, sede y responsable bajo el nombre editable, sustituyendo
+el subtítulo genérico. Editar puesto aparece a la derecha junto a `EntityRowActions`,
+que agrupa desactivación y eliminación con sus confirmaciones. Nuevo perfil se muestra
+en las acciones del panel Perfiles. El alta se deshabilita con explicación si el puesto no está activo, no tiene
+ordenador principal en su composición o el ordenador está retirado. Los datos de
+inventario, perfiles y catálogo se actualizan mediante `useListQuery`; fallar o cargar
+no se presenta como ausencia de registros. El catálogo aporta el icono actual de la
+plantilla y su ausencia conserva la alternativa visual habitual.
+
+
+## Nombre del puesto editable en la ficha
+
+`WorkstationNameEditor`, exportado por `@metis/assets/client`, vive en
+`workstations/rename/ui`. Encapsula el título `h1` y su edición inline: botón de lápiz,
+input etiquetado, Guardar/Cancelar, Enter/Escape y gestión del foco. El editor es propio
+de puestos; no se introduce un editor genérico sin otros consumidores. La ficha de
+compañía lo compone sin conocer la llamada HTTP.
+
+La validación y el máximo del input se obtienen de `WorkstationInputSchema.shape.name`,
+normalizando espacios exteriores. El borrador solo se inicia desde la acción de edición,
+sin efectos que lo sincronicen con las consultas. El efecto existente gestiona únicamente
+el foco del DOM. La mutación conserva el error en contexto, impide envíos repetidos y
+cierra la edición cuando el servidor confirma el resultado. Cancelar no escribe datos.
+
+`renameWorkstation` relee el inventario y reutiliza `saveWorkstation` con los valores
+vigentes y el nuevo nombre. No añade endpoints ni cambia las reglas del agregado o su
+historial. Si el nombre ya coincide, evita una escritura. La respuesta actualiza la caché
+de inventario y la invalidación posterior consulta el estado definitivo. Se mantiene el
+tratamiento habitual de CSRF y de errores de inventario.
diff --git a/docs/puestos-de-trabajo.md b/docs/puestos-de-trabajo.md
index edb988d..cb64a45 100644
--- a/docs/puestos-de-trabajo.md
+++ b/docs/puestos-de-trabajo.md
@@ -49,6 +49,42 @@ La ficha reúne únicamente perfiles del ordenador principal, incluidos los arch
La ficha consulta también el [software del ordenador](software-del-ordenador.md), incluido el sistema operativo actual, por `computerAssetId`. Las instalaciones permanecen vinculadas al activo al cambiar el ordenador principal.
+## Presentación de la ficha
+
+El título de la ficha permite cambiar el nombre mediante un botón de lápiz, sin abrir
+el formulario completo. La edición ofrece Guardar y Cancelar; Enter guarda y Escape
+cancela. El campo recibe el foco y lo devuelve al botón al terminar. El borrador se
+conserva si se actualizan los datos en segundo plano. Mientras guarda, se bloquean el
+campo y sus acciones; los errores permanecen en el editor y solo se cierra tras confirmar
+el guardado. Los nombres vacíos o demasiado largos se rechazan con el schema existente.
+Un nombre equivalente tras normalizar espacios no genera otra escritura.
+
+El guardado reutiliza la actualización del puesto y su historial. Antes relee los valores
+vigentes para no enviar sede, responsable o composición capturados al abrir el editor.
+La respuesta confirmada actualiza la caché de inventario y después invalida esa consulta;
+no se invalidan perfiles ni software porque sus referencias no cambian.
+
+La ficha separa Resumen, Perfiles y Software con pestañas accesibles y desplazables en
+espacios estrechos. Bajo el nombre, la cabecera muestra estado, sede y responsable si
+existe, con enlaces a sede y persona. A la derecha aparece Editar puesto y un menú de
+acciones con Desactivar y Eliminar definitivamente, manteniendo sus confirmaciones.
+Nuevo perfil se sitúa en la cabecera del panel Perfiles y se habilita únicamente para un puesto activo con un
+ordenador principal incluido en su composición y no retirado; el bloqueo explica cómo
+completar el puesto.
+
+El resumen destaca el ordenador principal y muestra los demás activos como periféricos
+mediante `AssetSummaryCard`, sin duplicados. Sus nombres enlazan las fichas de activos,
+que conservan el detalle técnico completo. Las personas se presentan con avatar, con el
+responsable primero y una única aparición por persona. La carga o el error de perfiles
+no se confunde con ausencia de personas. La pestaña Perfiles reúne los del ordenador
+principal, incluidos los archivados, mediante la tabla común con búsqueda, ordenación
+y paginación. Conserva los enlaces y avisos de revisión, sin repetir el mismo ordenador
+en cada fila. El software se carga al abrir su pestaña y mantiene la gestión existente.
+
+Los paneles de activos, personas y perfiles reutilizan `DetailPanel` de `@repo/rdx-ui`.
+El diseño responde al ancho disponible y mantiene accesibles contenidos y acciones en
+móvil y escritorio. Véase [Componentes de interfaz](componentes-de-interfaz.md).
+
## Desactivación y eliminación
El listado y la ficha completa ofrecen **Desactivar** y **Eliminar definitivamente**, mediante un mismo diálogo reutilizable con confirmación y resultado de servidor.
diff --git a/modules/assets/package.json b/modules/assets/package.json
index f25e552..f7e492c 100644
--- a/modules/assets/package.json
+++ b/modules/assets/package.json
@@ -10,7 +10,7 @@
},
"scripts": {
"typecheck": "tsc -p tsconfig.json",
- "test": "TSX_TSCONFIG_PATH=../../tsconfig.json node --import tsx --test src/api/domain/asset-catalog.test.ts src/api/domain/asset.test.ts src/api/domain/workstation.test.ts src/api/domain/asset-relationship.test.ts src/web/catalog/shared/catalog.test.tsx src/web/assets/shared/inventory.test.tsx src/api/domain/support.test.ts src/web/support-connections/shared/support.test.tsx src/api/domain/brand.test.ts src/web/brands/shared/brands.test.tsx src/api/domain/template-sections.test.ts src/web/workstations/shared/helpers/workstation-creation.test.ts src/web/workstations/shared/workstation-name.test.ts src/web/assets/select/asset-selection.test.tsx src/web/catalog-fields/select/catalog-field-selection.test.tsx"
+ "test": "TSX_TSCONFIG_PATH=../../tsconfig.json node --import tsx --test src/api/domain/asset-catalog.test.ts src/api/domain/asset.test.ts src/api/domain/workstation.test.ts src/api/domain/asset-relationship.test.ts src/web/catalog/shared/catalog.test.tsx src/web/assets/shared/inventory.test.tsx src/api/domain/support.test.ts src/web/support-connections/shared/support.test.tsx src/api/domain/brand.test.ts src/web/brands/shared/brands.test.tsx src/api/domain/template-sections.test.ts src/web/workstations/shared/helpers/workstation-creation.test.ts src/web/workstations/shared/workstation-name.test.ts src/web/workstations/rename/api/rename-workstation.test.ts src/web/assets/select/asset-selection.test.tsx src/web/catalog-fields/select/catalog-field-selection.test.tsx"
},
"dependencies": {
"@hookform/resolvers": "5.2.2",
diff --git a/modules/assets/src/common/locales/en.json b/modules/assets/src/common/locales/en.json
index 5dbbd92..46785db 100644
--- a/modules/assets/src/common/locales/en.json
+++ b/modules/assets/src/common/locales/en.json
@@ -370,21 +370,35 @@
},
"multiline": "Long text",
"workstationDetail": {
+ "actions": "More workstation actions",
"back": "Back to workstations",
"subtitle": "Assets, user profiles and people connected to this workstation.",
"loadError": "Unable to load the workstation.",
"notFound": "This workstation does not exist in this company.",
"assets": "Workstation assets",
"profiles": "User profiles",
- "profilesHint": "Profiles associated with the assets of this workstation.",
+ "profilesHint": "Profiles on the main computer. Peripherals do not host profiles for this workstation.",
"profilesError": "Unable to load user profiles.",
- "noProfiles": "No profiles are linked to this workstation’s assets.",
+ "noProfiles": "No profiles are linked to the main computer yet.",
"people": "Related people",
"noPeople": "No people are related to this workstation.",
"assignedPerson": "Workstation custodian",
- "profilePerson": "Has a profile on a workstation asset",
+ "profilePerson": "Has a profile on the main computer",
"needsReview": "References awaiting review",
- "missingAsset": "Asset unavailable."
+ "missingAsset": "Asset unavailable.",
+ "overview": "Overview",
+ "software": "Computer software",
+ "sections": "Workstation sections",
+ "peripherals": "Other assets and peripherals",
+ "noComputer": "This workstation has no available main computer",
+ "completeComputer": "Edit the workstation to add its assets and choose the main computer.",
+ "profileUnavailable": "To create a profile, the workstation must be active and have an available main computer.",
+ "peopleHint": "The custodian and people with profiles on the main computer.",
+ "searchProfiles": "Search by profile or person",
+ "review": "Review",
+ "rename": "Rename workstation",
+ "nameSaved": "Name updated.",
+ "nameInvalid": "Enter a valid workstation name."
},
"workstationComputer": "Main computer",
"workstationComputerHelp": "Select the main computer by clicking its card among the workstation assets. Profiles will be created on it; the other assets are peripherals.",
@@ -452,4 +466,4 @@
"fieldsDescription": "Complete the template details. Fields marked with * are required.",
"nameDescription": "Review the suggested name, technical status, site and registration date. Add notes if you need to provide more context."
}
-}
\ No newline at end of file
+}
diff --git a/modules/assets/src/common/locales/es.json b/modules/assets/src/common/locales/es.json
index 61b53ab..c23c5b4 100644
--- a/modules/assets/src/common/locales/es.json
+++ b/modules/assets/src/common/locales/es.json
@@ -370,21 +370,35 @@
},
"multiline": "Texto largo",
"workstationDetail": {
+ "actions": "Más acciones del puesto",
"back": "Volver a puestos de trabajo",
"subtitle": "Activos, perfiles de usuario y personas vinculadas a este puesto.",
"loadError": "No se ha podido cargar el puesto de trabajo.",
"notFound": "Este puesto de trabajo no existe en esta compañía.",
"assets": "Activos del puesto",
"profiles": "Perfiles de usuario",
- "profilesHint": "Perfiles asociados a los activos de este puesto.",
+ "profilesHint": "Perfiles del ordenador principal. Los periféricos no alojan perfiles de este puesto.",
"profilesError": "No se han podido cargar los perfiles de usuario.",
- "noProfiles": "No hay perfiles vinculados a los activos de este puesto.",
+ "noProfiles": "Todavía no hay perfiles vinculados al ordenador principal.",
"people": "Personas relacionadas",
"noPeople": "No hay personas relacionadas con este puesto.",
"assignedPerson": "Responsable del puesto",
- "profilePerson": "Con perfil en un activo del puesto",
+ "profilePerson": "Con perfil en el ordenador principal",
"needsReview": "Referencias pendientes de revisión",
- "missingAsset": "Activo no disponible."
+ "missingAsset": "Activo no disponible.",
+ "overview": "Resumen",
+ "software": "Software del ordenador",
+ "sections": "Secciones del puesto de trabajo",
+ "peripherals": "Otros activos y periféricos",
+ "noComputer": "Este puesto no tiene un ordenador principal disponible",
+ "completeComputer": "Edita el puesto para incorporar sus activos y elegir el ordenador principal.",
+ "profileUnavailable": "Para crear un perfil, el puesto debe estar activo y tener un ordenador principal disponible.",
+ "peopleHint": "Responsable y personas con perfiles en el ordenador principal.",
+ "searchProfiles": "Buscar por perfil o persona",
+ "review": "Revisión",
+ "rename": "Cambiar nombre del puesto",
+ "nameSaved": "Nombre actualizado.",
+ "nameInvalid": "Introduce un nombre válido para el puesto."
},
"workstationComputer": "Ordenador principal",
"workstationComputerHelp": "Pulsa la tarjeta del ordenador principal entre los activos del puesto. Los perfiles se crearán en él; el resto de activos son periféricos.",
@@ -452,4 +466,4 @@
"fieldsDescription": "Completa los datos de la plantilla. Los campos marcados con * son obligatorios.",
"nameDescription": "Revisa el nombre propuesto, el estado técnico, la sede y la fecha de alta. Añade notas si necesitas aportar más contexto."
}
-}
\ No newline at end of file
+}
diff --git a/modules/assets/src/web/assets/shared/ui/asset-summary-card.tsx b/modules/assets/src/web/assets/shared/ui/asset-summary-card.tsx
index 38765c8..588794c 100644
--- a/modules/assets/src/web/assets/shared/ui/asset-summary-card.tsx
+++ b/modules/assets/src/web/assets/shared/ui/asset-summary-card.tsx
@@ -12,10 +12,12 @@ import { assetCharacteristicsSummary } from "../helpers/asset-characteristics-su
export function AssetSummaryCard({
asset,
template,
+ name,
actions,
error,
}: {
asset: AssetDTO | undefined;
+ name?: ReactNode;
template?: CatalogItemDTO | undefined;
actions?: ReactNode;
error?: string | undefined;
@@ -35,7 +37,7 @@ export function AssetSummaryCard({
{asset?.values.name ?? "—"}
+{name ?? asset?.values.name ?? "—"}
{asset?.specification.templateName ?? "—"}
diff --git a/modules/assets/src/web/index.ts b/modules/assets/src/web/index.ts index 0e520e3..42ce040 100644 --- a/modules/assets/src/web/index.ts +++ b/modules/assets/src/web/index.ts @@ -9,5 +9,6 @@ export { inventoryApi } from "./shared/api"; export { inventoryKeys } from "./shared/queries"; export { AssetSupport } from "./support-connections"; export { WorkstationEditorDialog, WorkstationForm, WorkstationsPage } from "./workstations"; +export { WorkstationNameEditor } from "./workstations/rename/ui/workstation-name-editor"; export { WorkstationActions } from "./workstations/shared/ui/workstation-actions"; export { WorkstationLifecycleDialog } from "./workstations/shared/ui/workstation-lifecycle-dialog"; diff --git a/modules/assets/src/web/workstations/rename/api/rename-workstation.api.ts b/modules/assets/src/web/workstations/rename/api/rename-workstation.api.ts new file mode 100644 index 0000000..826300b --- /dev/null +++ b/modules/assets/src/web/workstations/rename/api/rename-workstation.api.ts @@ -0,0 +1,26 @@ +import { WorkstationInputSchema } from "../../../../common/workstations"; +import { getAssetInventory } from "../../../assets/list/api/get-asset-inventory.api"; +import { InventoryApiError } from "../../../shared/api/inventory-api-error"; +import { saveWorkstation } from "../../shared/api/save-workstation.api"; + +/** Relee la composición vigente para no enviar el resto de valores capturados al comenzar la edición. */ +export async function renameWorkstation(companyId: string, id: string, name: string) { + const parsed = WorkstationInputSchema.shape.name.safeParse(name.trim()); + + if (!parsed.success) { + throw new InventoryApiError("INVALID_REQUEST"); + } + const inventory = await getAssetInventory(companyId); + const workstation = inventory.workstations.find( + (item) => item.id === id && item.companyId === companyId + ); + + if (!workstation) { + throw new InventoryApiError("WORKSTATION_NOT_FOUND"); + } + if (parsed.data === workstation.values.name) { + return { workstation }; + } + + return saveWorkstation(companyId, { ...workstation.values, name: parsed.data }, id); +} diff --git a/modules/assets/src/web/workstations/rename/api/rename-workstation.test.ts b/modules/assets/src/web/workstations/rename/api/rename-workstation.test.ts new file mode 100644 index 0000000..8f30364 --- /dev/null +++ b/modules/assets/src/web/workstations/rename/api/rename-workstation.test.ts @@ -0,0 +1,144 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { configureCsrfTokenReader } from "@metis/core/client"; + +import { WorkstationSchema } from "../../../../common/workstations"; +import { InventoryApiError } from "../../../shared/api/inventory-api-error"; + +import { renameWorkstation } from "./rename-workstation.api"; + +const id = (n: number) => `0199b200-0000-7000-8000-${String(n).padStart(12, "0")}`; + +const companyId = id(1); +const values = { + name: "Nombre actual", + active: false, + siteId: id(3), + personId: id(4), + computerAssetId: id(5), + assetIds: [id(5), id(6)], +}; +const workstation = WorkstationSchema.parse({ + id: id(2), + companyId, + values, + history: [ + { + id: id(7), + actorId: id(8), + actorName: "Técnico", + recordedAt: "2026-10-16T12:00:00.000Z", + values, + }, + ], +}); + +test("renombrar: usa valores vigentes, normaliza el nombre, valida respuesta y envía CSRF", async (context) => { + context.after(configureCsrfTokenReader(() => "a".repeat(64))); + const original = globalThis.fetch; + const saved = { ...workstation, values: { ...values, name: "Nuevo nombre" } }; + const requests: string[] = []; + + globalThis.fetch = async (input, options) => { + requests.push(String(input)); + assert.equal(options?.credentials, "same-origin"); + if (options?.method === "GET") { + assert.equal(input, `/api/assets/${companyId}`); + + return Response.json({ assets: [], relationships: [], workstations: [workstation] }); + } + assert.equal(input, `/api/assets/${companyId}/workstations/${workstation.id}/update`); + assert.equal(options?.method, "POST"); + assert.equal(new Headers(options.headers).get("x-csrf-token"), "a".repeat(64)); + assert.deepEqual(JSON.parse(String(options.body)), { values: saved.values }); + + return Response.json({ workstation: saved }); + }; + try { + assert.deepEqual(await renameWorkstation(companyId, workstation.id, " Nuevo nombre "), { + workstation: saved, + }); + assert.equal(requests.length, 2); + } finally { + globalThis.fetch = original; + } +}); + +test("renombrar: rechaza vacíos, espacios y exceso de longitud antes de realizar peticiones", async () => { + const original = globalThis.fetch; + let requests = 0; + + globalThis.fetch = async () => { + requests += 1; + + return Response.json({}); + }; + try { + for (const name of ["", " ", "a".repeat(101)]) { + await assert.rejects( + () => renameWorkstation(companyId, workstation.id, name), + (error: unknown) => error instanceof InventoryApiError && error.code === "INVALID_REQUEST" + ); + } + assert.equal(requests, 0); + } finally { + globalThis.fetch = original; + } +}); + +test("renombrar: un nombre equivalente no provoca una escritura ni otra revisión", async () => { + const original = globalThis.fetch; + let requests = 0; + + globalThis.fetch = async (_input, options) => { + requests += 1; + assert.equal(options?.method, "GET"); + + return Response.json({ assets: [], relationships: [], workstations: [workstation] }); + }; + try { + assert.deepEqual(await renameWorkstation(companyId, workstation.id, ` ${values.name} `), { + workstation, + }); + assert.equal(requests, 1); + } finally { + globalThis.fetch = original; + } +}); + +test("renombrar: propaga puesto ausente y errores del servidor sin escritura posterior", async (context) => { + context.after(configureCsrfTokenReader(() => "a".repeat(64))); + const original = globalThis.fetch; + let exists = false; + let posts = 0; + + globalThis.fetch = async (_input, options) => { + if (options?.method === "GET") { + return Response.json({ + assets: [], + relationships: [], + workstations: exists ? [workstation] : [], + }); + } + posts += 1; + + return Response.json({ code: "WORKSTATION_IN_USE" }, { status: 409 }); + }; + try { + await assert.rejects( + () => renameWorkstation(companyId, workstation.id, "Nuevo"), + (error: unknown) => + error instanceof InventoryApiError && error.code === "WORKSTATION_NOT_FOUND" + ); + assert.equal(posts, 0); + exists = true; + await assert.rejects( + () => renameWorkstation(companyId, workstation.id, "Nuevo"), + (error: unknown) => error instanceof InventoryApiError && error.code === "WORKSTATION_IN_USE" + ); + assert.equal(posts, 1); + } finally { + globalThis.fetch = original; + } +}); diff --git a/modules/assets/src/web/workstations/rename/ui/workstation-name-editor.tsx b/modules/assets/src/web/workstations/rename/ui/workstation-name-editor.tsx new file mode 100644 index 0000000..cebe4cd --- /dev/null +++ b/modules/assets/src/web/workstations/rename/ui/workstation-name-editor.tsx @@ -0,0 +1,177 @@ +import { FormLabel } from "@repo/rdx-ui/components/form-label"; +import { PencilIcon } from "@repo/rdx-ui/lib/icons"; +import { Button } from "@repo/shadcn-ui/components/button"; +import { Input } from "@repo/shadcn-ui/components/input"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { FormEvent } from "react"; +import { useEffect, useId, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import type { WorkstationDTO } from "../../../../common/workstations"; +import { WorkstationInputSchema } from "../../../../common/workstations"; +import type { getAssetInventory } from "../../../assets/list/api/get-asset-inventory.api"; +import { inventoryKeys } from "../../../shared/queries"; +import { InventoryFeedback } from "../../../shared/ui/inventory-feedback"; +import { renameWorkstation } from "../api/rename-workstation.api"; + +/** Borrador local explícito: Enter guarda, Escape cancela y los refrescos no sustituyen lo escrito. */ +export function WorkstationNameEditor({ workstation }: { workstation: WorkstationDTO }) { + const { t } = useTranslation("assets"); + const client = useQueryClient(); + const id = useId(); + const input = useRef+ {t("workstationDetail.nameSaved")} +
+ )} +{t("noWorkstationAssets")}
+- {id === workstation.values.computerAssetId - ? t("workstationComputer") - : t("workstationAssets")} -
-- {asset.specification.templateName} -
-- {t("workstationDetail.missingAsset")} -
- )} -{t("workstationDetail.subtitle")}
-- {t("workstationDetail.profilesHint")} -
- {profiles.isPending &&{t("loading")}
} - {profiles.isError && ( -- {t("workstationDetail.profilesError")} -
- -- {t("workstationDetail.noProfiles")} -
- )} -
-
- {t("workstationDetail.needsReview")} -
- )} -{t("workstationDetail.noPeople")}
- )} -- {t( - person.id === workstation.values.personId - ? "workstationDetail.assignedPerson" - : "workstationDetail.profilePerson" - )} -
-