feat: implementar sugerencia de nombre para puestos de trabajo y mejorar formularios
This commit is contained in:
parent
6d6f807486
commit
45b559ab92
@ -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.
|
||||
|
||||
|
||||
@ -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.",
|
||||
|
||||
@ -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.",
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 }))}
|
||||
|
||||
@ -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"
|
||||
);
|
||||
});
|
||||
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user