diff --git a/docs/puestos-de-trabajo.md b/docs/puestos-de-trabajo.md index 91f3882..6bdd2ee 100644 --- a/docs/puestos-de-trabajo.md +++ b/docs/puestos-de-trabajo.md @@ -4,7 +4,7 @@ La sección Puestos de trabajo de cada compañía agrupa los activos utilizados habitualmente como un conjunto. Un puesto tiene UUID v7, nombre, estado activo/inactivo, sede opcional, persona opcional y hasta 200 activos exclusivos y 200 recursos compartidos. No se admite duplicar un activo ni incluirlo en ambas listas. Se puede crear vacío y completar posteriormente. Una persona puede tener varios puestos. Los puestos no contienen otros puestos. -Nuevo puesto abre un modal desde la cabecera derecha; Editar abre otro modal con los datos existentes. El formulario ofrece selectores con «+» para dar de alta sedes, personas y activos sin perder sus datos. Guardar un elemento relacionado lo selecciona o incorpora al puesto; cancelarlo mantiene el formulario principal. Los elementos guardados sobreviven si posteriormente se cancela el puesto. El alta de activo desde un puesto usa plantillas y sedes existentes para evitar un tercer modal. +Nuevo puesto abre un modal desde la cabecera derecha; Editar abre otro modal con los datos existentes. Al crear, elegir persona y sede propone el nombre «Puesto de {persona} en {sede}»; el técnico puede modificarlo o borrarlo antes de guardar y esa edición manual no se sobrescribe. El formulario ofrece selectores con «+» para dar de alta sedes, personas y activos sin perder sus datos. Guardar un elemento relacionado lo selecciona o incorpora al puesto; cancelarlo mantiene el formulario principal. Los elementos guardados sobreviven si posteriormente se cancela el puesto. El alta de activo desde un puesto usa plantillas y sedes existentes para evitar un tercer modal. La ayuda aparece inmediatamente antes de Guardar y Cancelar. El listado permite buscar por nombre, persona o sede. Historial del puesto muestra las versiones completas, con estado, asignación y composición, fecha y responsable. La ficha de cada activo muestra los puestos vigentes en los que se utiliza. diff --git a/modules/assets/src/common/locales/en.json b/modules/assets/src/common/locales/en.json index 96602d6..bb00165 100644 --- a/modules/assets/src/common/locales/en.json +++ b/modules/assets/src/common/locales/en.json @@ -144,6 +144,7 @@ "noSharedWorkstationAssets": "No shared resources.", "editWorkstation": "Edit workstation", "workstationPerson": "Workstation assignee", + "workstationSuggestedName": "{{person}}'s workstation at {{site}}", "workstationHistory": "Workstation history", "workstationAssets": "Workstation assets", "noWorkstations": "This company has no workstations yet.", diff --git a/modules/assets/src/common/locales/es.json b/modules/assets/src/common/locales/es.json index 976975c..3c90ede 100644 --- a/modules/assets/src/common/locales/es.json +++ b/modules/assets/src/common/locales/es.json @@ -144,6 +144,7 @@ "noSharedWorkstationAssets": "Sin recursos compartidos.", "editWorkstation": "Editar puesto", "workstationPerson": "Persona del puesto", + "workstationSuggestedName": "Puesto de {{person}} en {{site}}", "workstationHistory": "Historial del puesto", "workstationAssets": "Activos del puesto", "noWorkstations": "Esta compañía todavía no tiene puestos de trabajo.", diff --git a/modules/assets/src/web/workstations/shared/controllers/use-workstation-form-controller.ts b/modules/assets/src/web/workstations/shared/controllers/use-workstation-form-controller.ts index ffa3b59..7be27f0 100644 --- a/modules/assets/src/web/workstations/shared/controllers/use-workstation-form-controller.ts +++ b/modules/assets/src/web/workstations/shared/controllers/use-workstation-form-controller.ts @@ -2,6 +2,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useEffect, useRef, useState } from "react"; import { useForm } from "react-hook-form"; +import { useTranslation } from "react-i18next"; import type { WorkstationInput } from "../../../../common"; import { WorkstationInputSchema } from "../../../../common"; @@ -10,12 +11,15 @@ import { catalogApi, catalogKeys } from "../../../catalog/shared/api"; import { inventoryKeys } from "../../../shared/queries"; import { saveWorkstation } from "../api"; import type { WorkstationFormProps } from "../types"; +import { suggestedWorkstationName } from "../workstation-name"; export function useWorkstationFormController({ companyId, workstation, onSaved, + lookups, }: WorkstationFormProps) { + const { t } = useTranslation("assets"); const client = useQueryClient(); const mounted = useRef(true); @@ -74,6 +78,26 @@ export function useWorkstationFormController({ } }; + const selectAssociation = (field: "siteId" | "personId", id: string) => { + const selectedId = id || null; + + form.setValue(field, selectedId); + if (workstation || form.formState.dirtyFields.name) { + return; + } + const siteId = field === "siteId" ? selectedId : form.getValues("siteId"); + const personId = field === "personId" ? selectedId : form.getValues("personId"); + const name = suggestedWorkstationName( + lookups.people.find((item) => item.id === personId)?.name, + lookups.sites.find((item) => item.id === siteId)?.name, + (person, site) => t("workstationSuggestedName", { person, site }) + ); + + if (name) { + form.setValue("name", name, { shouldDirty: false }); + } + }; + const mutation = useMutation({ mutationFn: (input: WorkstationInput) => saveWorkstation(companyId, input, workstation?.id), onSuccess: async (response) => { @@ -87,13 +111,13 @@ export function useWorkstationFormController({ const siteCreated = (id: string) => { if (mounted.current) { - form.setValue("siteId", id); + selectAssociation("siteId", id); } }; const personCreated = (id: string) => { if (mounted.current) { - form.setValue("personId", id); + selectAssociation("personId", id); } }; @@ -109,6 +133,7 @@ export function useWorkstationFormController({ assets, unavailable, add, + selectAssociation, mutation, submit, siteCreated, diff --git a/modules/assets/src/web/workstations/shared/ui/workstation-form.tsx b/modules/assets/src/web/workstations/shared/ui/workstation-form.tsx index 05bd70c..75025a2 100644 --- a/modules/assets/src/web/workstations/shared/ui/workstation-form.tsx +++ b/modules/assets/src/web/workstations/shared/ui/workstation-form.tsx @@ -27,6 +27,7 @@ export function WorkstationForm(props: WorkstationFormProps) { assets, unavailable, add, + selectAssociation, mutation, submit, siteCreated, @@ -60,7 +61,7 @@ export function WorkstationForm(props: WorkstationFormProps) { }} id={`${prefix}-site`} label={t("site")} - onValueChange={(id) => form.setValue("siteId", id || null)} + onValueChange={(id) => selectAssociation("siteId", id)} options={lookups.sites.map((item) => ({ value: item.id, label: item.name }))} placeholder={t("noAssignment")} value={values.siteId ?? ""} @@ -75,7 +76,7 @@ export function WorkstationForm(props: WorkstationFormProps) { }} id={`${prefix}-person`} label={t("workstationPerson")} - onValueChange={(id) => form.setValue("personId", id || null)} + onValueChange={(id) => selectAssociation("personId", id)} options={lookups.people .filter((item) => item.active || item.id === workstation?.values.personId) .map((item) => ({ value: item.id, label: item.name }))} diff --git a/modules/assets/src/web/workstations/shared/workstation-name.test.ts b/modules/assets/src/web/workstations/shared/workstation-name.test.ts new file mode 100644 index 0000000..aeca901 --- /dev/null +++ b/modules/assets/src/web/workstations/shared/workstation-name.test.ts @@ -0,0 +1,15 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { suggestedWorkstationName } from "./workstation-name"; + +test("propone un nombre de puesto solo al conocer persona y sede", () => { + const format = (person: string, site: string) => `${person} @ ${site}`; + + assert.equal(suggestedWorkstationName(undefined, "Fábrica", format), undefined); + assert.equal(suggestedWorkstationName("Ana", undefined, format), undefined); + assert.equal( + suggestedWorkstationName("Ana García", "Uecko Fábrica", format), + "Ana García @ Uecko Fábrica" + ); +}); diff --git a/modules/assets/src/web/workstations/shared/workstation-name.ts b/modules/assets/src/web/workstations/shared/workstation-name.ts new file mode 100644 index 0000000..ca493a0 --- /dev/null +++ b/modules/assets/src/web/workstations/shared/workstation-name.ts @@ -0,0 +1,11 @@ +export function suggestedWorkstationName( + personName: string | undefined, + siteName: string | undefined, + format: (person: string, site: string) => string +) { + if (!(personName && siteName)) { + return undefined; + } + + return format(personName, siteName); +}