Mejorar la ayuda en los diálogos y cambiar checkbox por switch donde proceda.
This commit is contained in:
parent
6f83ceb0b4
commit
e472b2ee8c
@ -15,11 +15,13 @@ paquetes genéricos.
|
||||
- Card: bloques de información y estado.
|
||||
- Select: idioma y selección de sistemas, entornos, cuentas, personas y demás relaciones, con navegación por teclado.
|
||||
- Input y Label: credenciales y datos de usuario.
|
||||
- Checkbox: estado activo de usuarios, compañías, personas y departamentos.
|
||||
- Checkbox: filtros de listado, selección múltiple y configuración de columnas.
|
||||
- SwitchField: campos binarios de formularios, como estado activo, VPN, predeterminado o requerido.
|
||||
- Table: listados de usuarios, compañías, personas y departamentos.
|
||||
- Textarea: dirección opcional de las sedes, con varias líneas.
|
||||
- Dialog: primitiva de shadcn/ui, con foco y cierre por teclado.
|
||||
- RDX UI: FormDialog, FormActions, ContextHelp, SelectWithAction, EntityTable y StatisticCard.
|
||||
- RDX UI: FormDialog, FormActions, ContextHelp, SelectWithAction, SwitchField, EntityTable y
|
||||
StatisticCard.
|
||||
|
||||
## Convención de navegación
|
||||
|
||||
@ -86,6 +88,8 @@ Guías oficiales: [monorepos](https://ui.shadcn.com/docs/monorepo) e [instalaci
|
||||
|
||||
ContextHelp muestra una explicación breve y permite desplegar ejemplos y recomendaciones mediante teclado. EntityHelp aporta contenido específico en español e inglés para compañías, personas, departamentos, sedes, asignaciones y todas las secciones de cuentas y accesos. Los catálogos explican si la definición es global o exclusiva de la compañía. Los ejemplos son orientativos: nunca se convierten en valores del formulario.
|
||||
|
||||
La ayuda se presenta en una tarjeta con icono circular, título y explicación; debajo, una banda desplegable contiene los ejemplos y recomendaciones. ContextHelp admite `icon?: ReactNode` para sustituir el icono informativo predeterminado, por ejemplo `icon={<UsersIcon />}`. El icono es decorativo y hereda el tamaño y color del contenedor. Se conservan los tokens del tema, los textos aportados por cada módulo y el despliegue nativo por teclado.
|
||||
|
||||
El botón Ayuda de Cuentas y accesos y Catálogos informáticos permite consultar la explicación de la sección sin abrir un alta. La misma ayuda aparece dentro de los modales de alta y edición. FormDialog utiliza una variante amplia para personas y formularios con más campos; el número de campos no cambia la navegación a una página propia.
|
||||
|
||||
La ayuda de los formularios se coloca en su parte inferior, inmediatamente antes de FormActions. Este pie compartido reúne Guardar y Cancelar dentro del formulario, conservando el envío y el cierre accesible del diálogo. Los diálogos de consulta mantienen únicamente Cerrar.
|
||||
@ -101,6 +105,18 @@ Su contrato recibe id, etiqueta, opciones, placeholder, valor controlado y callb
|
||||
|
||||
No conoce compañías, HTTP, formularios ni modales: esas responsabilidades pertenecen al consumidor. No contiene textos propios que deban traducirse.
|
||||
|
||||
## SwitchField: activación y desactivación
|
||||
|
||||
`SwitchField` vive en `@repo/rdx-ui/components/switch-field`. Compone el Switch de shadcn/ui
|
||||
con su etiqueta accesible y recibe un valor controlado, `onCheckedChange`, `id`, texto y estado
|
||||
deshabilitado. No conoce React Hook Form ni ningún DTO: el formulario aporta el valor y actualiza
|
||||
su fuente de verdad.
|
||||
|
||||
Se usa para cambios binarios que expresan una propiedad o un estado de la entidad, incluidos
|
||||
activo, requerido, contacto, VPN, predeterminado y relación simétrica. No sustituye Checkbox en
|
||||
filtros ni en elecciones de varias opciones, donde la casilla comunica correctamente inclusión,
|
||||
exclusión o selección múltiple.
|
||||
|
||||
### Formularios que permiten alta rápida
|
||||
|
||||
Sistema permite crear un sistema propio de la compañía mediante un modal adicional. Entorno permite crear un entorno para el sistema activo seleccionado; su acción no aparece antes de elegir un sistema. Los campos fijos en edición no ofrecen alta rápida. Uso y otros valores cerrados tampoco ofrecen altas.
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
@ -48,10 +49,12 @@ export function BrandForm(props: BrandFormProps) {
|
||||
onSelect={selectLogo}
|
||||
/>
|
||||
{!quickCreate && (
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" {...form.register("active")} />
|
||||
{t("brands.active")}
|
||||
</label>
|
||||
<SwitchField
|
||||
checked={form.watch("active")}
|
||||
id={`${id}-active`}
|
||||
label={t("brands.active")}
|
||||
onCheckedChange={(active) => form.setValue("active", active, { shouldDirty: true })}
|
||||
/>
|
||||
)}
|
||||
</fieldset>
|
||||
{(mutation.error || form.formState.errors.name || form.formState.errors.active) && (
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
@ -33,20 +34,18 @@ export function CatalogRelationForm(props: CatalogRelationFormProps) {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input
|
||||
checked={symmetric}
|
||||
disabled={!!item}
|
||||
onChange={(event) => {
|
||||
form.setValue("symmetric", event.target.checked);
|
||||
if (event.target.checked) {
|
||||
form.setValue("inverseName", form.getValues("name"));
|
||||
}
|
||||
}}
|
||||
type="checkbox"
|
||||
/>
|
||||
{t("symmetric")}
|
||||
</label>
|
||||
<SwitchField
|
||||
checked={symmetric}
|
||||
disabled={!!item}
|
||||
id={`${id}-symmetric`}
|
||||
label={t("symmetric")}
|
||||
onCheckedChange={(isSymmetric) => {
|
||||
form.setValue("symmetric", isSymmetric);
|
||||
if (isSymmetric) {
|
||||
form.setValue("inverseName", form.getValues("name"));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{!symmetric && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${id}-inverse`}>{t("inverseName")}</Label>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -73,10 +73,19 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
<SwitchField
|
||||
checked={binding.required}
|
||||
disabled={mutation.isPending}
|
||||
id={`${id}-required-${binding.fieldId}`}
|
||||
label={
|
||||
<>
|
||||
{t("required")}
|
||||
<span className="sr-only">
|
||||
{": "}
|
||||
{field?.values.name ?? ""}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue(
|
||||
"fields",
|
||||
@ -89,13 +98,6 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-required-${binding.fieldId}`}>
|
||||
{t("required")}
|
||||
<span className="sr-only">
|
||||
{": "}
|
||||
{field?.values.name ?? ""}
|
||||
</span>
|
||||
</Label>
|
||||
<Button
|
||||
aria-label={t("remove", { name: field?.values.name ?? "" })}
|
||||
disabled={mutation.isPending}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
@ -61,18 +62,19 @@ export function SupportForm(props: SupportFormProps) {
|
||||
value={values.state}
|
||||
/>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" {...form.register("requiresVpn")} />
|
||||
{t("support.requiresVpn")}
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input
|
||||
disabled={values.state !== "available"}
|
||||
type="checkbox"
|
||||
{...form.register("preferred")}
|
||||
/>
|
||||
{t("support.preferred")}
|
||||
</label>
|
||||
<SwitchField
|
||||
checked={values.requiresVpn}
|
||||
id={`${prefix}-requires-vpn`}
|
||||
label={t("support.requiresVpn")}
|
||||
onCheckedChange={(requiresVpn) => form.setValue("requiresVpn", requiresVpn)}
|
||||
/>
|
||||
<SwitchField
|
||||
checked={values.preferred}
|
||||
disabled={values.state !== "available"}
|
||||
id={`${prefix}-preferred`}
|
||||
label={t("support.preferred")}
|
||||
onCheckedChange={(preferred) => form.setValue("preferred", preferred)}
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${prefix}-notes`}>{t("support.notes")}</Label>
|
||||
<textarea
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
@ -28,10 +29,12 @@ export function SupportToolForm(props: SupportToolFormProps) {
|
||||
{...form.register("name")}
|
||||
/>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" {...form.register("active")} />
|
||||
{t("support.active")}
|
||||
</label>
|
||||
<SwitchField
|
||||
checked={form.watch("active")}
|
||||
id={`${id}-active`}
|
||||
label={t("support.active")}
|
||||
onCheckedChange={(active) => form.setValue("active", active, { shouldDirty: true })}
|
||||
/>
|
||||
</fieldset>
|
||||
<SupportFeedback
|
||||
error={mutation.error}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
@ -41,10 +42,12 @@ export function WorkstationForm(props: WorkstationFormProps) {
|
||||
<Label htmlFor={prefix}>{t("name")}</Label>
|
||||
<Input id={prefix} maxLength={100} required {...form.register("name")} />
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" {...form.register("active")} />
|
||||
{t("active")}
|
||||
</label>
|
||||
<SwitchField
|
||||
checked={values.active}
|
||||
id={`${prefix}-active`}
|
||||
label={t("active")}
|
||||
onCheckedChange={(active) => form.setValue("active", active)}
|
||||
/>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${prefix}-site`}>{t("site")}</Label>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { SelectAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -38,15 +38,13 @@ export function AccessFieldControl({
|
||||
const { t } = useTranslation("access");
|
||||
if (field.type === "boolean") {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={Boolean(value)}
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
onCheckedChange={(checked) => form.setValue(field.key, checked === true)}
|
||||
/>
|
||||
<Label htmlFor={id}>{t(field.key)}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={Boolean(value)}
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
label={t(field.key)}
|
||||
onCheckedChange={(checked) => form.setValue(field.key, checked)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (field.type === "permissions") {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { useId } from "react";
|
||||
@ -70,17 +70,13 @@ export function AssignmentForm(props: AssignmentFormProps) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={!form.watch("endDate") && form.watch("isPrimary")}
|
||||
disabled={!!form.watch("endDate")}
|
||||
id={`${id}-primary`}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue("isPrimary", checked === true, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-primary`}>{t("primaryDepartment")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={!form.watch("endDate") && form.watch("isPrimary")}
|
||||
disabled={!!form.watch("endDate")}
|
||||
id={`${id}-primary`}
|
||||
label={t("primaryDepartment")}
|
||||
onCheckedChange={(checked) => form.setValue("isPrimary", checked, { shouldDirty: true })}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">{t("assignmentHelp")}</p>
|
||||
<CompanyFeedback
|
||||
error={mutation.error}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { useId } from "react";
|
||||
@ -28,16 +28,12 @@ export function CompanyForm(props: CompanyFormProps) {
|
||||
<Label htmlFor={`${id}-name`}>{t("name")}</Label>
|
||||
<Input id={`${id}-name`} maxLength={100} required {...form.register("name")} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={form.watch("active")}
|
||||
id={`${id}-active`}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue("active", checked === true, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-active`}>{t("activeCompany")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={form.watch("active")}
|
||||
id={`${id}-active`}
|
||||
label={t("activeCompany")}
|
||||
onCheckedChange={(checked) => form.setValue("active", checked, { shouldDirty: true })}
|
||||
/>
|
||||
<CompanyLogoField
|
||||
companyId={savedCompanyId}
|
||||
disabled={mutation.isPending}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { useId } from "react";
|
||||
@ -22,17 +22,13 @@ export function DepartmentForm(props: DepartmentFormProps) {
|
||||
<Label htmlFor={id}>{t("name")}</Label>
|
||||
<Input id={id} maxLength={100} required {...form.register("name")} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={form.watch("active")}
|
||||
disabled={requireActive}
|
||||
id={`${id}-active`}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue("active", checked === true, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-active`}>{t("activeDepartment")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={form.watch("active")}
|
||||
disabled={requireActive}
|
||||
id={`${id}-active`}
|
||||
label={t("activeDepartment")}
|
||||
onCheckedChange={(checked) => form.setValue("active", checked, { shouldDirty: true })}
|
||||
/>
|
||||
{department && (
|
||||
<p className="text-sm text-muted-foreground">{t("departmentDeactivationHelp")}</p>
|
||||
)}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -35,16 +35,12 @@ export function InterventionTemplateForm(props: InterventionTemplateFormProps) {
|
||||
{...form.register("description")}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={form.watch("active")}
|
||||
id={`${prefix}-active`}
|
||||
onCheckedChange={(value) =>
|
||||
form.setValue("active", value === true, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${prefix}-active`}>{t("active")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={form.watch("active")}
|
||||
id={`${prefix}-active`}
|
||||
label={t("active")}
|
||||
onCheckedChange={(active) => form.setValue("active", active, { shouldDirty: true })}
|
||||
/>
|
||||
<h3 className="font-medium">{t("interventionTemplates.steps")}</h3>
|
||||
{steps.fields.map((step, index) => (
|
||||
<TemplateStepFields
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -73,16 +73,14 @@ export function TemplateStepFields({
|
||||
{...form.register(`steps.${index}.instructions`)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Checkbox
|
||||
checked={form.watch(`steps.${index}.required`)}
|
||||
id={`${id}-required`}
|
||||
onCheckedChange={(value) =>
|
||||
form.setValue(`steps.${index}.required`, value === true, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-required`}>{t("interventionTemplates.required")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={form.watch(`steps.${index}.required`)}
|
||||
id={`${id}-required`}
|
||||
label={t("interventionTemplates.required")}
|
||||
onCheckedChange={(required) =>
|
||||
form.setValue(`steps.${index}.required`, required, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -87,16 +87,12 @@ export function InterventionSteps({
|
||||
{...form.register(`steps.${index}.instructions`)}
|
||||
/>
|
||||
</details>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Checkbox
|
||||
checked={form.watch(`steps.${index}.required`)}
|
||||
id={`${id}-required`}
|
||||
onCheckedChange={(value) =>
|
||||
form.setValue(`steps.${index}.required`, value === true)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-required`}>{t("interventionTemplates.required")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={form.watch(`steps.${index}.required`)}
|
||||
id={`${id}-required`}
|
||||
label={t("interventionTemplates.required")}
|
||||
onCheckedChange={(required) => form.setValue(`steps.${index}.required`, required)}
|
||||
/>
|
||||
<SelectWithAction
|
||||
id={`${id}-state`}
|
||||
label={t("interventions.state")}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { useId } from "react";
|
||||
@ -36,27 +36,21 @@ export function PersonForm(props: PersonFormProps) {
|
||||
<Input id={`${id}-phone`} maxLength={50} type="tel" {...form.register("phone")} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={form.watch("active")}
|
||||
disabled={requireActive}
|
||||
id={`${id}-active`}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue("active", checked === true, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-active`}>{t("activePerson")}</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={form.watch("isContact")}
|
||||
id={`${id}-contact`}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue("isContact", checked === true, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-contact`}>{t("isContact")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={form.watch("active")}
|
||||
disabled={requireActive}
|
||||
id={`${id}-active`}
|
||||
label={t("activePerson")}
|
||||
onCheckedChange={(active) => form.setValue("active", active, { shouldDirty: true })}
|
||||
/>
|
||||
<SwitchField
|
||||
checked={form.watch("isContact")}
|
||||
id={`${id}-contact`}
|
||||
label={t("isContact")}
|
||||
onCheckedChange={(isContact) =>
|
||||
form.setValue("isContact", isContact, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
{person?.active && !form.watch("active") && (
|
||||
<p className="text-sm text-muted-foreground">{t("deactivationHelp")}</p>
|
||||
)}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -125,14 +126,13 @@ export function PrinterConfigurationForm(props: PrinterConfigurationFormProps) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
{...form.register("isDefault")}
|
||||
disabled={!["pending", "configured"].includes(values.state)}
|
||||
/>
|
||||
{t("printers.default")}
|
||||
</label>
|
||||
<SwitchField
|
||||
checked={values.isDefault}
|
||||
disabled={!["pending", "configured"].includes(values.state)}
|
||||
id={`${prefix}-default`}
|
||||
label={t("printers.default")}
|
||||
onCheckedChange={(isDefault) => form.setValue("isDefault", isDefault)}
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${prefix}-notes`}>{t("mail.notes")}</Label>
|
||||
<Textarea
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -54,14 +54,12 @@ export function ProcessTemplateForm(props: ProcessTemplateFormProps) {
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground">{t("processes.instructionsHelp")}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={form.watch("active")}
|
||||
id={`${prefix}-active`}
|
||||
onCheckedChange={(v) => form.setValue("active", v === true)}
|
||||
/>
|
||||
<Label htmlFor={`${prefix}-active`}>{t("active")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={form.watch("active")}
|
||||
id={`${prefix}-active`}
|
||||
label={t("active")}
|
||||
onCheckedChange={(active) => form.setValue("active", active)}
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${prefix}-notes`}>{t("mail.notes")}</Label>
|
||||
<Textarea id={`${prefix}-notes`} maxLength={4000} rows={3} {...form.register("notes")} />
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -104,14 +104,12 @@ export function RemoteConnectionForm(props: RemoteConnectionFormProps) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={v.requiresVpn}
|
||||
id={`${prefix}-vpn`}
|
||||
onCheckedChange={(checked) => form.setValue("requiresVpn", checked === true)}
|
||||
/>
|
||||
<Label htmlFor={`${prefix}-vpn`}>{t("remote.requiresVpn")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={v.requiresVpn}
|
||||
id={`${prefix}-vpn`}
|
||||
label={t("remote.requiresVpn")}
|
||||
onCheckedChange={(requiresVpn) => form.setValue("requiresVpn", requiresVpn)}
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${prefix}-notes`}>{t("mail.notes")}</Label>
|
||||
<Textarea
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -44,14 +44,12 @@ export function TeleworkAuthorizationForm(props: TeleworkAuthorizationFormProps)
|
||||
value={values.personId}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={values.active}
|
||||
id={`${prefix}-active`}
|
||||
onCheckedChange={(checked) => form.setValue("active", checked === true)}
|
||||
/>
|
||||
<Label htmlFor={`${prefix}-active`}>{t("telework.active")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={values.active}
|
||||
id={`${prefix}-active`}
|
||||
label={t("telework.active")}
|
||||
onCheckedChange={(active) => form.setValue("active", active)}
|
||||
/>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${prefix}-start`}>{t("telework.startsOn")}</Label>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
@ -86,10 +87,12 @@ export function WorkProfileForm(props: WorkProfileFormProps) {
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" {...form.register("active")} />
|
||||
{t("profiles.active")}
|
||||
</label>
|
||||
<SwitchField
|
||||
checked={values.active}
|
||||
id={`${prefix}-active`}
|
||||
label={t("profiles.active")}
|
||||
onCheckedChange={(active) => form.setValue("active", active)}
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${prefix}-notes`}>{t("profiles.notes")}</Label>
|
||||
<Textarea
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { useId } from "react";
|
||||
@ -55,17 +55,13 @@ export function UserForm(props: UserFormProps) {
|
||||
</div>
|
||||
)}
|
||||
{user && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={form.watch("active")}
|
||||
disabled={user.isInitialAdmin}
|
||||
id={`${id}-active`}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue("active", checked === true, { shouldDirty: true })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-active`}>{t("active")}</Label>
|
||||
</div>
|
||||
<SwitchField
|
||||
checked={form.watch("active")}
|
||||
disabled={user.isInitialAdmin}
|
||||
id={`${id}-active`}
|
||||
label={t("active")}
|
||||
onCheckedChange={(active) => form.setValue("active", active, { shouldDirty: true })}
|
||||
/>
|
||||
)}
|
||||
{user?.isInitialAdmin && (
|
||||
<p className="text-xs text-muted-foreground">{t("adminProtected")}</p>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
|
||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||
import { SwitchField } from "@repo/rdx-ui/components/switch-field";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||
import { Input } from "@repo/shadcn-ui/components/input";
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Textarea } from "@repo/shadcn-ui/components/textarea";
|
||||
@ -95,10 +95,19 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
<SwitchField
|
||||
checked={binding.required}
|
||||
disabled={mutation.isPending}
|
||||
id={`${id}-required-${binding.fieldId}`}
|
||||
label={
|
||||
<>
|
||||
{t("required")}
|
||||
<span className="sr-only">
|
||||
{": "}
|
||||
{field?.values.name ?? ""}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue(
|
||||
"fields",
|
||||
@ -111,13 +120,6 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`${id}-required-${binding.fieldId}`}>
|
||||
{t("required")}
|
||||
<span className="sr-only">
|
||||
{": "}
|
||||
{field?.values.name ?? ""}
|
||||
</span>
|
||||
</Label>
|
||||
<Button
|
||||
aria-label={t("remove", { name: field?.values.name ?? "" })}
|
||||
disabled={mutation.isPending}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { ChevronDownIcon, InfoIcon } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export function ContextHelp({
|
||||
title,
|
||||
description,
|
||||
@ -5,6 +8,7 @@ export function ContextHelp({
|
||||
guidance,
|
||||
examplesLabel,
|
||||
scopeNote,
|
||||
icon = <InfoIcon />,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
@ -12,17 +16,35 @@ export function ContextHelp({
|
||||
guidance: string;
|
||||
examplesLabel: string;
|
||||
scopeNote?: string | undefined;
|
||||
icon?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<aside aria-label={title} className="space-y-2 rounded-lg border bg-muted/40 p-4 text-sm">
|
||||
<p className="font-medium">{title}</p>
|
||||
<p className="text-muted-foreground">{description}</p>
|
||||
{scopeNote && <p>{scopeNote}</p>}
|
||||
<details className="group">
|
||||
<summary className="cursor-pointer rounded-sm font-medium text-primary focus-visible:outline-2 focus-visible:outline-ring">
|
||||
<aside
|
||||
aria-label={title}
|
||||
className="space-y-4 rounded-xl border bg-muted/30 p-4 text-sm sm:p-5"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="flex size-10 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary [&_svg]:size-5"
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
<div className="min-w-0 space-y-1 pt-1">
|
||||
<p className="text-base font-semibold text-foreground">{title}</p>
|
||||
<p className="leading-relaxed text-muted-foreground">{description}</p>
|
||||
{scopeNote && <p className="leading-relaxed text-muted-foreground">{scopeNote}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<details className="group overflow-hidden rounded-lg border border-primary/10">
|
||||
<summary className="flex cursor-pointer list-none items-center gap-3 rounded-lg bg-primary/5 px-4 py-3 font-semibold text-primary focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring [&::-webkit-details-marker]:hidden">
|
||||
<ChevronDownIcon
|
||||
aria-hidden="true"
|
||||
className="size-4 shrink-0 -rotate-90 transition-transform group-open:rotate-0 motion-reduce:transition-none"
|
||||
/>
|
||||
{examplesLabel}
|
||||
</summary>
|
||||
<div className="mt-2 space-y-2 text-muted-foreground">
|
||||
<div className="space-y-2 px-4 py-3 leading-relaxed text-muted-foreground">
|
||||
<p>{examples}</p>
|
||||
<p>{guidance}</p>
|
||||
</div>
|
||||
|
||||
18
packages/rdx-ui/src/components/switch-field.tsx
Normal file
18
packages/rdx-ui/src/components/switch-field.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { Switch } from "@repo/shadcn-ui/components/switch";
|
||||
import type { ComponentProps, ReactNode } from "react";
|
||||
|
||||
type SwitchFieldProps = Readonly<
|
||||
ComponentProps<typeof Switch> & {
|
||||
label: ReactNode;
|
||||
}
|
||||
>;
|
||||
|
||||
export function SwitchField({ id, label, ...switchProps }: SwitchFieldProps) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id={id} {...switchProps} />
|
||||
<Label htmlFor={id}>{label}</Label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user