diff --git a/docs/componentes-de-interfaz.md b/docs/componentes-de-interfaz.md
index 630aa7a..6f2a04f 100644
--- a/docs/componentes-de-interfaz.md
+++ b/docs/componentes-de-interfaz.md
@@ -473,6 +473,17 @@ No conoce entidades, formularios, consultas ni endpoints.
`FormProgress` de `@repo/rdx-ui/components/form-progress` presenta pasos numerados, marca el actual con `aria-current="step"`, identifica los completados y lleva el foco al anuncio traducido de progreso. Recibe etiquetas, descripción y textos accesibles del consumidor; no conoce entidades ni consultas. Lo usan los asistentes de recursos, activos y puestos.
+El diálogo de alta de puesto usa tamaño medio y cabecera con icono decorativo; la edición
+mantiene tamaño grande. Asociaciones, activos, identificación y resumen utilizan
+`FormSection variant="plain"`, títulos de grupo uniformes y separación de 20 px. Las
+asociaciones y el resumen pasan a dos columnas según el ancho del contenedor; el nombre
+del alta ocupa todo el ancho. Los activos seleccionados usan filas compactas con separadores,
+nombres que pueden partirse y botones de retirada con etiqueta accesible. El resumen
+muestra `—` para valores ausentes. La ayuda del ordenador se vincula al selector mediante
+`aria-describedby`; el nombre identifica su error junto al campo. La ayuda general queda
+al final, antes del pie fijo, con navegación por flechas y cancelación deshabilitada durante
+el guardado. Se conservan el cuerpo desplazable y la altura según contenido.
+
El nuevo puesto se organiza en sede/responsable, activos, ordenador principal y resumen. Los controles de asociaciones, composición, ordenador y resumen tienen responsabilidades propias y se reutilizan en la edición cuando corresponde. Las acciones Anterior/Siguiente conservan valores; Guardar aparece en el resumen. La sede única y el activo único tienen selección automática. Véase [Puestos de trabajo](puestos-de-trabajo.md#alta-en-cuatro-pasos).
## Selección de activos en diálogo
diff --git a/docs/puestos-de-trabajo.md b/docs/puestos-de-trabajo.md
index 331f50b..3f2715e 100644
--- a/docs/puestos-de-trabajo.md
+++ b/docs/puestos-de-trabajo.md
@@ -72,6 +72,13 @@ El estado mostrado del activo se calcula desde su estado técnico y los puestos
3. Ordenador principal. Se elige en un diálogo de selección única entre los activos seleccionados. Si solo queda uno se asigna automáticamente; al retirar el ordenador se limpia la selección o se asigna el único restante.
4. Resumen. Muestra sede, responsable, composición y ordenador principal. El nombre propuesto por sede/responsable se puede editar antes de Guardar.
+El diálogo de alta utiliza tamaño medio y cabecera con icono decorativo. Los grupos
+reutilizan `FormSection variant="plain"`; asociaciones y resumen responden al ancho
+del contenedor, con dos columnas cuando caben y una en espacios estrechos. Los activos
+seleccionados se muestran como filas compactas, sin tarjetas anidadas. El nombre del
+resumen ocupa el ancho completo y los valores ausentes muestran `—`. La edición conserva
+el diálogo grande y los mismos grupos, conforme al [patrón de diálogos](patron-de-dialogos.md).
+
Anterior conserva valores y Siguiente valida los requisitos del paso. Guardar solo aparece en el resumen y no se envía el alta desde pasos intermedios. La edición mantiene un formulario único y permite conservar/completar puestos anteriores o desactivados. Cancelar permanece en el pie del modal y la ayuda inmediatamente encima.
Se elimina `sharedAssetIds` de contratos, dominio, reservas y vistas. La migración explícita `removeSharedWorkstationAssets`, ejecutada durante la preparación del esquema, elimina las asociaciones de tipo compartido tanto actuales como históricas y limpia sus referencias técnicas cuando no existe ninguna asociación exclusiva histórica. No convierte activos compartidos en exclusivos, no borra activos ni revisiones y no genera reservas conflictivas. Es transaccional e idempotente. Las composiciones exclusivas e identidades se conservan.
diff --git a/modules/assets/src/common/locales/en.json b/modules/assets/src/common/locales/en.json
index 4d0e1a9..246a685 100644
--- a/modules/assets/src/common/locales/en.json
+++ b/modules/assets/src/common/locales/en.json
@@ -326,7 +326,10 @@
},
"formLayout": {
"identity": "Identification and assignment",
- "template": "Template details"
+ "template": "Template details",
+ "workstationAssociations": "Location and owner",
+ "workstationIdentity": "Workstation identification",
+ "workstationSummary": "Selected details"
},
"detailSections": {
"identification": "Identification",
diff --git a/modules/assets/src/common/locales/es.json b/modules/assets/src/common/locales/es.json
index d1f3dd8..3c80e02 100644
--- a/modules/assets/src/common/locales/es.json
+++ b/modules/assets/src/common/locales/es.json
@@ -326,7 +326,10 @@
},
"formLayout": {
"identity": "Identificación y asignación",
- "template": "Datos de la plantilla"
+ "template": "Datos de la plantilla",
+ "workstationAssociations": "Ubicación y responsable",
+ "workstationIdentity": "Identificación del puesto",
+ "workstationSummary": "Datos seleccionados"
},
"detailSections": {
"identification": "Identificación",
diff --git a/modules/assets/src/web/assets/shared/inventory.test.tsx b/modules/assets/src/web/assets/shared/inventory.test.tsx
index 416e50c..afe8f18 100644
--- a/modules/assets/src/web/assets/shared/inventory.test.tsx
+++ b/modules/assets/src/web/assets/shared/inventory.test.tsx
@@ -20,7 +20,7 @@ import type {
WorkstationDTO,
} from "../../../common";
import { AssetSchema } from "../../../common/inventory";
-import { WorkstationSchema } from "../../../common/workstations";
+import { WorkstationInputSchema, WorkstationSchema } from "../../../common/workstations";
import { AssetRelationships } from "../../asset-relationships";
import { catalogKeys } from "../../catalog/shared/api";
import { CatalogRelationForm } from "../../catalog-relations";
@@ -28,6 +28,10 @@ import { CatalogTemplateForm } from "../../catalog-templates";
import { assetsEnglish, assetsSpanish } from "../../shared";
import { inventoryKeys } from "../../shared/queries";
import { WorkstationForm, WorkstationsPage } from "../../workstations";
+import { WorkstationAssetFields } from "../../workstations/shared/ui/workstation-asset-fields";
+import { WorkstationComputerField } from "../../workstations/shared/ui/workstation-computer-field";
+import { WorkstationFormActions } from "../../workstations/shared/ui/workstation-form-actions";
+import { WorkstationSummary } from "../../workstations/shared/ui/workstation-summary";
import { AssetCreateProgress } from "../create/ui/asset-create-progress";
import { AssetSelectionPanel } from "../detail/ui/asset-selection-panel";
import { AssetInventoryPage } from "../list";
@@ -786,3 +790,91 @@ test("nombre propuesto: usa texto de la plantilla, omite marca y vacíos y respe
);
assert.equal(suggestedAssetName(undefined, fields, {}, undefined), "");
});
+
+test("diseño del puesto: grupos compactos, columnas por contenedor y resumen sin textos de ausencia", async () => {
+ const values = WorkstationInputSchema.parse({
+ name: "Puesto",
+ active: true,
+ siteId: null,
+ personId: null,
+ assetIds: [],
+ computerAssetId: null,
+ });
+
+ for (const language of ["es", "en"]) {
+ const initial = await render(
+ language,
+ undefined} />
+ );
+
+ assert.ok(
+ initial.includes(language === "es" ? "Ubicación y responsable" : "Location and owner")
+ );
+ assert.ok(initial.includes("uppercase tracking-wide"));
+ assert.ok(initial.includes("@min-[560px]:grid-cols-2"));
+ assert.ok(initial.indexOf("
+ );
+
+ assert.ok(summary.includes(language === "es" ? "Datos seleccionados" : "Selected details"));
+ assert.ok(summary.includes("@min-[560px]:grid-cols-2"));
+ assert.equal((summary.match(/—/g) ?? []).length, 4);
+ assert.equal(summary.includes(language === "es" ? "Sin asignación" : "No assignment"), false);
+ const computer = await render(
+ language,
+ undefined}
+ prefix="design"
+ required
+ singleComputer={false}
+ sites={[]}
+ values={values}
+ />
+ );
+
+ assert.ok(
+ computer.includes('aria-describedby="design-computer-help design-computer-required"')
+ );
+ assert.ok(computer.includes('id="design-computer-help"'));
+ const selection = await render(
+ language,
+ undefined}
+ onCreate={() => undefined}
+ onRemove={() => undefined}
+ prefix="design"
+ selectedIds={[companyId]}
+ sites={[]}
+ unavailableReason={() => "ASSET_NOT_FOUND"}
+ />
+ );
+
+ assert.ok(selection.includes("divide-y divide-border/60"));
+ assert.ok(selection.includes("—"));
+ assert.match(selection, /