Compare commits

..

2 Commits

Author SHA1 Message Date
ef4f41493a Cambio de tema 2026-10-09 20:30:51 +02:00
791fa06ddd Mejora en los cuadros de diálogo 2026-10-09 20:30:41 +02:00
50 changed files with 2783 additions and 2582 deletions

View File

@ -31,7 +31,7 @@
"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
monospace;
--color-shell-exterior: #000;
--color-shell-content: #f7f9fb;
--color-shell-content: var(--color-background);
--color-shell-header: #fff;
--color-shell-sidebar: #0e282c;
--color-shell-sidebar-top: #0c252a;

View File

@ -285,3 +285,13 @@ contrae el menú y conservan la selección y las ayudas del botón de navegació
Componentes de @repo/rdx-ui independientes de los DTO, consultas y endpoints de Metis Desk. FieldSections recibe secciones, elementos con sectionId opcional y renderItem; presenta una cuadrícula plana cuando no hay secciones, o bloques desplegables accesibles con descripción y «Sin sección». Admite presentación de valores como listas de descripción y apertura ante errores externos.
TemplateSectionsEditor recibe secciones, referencias de campos, nombres, textos traducidos, generador de identidad y onChange. Permite ordenar secciones/campos, editar nombre/descripción, asignar campos y mantener su obligatoriedad mediante SwitchField. El módulo consumidor conserva el estado en React Hook Form y genera UUID v7. Los helpers removeTemplateSection y copyTemplateLayout conservan referencias y remapean identidades respectivamente. No se modifican los componentes vendor.
## Formularios modales adaptativos
`FormDialog` admite `size="small" | "medium" | "large"`. Los formularios sencillos mantienen el tamaño pequeño; personas y usuarios emplean el intermedio. `wide` sigue siendo un alias del tamaño grande: 90 % del ancho disponible, con máximo de 1.200 px y márgenes en móvil. No se ensanchan automáticamente las altas sencillas.
La cabecera permanece visible. Un formulario modal utiliza una raíz flex con altura mínima cero, `FormBody` para campos, errores y ayuda, y `FormActions` como hermano final fuera del cuerpo desplazable. Los botones permanecen dentro del formulario HTML original: se conservan envío por teclado, validación nativa, estados pendientes y cancelación. La ayuda queda al final del cuerpo, inmediatamente antes de las acciones. Los diálogos de consulta conservan el cuerpo desplazable y su pie externo.
`FormSection` agrupa datos relacionados mediante título accesible y un borde discreto. Activos, recursos y plantillas lo utilizan para sus datos principales. Las características y las secciones del editor se distribuyen en dos bloques por fila cuando el contenedor dispone de al menos 900 px; los controles dentro de cada bloque mantienen una o dos columnas según el espacio. Una plantilla sin secciones puede aprovechar hasta tres columnas de características. Observaciones y ayuda ocupan toda la fila. La disposición responde al espacio del modal, no solo al ancho del navegador.
El desplazamiento vertical se conserva cuando el contenido supera la altura disponible; no se ocultan campos ni se reduce artificialmente su tamaño para eliminarlo. No se introducen pestañas ni páginas nuevas sin una necesidad específica del formulario.

View File

@ -299,5 +299,9 @@
"assign": "Section",
"help": "Organize fields without changing their values or requirements. Removing a section moves its fields to Unsectioned.",
"empty": "This section has no fields yet."
},
"formLayout": {
"identity": "Identification and assignment",
"template": "Template details"
}
}

View File

@ -299,5 +299,9 @@
"assign": "Sección",
"help": "Organiza los campos sin cambiar sus valores ni su obligatoriedad. Al eliminar una sección, sus campos pasan a «Sin sección».",
"empty": "Esta sección aún no tiene campos."
},
"formLayout": {
"identity": "Identificación y asignación",
"template": "Datos de la plantilla"
}
}

View File

@ -1,4 +1,4 @@
import { FormActions, FormDialog } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody, FormDialog } from "@repo/rdx-ui/components/form-dialog";
import { Button } from "@repo/shadcn-ui/components/button";
import { useTranslation } from "react-i18next";
@ -22,11 +22,13 @@ export function EndRelationshipDialog(props: EndRelationshipDialogProps) {
open
title={t("endRelationship")}
>
<FormBody>
<p className="text-sm">
{relationship.values.name} · {sourceName} → {targetName}
</p>
<p className="text-sm text-muted-foreground">{t("endRelationshipConfirm")}</p>
<InventoryFeedback error={mutation.error} />
</FormBody>
<FormActions>
<Button
disabled={mutation.isPending}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Label } from "@repo/shadcn-ui/components/label";
@ -34,7 +34,8 @@ export function RelationshipForm(props: RelationshipFormProps) {
const id = useId();
return (
<>
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<div className="space-y-2">
<Label htmlFor={`${id}-type`}>{t("relationshipType")}</Label>
<SelectWithAction
@ -43,7 +44,10 @@ export function RelationshipForm(props: RelationshipFormProps) {
id={`${id}-type`}
label={t("relationshipType")}
onValueChange={setTypeId}
options={relationshipTypes.map((item) => ({ value: item.id, label: item.values.name }))}
options={relationshipTypes.map((item) => ({
value: item.id,
label: item.values.name,
}))}
placeholder={t("chooseRelation")}
required
value={typeId}
@ -79,6 +83,7 @@ export function RelationshipForm(props: RelationshipFormProps) {
guidance={t("relationshipGuidance")}
title={t("helpTitle")}
/>
</FormBody>
<FormActions>
<Button disabled={!(typeId && targetId) || mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,6 +1,7 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FieldSections } from "@repo/rdx-ui/components/field-sections";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { FormSection } from "@repo/rdx-ui/components/form-section";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -43,8 +44,10 @@ export function AssetForm(props: AssetFormProps) {
const selectProps = { disabled: mutation.isPending, placeholder: t("noAssignment") };
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-5" disabled={mutation.isPending}>
<FormSection title={t("formLayout.identity")}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-name`}>{t("name")}</Label>
<Input id={`${prefix}-name`} maxLength={100} required {...form.register("name")} />
@ -64,7 +67,12 @@ export function AssetForm(props: AssetFormProps) {
onValueChange={selectTemplate}
options={
asset
? [{ value: asset.values.templateId, label: asset.specification.templateName }]
? [
{
value: asset.values.templateId,
label: asset.specification.templateName,
},
]
: templates.map((item) => ({ value: item.id, label: item.values.name }))
}
placeholder={t("chooseValue")}
@ -130,6 +138,7 @@ export function AssetForm(props: AssetFormProps) {
/>
</div>
</div>
</FormSection>
{fields.length > 0 && (
<section className="space-y-3">
<h3 className="font-medium">{t("characteristics")}</h3>
@ -197,6 +206,7 @@ export function AssetForm(props: AssetFormProps) {
scopeNote={t("assetScope")}
title={t("helpTitle")}
/>
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -29,7 +29,8 @@ export function BrandForm(props: BrandFormProps) {
const { t } = useTranslation("assets");
const id = useId();
return (
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending || isReadingLogo}>
<div className="space-y-2">
<Label htmlFor={id}>{t("name")}</Label>
@ -77,6 +78,7 @@ export function BrandForm(props: BrandFormProps) {
scopeNote={t("globalHelp")}
title={t("helpTitle")}
/>
</FormBody>
<FormActions>
<Button disabled={mutation.isPending || isReadingLogo} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -19,7 +19,8 @@ export function CatalogFieldForm(props: CatalogFieldFormProps) {
const { t } = useTranslation("assets");
const id = useId();
return (
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<div className="space-y-2">
<Label htmlFor={`${id}-name`}>{t("name")}</Label>
<Input id={`${id}-name`} maxLength={100} required {...form.register("name")} />
@ -67,6 +68,7 @@ export function CatalogFieldForm(props: CatalogFieldFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<CatalogHelp kind="field" scope={scope} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -18,7 +18,8 @@ export function CatalogRelationForm(props: CatalogRelationFormProps) {
const { t } = useTranslation("assets");
const id = useId();
return (
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<div className="space-y-2">
<Label htmlFor={id}>{t("name")}</Label>
<Input
@ -49,7 +50,12 @@ export function CatalogRelationForm(props: CatalogRelationFormProps) {
{!symmetric && (
<div className="space-y-2">
<Label htmlFor={`${id}-inverse`}>{t("inverseName")}</Label>
<Input id={`${id}-inverse`} maxLength={100} required {...form.register("inverseName")} />
<Input
id={`${id}-inverse`}
maxLength={100}
required
{...form.register("inverseName")}
/>
</div>
)}
<div className="space-y-2">
@ -61,6 +67,7 @@ export function CatalogRelationForm(props: CatalogRelationFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<CatalogHelp kind="relation" scope={scope} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,6 @@
import { UniqueID } from "@repo/rdx-ddd";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { FormSection } from "@repo/rdx-ui/components/form-section";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { TemplateSectionsEditor } from "@repo/rdx-ui/components/template-sections-editor";
import { Button } from "@repo/shadcn-ui/components/button";
@ -23,7 +24,9 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
const id = useId();
return (
<>
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<FormSection title={t("formLayout.template")}>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor={`${id}-name`}>{t("name")}</Label>
@ -39,6 +42,7 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
/>
</div>
</div>
</FormSection>
<div className="space-y-2">
<Label htmlFor={`${id}-field`}>{t("selectField")}</Label>
<SelectWithAction
@ -103,6 +107,7 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<CatalogHelp kind="template" scope={scope} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -21,7 +21,8 @@ export function SupportForm(props: SupportFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={prefix}>{t("support.tool")}</Label>
@ -39,7 +40,11 @@ export function SupportForm(props: SupportFormProps) {
</div>
<div className="space-y-2">
<Label htmlFor={`${prefix}-destination`}>{t("support.destination")}</Label>
<Input id={`${prefix}-destination`} maxLength={300} {...form.register("destination")} />
<Input
id={`${prefix}-destination`}
maxLength={300}
{...form.register("destination")}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`${prefix}-state`}>{t("support.state")}</Label>
@ -90,6 +95,7 @@ export function SupportForm(props: SupportFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<SupportHelp />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -17,7 +17,8 @@ export function SupportToolForm(props: SupportToolFormProps) {
const { t } = useTranslation("assets");
const id = useId();
return (
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={id}>{t("support.toolName")}</Label>
@ -41,6 +42,7 @@ export function SupportToolForm(props: SupportToolFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<SupportHelp tool />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -36,7 +36,8 @@ export function WorkstationForm(props: WorkstationFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={prefix}>{t("name")}</Label>
@ -173,6 +174,7 @@ export function WorkstationForm(props: WorkstationFormProps) {
scopeNote={t("workstationScope")}
title={t("helpTitle")}
/>
</FormBody>
<FormActions>
<Button
disabled={

View File

@ -1,4 +1,4 @@
import { FormActions, FormDialog } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody, FormDialog } from "@repo/rdx-ui/components/form-dialog";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
import { Label } from "@repo/shadcn-ui/components/label";
@ -23,7 +23,8 @@ export function QuickCreateDialog(props: QuickCreateDialogProps) {
open
title={t(target.kind === "system" ? "quickAddSystem" : "quickAddEnvironment")}
>
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
{target.kind === "environment" && (
<p className="text-sm text-muted-foreground">
{t("quickEnvironmentContext", { name: target.systemName })}
@ -65,6 +66,7 @@ export function QuickCreateDialog(props: QuickCreateDialogProps) {
)}
<p className="text-sm text-muted-foreground">{t("quickCreateNotice")}</p>
<EntityHelp kind={target.kind} scope={scope} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "quickSaveSelect")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { Button } from "@repo/shadcn-ui/components/button";
import type { ComponentType } from "react";
import { useId } from "react";
@ -37,7 +37,8 @@ export function AccessFormFields(
const prefix = useId();
return (
<>
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<div className="grid gap-4 sm:grid-cols-2">
{editableFields.map((field) => (
<div
@ -83,6 +84,7 @@ export function AccessFormFields(
<p className="text-sm text-muted-foreground">{t("configurationHelp")}</p>
)}
<EntityHelp kind={kind} scope={scope} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -45,7 +45,8 @@ export function ApplicationConfigurationForm(props: ApplicationConfigurationForm
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
@ -160,6 +161,7 @@ export function ApplicationConfigurationForm(props: ApplicationConfigurationForm
scopeNote={t("applications.scope")}
title={t("applications.help")}
/>
</FormBody>
<FormActions>
<Button disabled={!ready || mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -21,7 +21,8 @@ export function AssignmentForm(props: AssignmentFormProps) {
const id = useId();
return (
<>
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<div className="space-y-2">
<Label htmlFor={`${id}-department`}>{t("department")}</Label>
<SelectWithAction
@ -75,7 +76,9 @@ export function AssignmentForm(props: AssignmentFormProps) {
disabled={!!form.watch("endDate")}
id={`${id}-primary`}
label={t("primaryDepartment")}
onCheckedChange={(checked) => form.setValue("isPrimary", checked, { shouldDirty: true })}
onCheckedChange={(checked) =>
form.setValue("isPrimary", checked, { shouldDirty: true })
}
/>
<p className="text-xs text-muted-foreground">{t("assignmentHelp")}</p>
<CompanyFeedback
@ -84,6 +87,7 @@ export function AssignmentForm(props: AssignmentFormProps) {
/>
<EntityHelp kind="assignment" />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -44,7 +44,8 @@ export function CertificateConfigurationForm(props: CertificateConfigurationForm
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
@ -142,6 +143,7 @@ export function CertificateConfigurationForm(props: CertificateConfigurationForm
scopeNote={t("certificates.scope")}
title={t("certificates.help")}
/>
</FormBody>
<FormActions>
<Button disabled={!ready || mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -20,7 +20,8 @@ export function CompanyForm(props: CompanyFormProps) {
const { t } = useTranslation("companies");
const id = useId();
return (
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
{showTitle && (
<h2 className="text-lg font-semibold">{t(company ? "editCompany" : "newCompany")}</h2>
)}
@ -54,6 +55,7 @@ export function CompanyForm(props: CompanyFormProps) {
/>
<EntityHelp kind="company" />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -17,7 +17,8 @@ export function DepartmentForm(props: DepartmentFormProps) {
const { t } = useTranslation("companies");
const id = useId();
return (
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<div className="space-y-2">
<Label htmlFor={id}>{t("name")}</Label>
<Input id={id} maxLength={100} required {...form.register("name")} />
@ -38,6 +39,7 @@ export function DepartmentForm(props: DepartmentFormProps) {
/>
<EntityHelp kind="department" />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -40,7 +40,8 @@ export function FolderMappingForm(props: FolderMappingFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<FolderMappingTargets
company={company}
@ -127,6 +128,7 @@ export function FolderMappingForm(props: FolderMappingFormProps) {
scopeNote={t("folders.scope")}
title={t("folders.help")}
/>
</FormBody>
<FormActions>
<Button disabled={mutation.isPending || !ready} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -20,14 +20,17 @@ export function InterventionTemplateForm(props: InterventionTemplateFormProps) {
const { t } = useTranslation("companies");
const prefix = useId();
return (
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-name`}>{t("name")}</Label>
<Input id={`${prefix}-name`} maxLength={160} required {...form.register("name")} />
</div>
<div className="space-y-2">
<Label htmlFor={`${prefix}-description`}>{t("interventionTemplates.description")}</Label>
<Label htmlFor={`${prefix}-description`}>
{t("interventionTemplates.description")}
</Label>
<Textarea
id={`${prefix}-description`}
maxLength={4000}
@ -67,6 +70,7 @@ export function InterventionTemplateForm(props: InterventionTemplateFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<InterventionTemplateHelp />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -52,7 +52,8 @@ export function InterventionForm(props: InterventionFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<InterventionCompanyField
companies={data.companies}
@ -149,6 +150,7 @@ export function InterventionForm(props: InterventionFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<InterventionHelp />
</FormBody>
<FormActions>
<Button
disabled={mutation.isPending || !companyId || !lookups.data || lookups.isError}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { Button } from "@repo/shadcn-ui/components/button";
import { Label } from "@repo/shadcn-ui/components/label";
import { Textarea } from "@repo/shadcn-ui/components/textarea";
@ -44,7 +44,8 @@ export function MailConfigurationForm(props: MailConfigurationFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={disabled}>
{legacy && (
<p className="rounded-md border p-3 text-sm">
@ -109,6 +110,7 @@ export function MailConfigurationForm(props: MailConfigurationFormProps) {
scopeNote={t("mail.scope")}
title={t("mail.help")}
/>
</FormBody>
<FormActions>
<Button disabled={disabled || !ready} type="submit">
{t(disabled ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -31,7 +31,8 @@ export function MailboxUseForm(props: MailboxUseFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-mailbox`}>{t("mailbox.titleSingle")}</Label>
@ -107,6 +108,7 @@ export function MailboxUseForm(props: MailboxUseFormProps) {
guidance={t("mailbox.useGuidance")}
title={t("mailbox.useHelp")}
/>
</FormBody>
<FormActions>
<Button disabled={mutation.isPending || !query.data} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -19,8 +19,8 @@ export function PersonEditorDialog({
formActions
onOpenChange={onOpenChange}
open={open}
size="medium"
title={t(formProps.person ? "editPerson" : "newPerson")}
wide
>
<PersonForm {...formProps} key={formProps.person?.id ?? "new"} />
</FormDialog>

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -17,7 +17,8 @@ export function PersonForm(props: PersonFormProps) {
const { t } = useTranslation("companies");
const id = useId();
return (
<form className="max-w-2xl space-y-6" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor={`${id}-name`}>{t("name")}</Label>
@ -60,6 +61,7 @@ export function PersonForm(props: PersonFormProps) {
/>
<p className="text-sm text-muted-foreground">{t("personHelp")}</p>
<EntityHelp kind="person" />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -47,7 +47,8 @@ export function PrinterConfigurationForm(props: PrinterConfigurationFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
@ -99,7 +100,8 @@ export function PrinterConfigurationForm(props: PrinterConfigurationFormProps) {
id={`${prefix}-connection`}
label={t("printers.connection")}
onValueChange={(value) => {
const parsed = PrinterConfigurationInputSchema.shape.connection.safeParse(value);
const parsed =
PrinterConfigurationInputSchema.shape.connection.safeParse(value);
if (parsed.success) {
form.setValue("connection", parsed.data);
}
@ -159,6 +161,7 @@ export function PrinterConfigurationForm(props: PrinterConfigurationFormProps) {
scopeNote={t("printers.scope")}
title={t("printers.help")}
/>
</FormBody>
<FormActions>
<Button disabled={!ready || mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -18,7 +18,8 @@ export function ProcessTemplateForm(props: ProcessTemplateFormProps) {
const { t } = useTranslation("companies");
const prefix = useId();
return (
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-name`}>{t("name")}</Label>
@ -62,7 +63,12 @@ export function ProcessTemplateForm(props: ProcessTemplateFormProps) {
/>
<div className="space-y-2">
<Label htmlFor={`${prefix}-notes`}>{t("mail.notes")}</Label>
<Textarea id={`${prefix}-notes`} maxLength={4000} rows={3} {...form.register("notes")} />
<Textarea
id={`${prefix}-notes`}
maxLength={4000}
rows={3}
{...form.register("notes")}
/>
</div>
</fieldset>
<ProcessFeedback
@ -70,6 +76,7 @@ export function ProcessTemplateForm(props: ProcessTemplateFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<ProcessHelp template />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -20,7 +20,8 @@ export function ProcessStartForm(props: ProcessStartFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-name`}>{t("name")}</Label>
@ -87,6 +88,7 @@ export function ProcessStartForm(props: ProcessStartFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<ProcessHelp />
</FormBody>
<FormActions>
<Button
disabled={

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -20,7 +20,8 @@ export function ProcessEditForm(props: ProcessEditFormProps) {
const { t } = useTranslation("companies");
const prefix = useId();
return (
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-name`}>{t("name")}</Label>
@ -47,7 +48,12 @@ export function ProcessEditForm(props: ProcessEditFormProps) {
</div>
<div className="space-y-2">
<Label htmlFor={`${prefix}-notes`}>{t("mail.notes")}</Label>
<Textarea id={`${prefix}-notes`} maxLength={4000} rows={3} {...form.register("notes")} />
<Textarea
id={`${prefix}-notes`}
maxLength={4000}
rows={3}
{...form.register("notes")}
/>
</div>
<p className="text-sm text-muted-foreground">{t("processes.resolutionHelp")}</p>
{process.values.tasks.map((task, index) => (
@ -59,6 +65,7 @@ export function ProcessEditForm(props: ProcessEditFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<ProcessHelp />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -40,7 +40,8 @@ export function RemoteConnectionForm(props: RemoteConnectionFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-person`}>{t("remote.person")}</Label>
@ -135,6 +136,7 @@ export function RemoteConnectionForm(props: RemoteConnectionFormProps) {
scopeNote={t("remote.scope")}
title={t("remote.help")}
/>
</FormBody>
<FormActions>
<Button disabled={!ready || mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
import { Label } from "@repo/shadcn-ui/components/label";
@ -17,7 +17,8 @@ export function SiteForm(props: SiteFormProps) {
const { t } = useTranslation("companies");
const id = useId();
return (
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
{showTitle && <h3 className="text-lg font-semibold">{t(site ? "editSite" : "newSite")}</h3>}
<div className="space-y-2">
<Label htmlFor={`${id}-name`}>{t("name")}</Label>
@ -41,6 +42,7 @@ export function SiteForm(props: SiteFormProps) {
/>
<EntityHelp kind="site" />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -23,12 +23,15 @@ export function TeleworkAuthorizationForm(props: TeleworkAuthorizationFormProps)
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-person`}>{t("telework.person")}</Label>
<SelectWithAction
action={fixed ? undefined : { label: t("newPerson"), onClick: () => setQuick(true) }}
action={
fixed ? undefined : { label: t("newPerson"), onClick: () => setQuick(true) }
}
disabled={fixed}
id={`${prefix}-person`}
label={t("telework.person")}
@ -96,6 +99,7 @@ export function TeleworkAuthorizationForm(props: TeleworkAuthorizationFormProps)
scopeNote={t("telework.scope")}
title={t("telework.help")}
/>
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -38,7 +38,8 @@ export function TicketForm(props: TicketFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={mutation.isPending}>
<div className="space-y-2">
<Label htmlFor={prefix}>{t("tickets.company")}</Label>
@ -140,6 +141,7 @@ export function TicketForm(props: TicketFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<TicketHelp />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending || !companyId || !lookups.data} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,5 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -35,7 +35,8 @@ export function WorkProfileForm(props: WorkProfileFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-4" disabled={disabled}>
<div className="space-y-2">
<Label htmlFor={prefix}>{t("profiles.name")}</Label>
@ -119,6 +120,7 @@ export function WorkProfileForm(props: WorkProfileFormProps) {
scopeNote={t("profiles.scope")}
title={t("profiles.help")}
/>
</FormBody>
<FormActions>
<Button disabled={disabled || !inventory.data || !access.data} type="submit">
{t(disabled ? "saving" : "save")}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
import { Label } from "@repo/shadcn-ui/components/label";
@ -15,7 +15,8 @@ export function PasswordForm(props: PasswordFormProps) {
const { t } = useTranslation("identity");
const id = useId();
return (
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
{showTitle && (
<h3 className="font-semibold">
{t(target ? "resetPassword" : "changePassword")}
@ -56,6 +57,7 @@ export function PasswordForm(props: PasswordFormProps) {
</p>
)}
<FormFeedback error={mutation.error} success={mutation.isSuccess} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -24,6 +24,7 @@ export function UserEditorDialog(
}
}}
open={props.open}
size="medium"
title={t(
props.mode === "create"
? "createUser"

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } 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";
@ -16,7 +16,8 @@ export function UserForm(props: UserFormProps) {
const { t } = useTranslation("identity");
const id = useId();
return (
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
{showTitle && <h3 className="font-semibold">{t(user ? "editUser" : "createUser")}</h3>}
<div className="space-y-2">
<Label htmlFor={`${id}-name`}>{t("name")}</Label>
@ -72,6 +73,7 @@ export function UserForm(props: UserFormProps) {
</p>
)}
<FormFeedback error={mutation.error} success={mutation.isSuccess} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -152,5 +152,9 @@
"assign": "Section",
"help": "Organize fields without changing their values or requirements. Removing a section moves its fields to Unsectioned.",
"empty": "This section has no fields yet."
},
"formLayout": {
"identity": "Resource identification",
"template": "Template details"
}
}

View File

@ -152,5 +152,9 @@
"assign": "Sección",
"help": "Organiza los campos sin cambiar sus valores ni su obligatoriedad. Al eliminar una sección, sus campos pasan a «Sin sección».",
"empty": "Esta sección aún no tiene campos."
},
"formLayout": {
"identity": "Identificación del recurso",
"template": "Datos de la plantilla"
}
}

View File

@ -1,4 +1,4 @@
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -19,7 +19,8 @@ export function CatalogFieldForm(props: CatalogFieldFormProps) {
const { t } = useTranslation("resources");
const id = useId();
return (
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<div className="space-y-2">
<Label htmlFor={`${id}-name`}>{t("name")}</Label>
<Input id={`${id}-name`} maxLength={100} required {...form.register("name")} />
@ -67,6 +68,7 @@ export function CatalogFieldForm(props: CatalogFieldFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<CatalogHelp kind="field" scope={scope} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,5 +1,6 @@
import { UniqueID } from "@repo/rdx-ddd";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { FormSection } from "@repo/rdx-ui/components/form-section";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { TemplateSectionsEditor } from "@repo/rdx-ui/components/template-sections-editor";
import { Button } from "@repo/shadcn-ui/components/button";
@ -23,7 +24,9 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
const id = useId();
return (
<>
<form className="space-y-4" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<FormSection title={t("formLayout.template")}>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor={`${id}-name`}>{t("name")}</Label>
@ -61,6 +64,7 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
/>
<p className="text-xs text-muted-foreground">{t("templateUsageHelp")}</p>
</div>
</FormSection>
<div className="space-y-2">
<Label htmlFor={`${id}-field`}>{t("selectField")}</Label>
<SelectWithAction
@ -125,6 +129,7 @@ export function CatalogTemplateForm(props: CatalogTemplateFormProps) {
invalid={Object.keys(form.formState.errors).length > 0}
/>
<CatalogHelp kind="template" scope={scope} />
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -1,6 +1,7 @@
import { ContextHelp } from "@repo/rdx-ui/components/context-help";
import { FieldSections } from "@repo/rdx-ui/components/field-sections";
import { FormActions } from "@repo/rdx-ui/components/form-dialog";
import { FormActions, FormBody } from "@repo/rdx-ui/components/form-dialog";
import { FormSection } from "@repo/rdx-ui/components/form-section";
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
import { Button } from "@repo/shadcn-ui/components/button";
import { Input } from "@repo/shadcn-ui/components/input";
@ -42,8 +43,10 @@ export function ResourceForm(props: ResourceFormProps) {
const prefix = useId();
return (
<>
<form className="space-y-5" onSubmit={submit}>
<form className="flex min-h-0 flex-1 flex-col overflow-hidden" onSubmit={submit}>
<FormBody>
<fieldset className="space-y-5" disabled={mutation.isPending}>
<FormSection title={t("formLayout.identity")}>
<div className="space-y-2">
<Label htmlFor={`${prefix}-name`}>{t("name")}</Label>
<Input id={`${prefix}-name`} maxLength={100} required {...form.register("name")} />
@ -90,6 +93,7 @@ export function ResourceForm(props: ResourceFormProps) {
/>
</div>
</div>
</FormSection>
<ResourceIntegrationFields
fixed={!!resource}
integration={values.integration}
@ -147,6 +151,7 @@ export function ResourceForm(props: ResourceFormProps) {
scopeNote={t(`${helpPrefix}Scope`)}
title={t("helpTitle")}
/>
</FormBody>
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}

View File

@ -29,9 +29,13 @@ export function FieldSections<T extends { sectionId?: string | undefined }>({
}, [hasErrors]);
const content = (fields: readonly T[]) =>
values ? (
<dl className="grid gap-4 sm:grid-cols-2">{fields.map(renderItem)}</dl>
<dl className="grid gap-4 sm:grid-cols-2 @min-[900px]:grid-cols-3">
{fields.map(renderItem)}
</dl>
) : (
<div className="grid gap-4 sm:grid-cols-2">{fields.map(renderItem)}</div>
<div className="grid gap-4 sm:grid-cols-2 @min-[900px]:grid-cols-3">
{fields.map(renderItem)}
</div>
);
if (!sections.length) {
return content(items);
@ -40,7 +44,7 @@ export function FieldSections<T extends { sectionId?: string | undefined }>({
const ungrouped = items.filter((item) => !(item.sectionId && known.has(item.sectionId)));
return (
<div
className="space-y-3"
className="grid items-start gap-4 @min-[900px]:grid-cols-2"
onInvalidCapture={(event) => {
// Abrir antes de que el navegador intente enfocar un control inválido oculto.
if (event.target instanceof HTMLElement) {
@ -58,7 +62,7 @@ export function FieldSections<T extends { sectionId?: string | undefined }>({
return null;
}
return (
<details className="rounded-md border" key={section.id} open>
<details className="@container rounded-md border" key={section.id} open>
<summary className="cursor-pointer rounded-md px-4 py-3 text-sm font-medium focus-visible:outline-2 focus-visible:outline-ring">
{section.name}
</summary>
@ -72,7 +76,7 @@ export function FieldSections<T extends { sectionId?: string | undefined }>({
);
})}
{ungrouped.length > 0 && (
<section className="space-y-3">
<section className="col-span-full space-y-3">
<h4 className="text-sm font-medium">{ungroupedLabel}</h4>
{content(ungrouped)}
</section>

View File

@ -18,6 +18,7 @@ export function FormDialog({
cancelLabel,
children,
wide = false,
size,
formActions = false,
}: {
open: boolean;
@ -26,28 +27,39 @@ export function FormDialog({
cancelLabel: string;
children: ReactNode;
wide?: boolean;
size?: "small" | "medium" | "large";
formActions?: boolean;
}) {
const width = size ?? (wide ? "large" : "small");
const widths = {
small: "sm:max-w-xl",
medium: "sm:max-w-3xl",
large: "sm:w-[90vw] sm:max-w-[1200px]",
};
return (
<Dialog onOpenChange={onOpenChange} open={open}>
<DialogContent
className={`max-h-[calc(100dvh-2rem)] overflow-y-auto ${wide ? "sm:max-w-3xl" : "sm:max-w-xl"}`}
className={`flex max-h-[calc(100dvh-2rem)] flex-col gap-0 overflow-hidden p-0 ${widths[width]}`}
showCloseButton={true}
>
<DialogHeader>
<DialogHeader className="shrink-0 border-b px-6 py-5 pr-14">
<DialogTitle className="text-lg font-bold">{title}</DialogTitle>
</DialogHeader>
<div
className="@container flex min-h-0 flex-1 flex-col overflow-y-auto px-6 pt-5 pb-5 [&:has([data-slot=form-body])]:overflow-hidden [&:has([data-slot=form-body])]:pb-0"
data-slot="form-dialog-content"
>
<FormCancelContext.Provider value={formActions ? cancelLabel : undefined}>
{children}
</FormCancelContext.Provider>
</div>
{!formActions && (
<DialogFooter>
<DialogFooter className="shrink-0 border-t px-6 py-4">
<DialogClose render={<Button type="button" variant="outline" />}>
{cancelLabel}
</DialogClose>
</DialogFooter>
)}
</DialogContent>
</Dialog>
);
@ -56,7 +68,7 @@ export function FormDialog({
export function FormActions({ children }: { children: ReactNode }) {
const cancelLabel = useContext(FormCancelContext);
return (
<DialogFooter className="border-t pt-4">
<DialogFooter className="shrink-0 border-t bg-popover py-4">
{cancelLabel && (
<DialogClose render={<Button type="button" variant="outline" />}>{cancelLabel}</DialogClose>
)}
@ -64,3 +76,12 @@ export function FormActions({ children }: { children: ReactNode }) {
</DialogFooter>
);
}
/** El cuerpo se desplaza dentro del formulario; las acciones conservan su submit nativo. */
export function FormBody({ children }: { children: ReactNode }) {
return (
<div className="min-h-0 flex-1 space-y-5 overflow-y-auto pb-5 pr-1" data-slot="form-body">
{children}
</div>
);
}

View File

@ -0,0 +1,15 @@
import type { ReactNode } from "react";
import { useId } from "react";
/** Bloque visual reutilizable con título accesible; no altera validación ni datos. */
export function FormSection({ title, children }: { title: string; children: ReactNode }) {
const id = useId();
return (
<section aria-labelledby={id} className="space-y-4 rounded-lg border p-4">
<h3 className="text-sm font-semibold" id={id}>
{title}
</h3>
{children}
</section>
);
}

View File

@ -87,8 +87,13 @@ export function TemplateSectionsEditor({
</Button>
</div>
<p className="text-xs text-muted-foreground">{labels.help}</p>
<div className="grid items-start gap-4 @min-[900px]:grid-cols-2">
{sections.map((section, index) => (
<fieldset className="space-y-3 rounded-md border p-3" disabled={disabled} key={section.id}>
<fieldset
className="space-y-3 rounded-md border p-3"
disabled={disabled}
key={section.id}
>
<legend className="sr-only">{section.name || labels.name}</legend>
<div className="flex items-end gap-2">
<div className="min-w-0 flex-1 space-y-2">
@ -157,6 +162,7 @@ export function TemplateSectionsEditor({
{fieldList(section.id)}
</fieldset>
))}
</div>
{fieldList()}
</section>
);

View File

@ -5,111 +5,111 @@
@custom-variant dark (&:is(.dark *));
:root {
--background: oklch(0.9916 0.0050 228.8162);
--foreground: oklch(0.2012 0.0307 210.2063);
--background: oklch(0.9911 0 0);
--foreground: oklch(0.1763 0.0140 258.3572);
--card: oklch(1.0000 0 0);
--card-foreground: oklch(0.2012 0.0307 210.2063);
--card-foreground: oklch(0.1763 0.0140 258.3572);
--popover: oklch(1.0000 0 0);
--popover-foreground: oklch(0.2395 0.0455 282.4182);
--primary: oklch(0.7040 0.1400 182.5030);
--primary-foreground: oklch(0.9911 0 0);
--secondary: oklch(0.9507 0.0148 196.9072);
--secondary-foreground: oklch(0.4011 0.0399 210.3561);
--muted: oklch(0.9604 0.0096 196.9971);
--muted-foreground: oklch(0.4999 0.0296 211.7976);
--accent: oklch(0.9104 0.0460 174.0259);
--accent-foreground: oklch(0.4524 0.0772 194.7689);
--destructive: oklch(0.5993 0.1597 24.9573);
--popover-foreground: oklch(0.1763 0.0140 258.3572);
--primary: oklch(0.5882 0.2309 28.2936);
--primary-foreground: oklch(1.0000 0 0);
--secondary: oklch(0.3303 0.0753 267.1046);
--secondary-foreground: oklch(1.0000 0 0);
--muted: oklch(0.9674 0.0013 286.3752);
--muted-foreground: oklch(0.5517 0.0138 285.9385);
--accent: oklch(0.8342 0.0950 7.8239);
--accent-foreground: oklch(0.3155 0.1064 18.1157);
--destructive: oklch(0.3999 0.1641 29.2339);
--destructive-foreground: oklch(1.0000 0 0);
--border: oklch(0.9093 0.0098 204.9097);
--input: oklch(0.9093 0.0098 204.9097);
--ring: oklch(0.7040 0.1400 182.5030);
--chart-1: oklch(0.7040 0.1400 182.5030);
--chart-2: oklch(0.6700 0.1336 230.0356);
--chart-3: oklch(0.7217 0.1638 297.0816);
--chart-4: oklch(0.8614 0.1165 74.2273);
--chart-5: oklch(0.8975 0.0740 131.2181);
--sidebar: oklch(0.9794 0.0045 214.3282);
--sidebar-foreground: oklch(0.2514 0.0307 209.6770);
--sidebar-primary: oklch(0.7040 0.1400 182.5030);
--sidebar-primary-foreground: oklch(0.9859 0.0040 286.3269);
--sidebar-accent: oklch(0.9104 0.0460 174.0259);
--sidebar-accent-foreground: oklch(0.2395 0.0455 282.4182);
--sidebar-border: oklch(0.9202 0.0202 200.6310);
--sidebar-ring: oklch(0.5606 0.1650 288.1741);
--font-sans: Inter, sans-serif;
--font-serif: Georgia, serif;
--font-mono: JetBrains Mono, monospace;
--radius: 0.5rem;
--border: oklch(0.9197 0.0040 286.3202);
--input: oklch(0.9197 0.0040 286.3202);
--ring: oklch(0.5882 0.2309 28.2936);
--chart-1: oklch(0.5882 0.2309 28.2936);
--chart-2: oklch(0.3303 0.0753 267.1046);
--chart-3: oklch(0.8342 0.0950 7.8239);
--chart-4: oklch(0.8948 0.0509 64.2066);
--chart-5: oklch(0.7118 0.0129 286.0665);
--sidebar: oklch(1.0000 0 0);
--sidebar-foreground: oklch(0.3703 0.0119 285.8054);
--sidebar-primary: oklch(0.5882 0.2309 28.2936);
--sidebar-primary-foreground: oklch(1.0000 0 0);
--sidebar-accent: oklch(0.9674 0.0013 286.3752);
--sidebar-accent-foreground: oklch(0.1763 0.0140 258.3572);
--sidebar-border: oklch(0.9674 0.0013 286.3752);
--sidebar-ring: oklch(0.5882 0.2309 28.2936);
--font-sans: 'Inter', sans-serif;
--font-serif: 'Georgia', serif;
--font-mono: 'JetBrains Mono', monospace;
--radius: 0.75rem;
--shadow-x: 0px;
--shadow-y: 4px;
--shadow-blur: 10px;
--shadow-spread: 0px;
--shadow-opacity: 0.12;
--shadow-color: #2d2d53;
--shadow-2xs: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.06);
--shadow-xs: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.06);
--shadow-sm: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 1px 2px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 1px 2px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow-md: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 2px 4px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow-lg: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 4px 6px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow-xl: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 8px 10px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow-2xl: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.30);
--tracking-normal: 0em;
--shadow-spread: -2px;
--shadow-opacity: 0.1;
--shadow-color: 0 0% 0%;
--shadow-2xs: 0px 4px 10px -2px hsl(0 0% 0% / 0.05);
--shadow-xs: 0px 4px 10px -2px hsl(0 0% 0% / 0.05);
--shadow-sm: 0px 4px 10px -2px hsl(0 0% 0% / 0.10), 0px 1px 2px -3px hsl(0 0% 0% / 0.10);
--shadow: 0px 4px 10px -2px hsl(0 0% 0% / 0.10), 0px 1px 2px -3px hsl(0 0% 0% / 0.10);
--shadow-md: 0px 4px 10px -2px hsl(0 0% 0% / 0.10), 0px 2px 4px -3px hsl(0 0% 0% / 0.10);
--shadow-lg: 0px 4px 10px -2px hsl(0 0% 0% / 0.10), 0px 4px 6px -3px hsl(0 0% 0% / 0.10);
--shadow-xl: 0px 4px 10px -2px hsl(0 0% 0% / 0.10), 0px 8px 10px -3px hsl(0 0% 0% / 0.10);
--shadow-2xl: 0px 4px 10px -2px hsl(0 0% 0% / 0.25);
--tracking-normal: -0.02em;
--spacing: 0.25rem;
}
.dark {
--background: oklch(0.1396 0.0155 195.5244);
--foreground: oklch(0.9396 0.0097 204.9127);
--card: oklch(0.1788 0.0202 201.3265);
--card-foreground: oklch(0.9396 0.0097 204.9127);
--popover: oklch(0.2101 0.0257 280.5303);
--popover-foreground: oklch(0.9294 0.0202 286.0004);
--primary: oklch(0.7040 0.1400 182.5030);
--background: oklch(0.1645 0.0086 274.3354);
--foreground: oklch(0.9911 0 0);
--card: oklch(0.2145 0.0184 270.4182);
--card-foreground: oklch(0.9911 0 0);
--popover: oklch(0.2145 0.0184 270.4182);
--popover-foreground: oklch(0.9911 0 0);
--primary: oklch(0.6453 0.2404 27.3106);
--primary-foreground: oklch(1.0000 0 0);
--secondary: oklch(0.2413 0.0307 198.8697);
--secondary-foreground: oklch(0.8496 0.0299 199.2418);
--muted: oklch(0.2185 0.0195 201.5676);
--muted-foreground: oklch(0.6494 0.0304 199.2066);
--accent: oklch(0.4370 0.0780 188.2160);
--accent-foreground: oklch(0.7994 0.0596 196.0230);
--destructive: oklch(0.6593 0.2097 25.0107);
--destructive-foreground: oklch(0.9859 0.0040 286.3269);
--border: oklch(0.2397 0.0193 201.5913);
--input: oklch(0.2397 0.0193 201.5913);
--ring: oklch(0.7040 0.1400 182.5030);
--chart-1: oklch(0.7040 0.1400 182.5030);
--chart-2: oklch(0.6700 0.1336 230.0356);
--chart-3: oklch(0.7217 0.1638 297.0816);
--chart-4: oklch(0.8614 0.1165 74.2273);
--chart-5: oklch(0.8975 0.0740 131.2181);
--sidebar: oklch(0.1604 0.0209 201.0807);
--sidebar-foreground: oklch(0.9396 0.0097 204.9127);
--sidebar-primary: oklch(0.7040 0.1400 182.5030);
--secondary: oklch(0.2604 0.0471 267.4902);
--secondary-foreground: oklch(1.0000 0 0);
--muted: oklch(0.2739 0.0055 286.0326);
--muted-foreground: oklch(0.7118 0.0129 286.0665);
--accent: oklch(0.6986 0.1954 14.1660);
--accent-foreground: oklch(1.0000 0 0);
--destructive: oklch(0.3958 0.1331 25.7230);
--destructive-foreground: oklch(1.0000 0 0);
--border: oklch(0.2739 0.0055 286.0326);
--input: oklch(0.2739 0.0055 286.0326);
--ring: oklch(0.6453 0.2404 27.3106);
--chart-1: oklch(0.6453 0.2404 27.3106);
--chart-2: oklch(0.3986 0.0946 266.8132);
--chart-3: oklch(0.7570 0.1485 11.9623);
--chart-4: oklch(0.7824 0.0829 60.0948);
--chart-5: oklch(0.4419 0.0146 285.7864);
--sidebar: oklch(0.1645 0.0086 274.3354);
--sidebar-foreground: oklch(0.7118 0.0129 286.0665);
--sidebar-primary: oklch(0.6453 0.2404 27.3106);
--sidebar-primary-foreground: oklch(1.0000 0 0);
--sidebar-accent: oklch(0.4370 0.0780 188.2160);
--sidebar-accent-foreground: oklch(0.9294 0.0202 286.0004);
--sidebar-border: oklch(0.3008 0.0343 281.7840);
--sidebar-ring: oklch(0.7155 0.1583 288.7763);
--font-sans: Inter, sans-serif;
--font-serif: Georgia, serif;
--font-mono: JetBrains Mono, monospace;
--radius: 0.5rem;
--sidebar-accent: oklch(0.2145 0.0184 270.4182);
--sidebar-accent-foreground: oklch(0.9911 0 0);
--sidebar-border: oklch(0.2739 0.0055 286.0326);
--sidebar-ring: oklch(0.6453 0.2404 27.3106);
--font-sans: 'Inter', sans-serif;
--font-serif: 'Georgia', serif;
--font-mono: 'JetBrains Mono', monospace;
--radius: 0.75rem;
--shadow-x: 0px;
--shadow-y: 4px;
--shadow-blur: 10px;
--shadow-spread: 0px;
--shadow-opacity: 0.12;
--shadow-color: #2d2d53;
--shadow-2xs: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.06);
--shadow-xs: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.06);
--shadow-sm: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 1px 2px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 1px 2px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow-md: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 2px 4px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow-lg: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 4px 6px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow-xl: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.12), 0px 8px 10px -1px hsl(240 29.6875% 25.0980% / 0.12);
--shadow-2xl: 0px 4px 10px 0px hsl(240 29.6875% 25.0980% / 0.30);
--shadow-y: 8px;
--shadow-blur: 15px;
--shadow-spread: -3px;
--shadow-opacity: 0.4;
--shadow-color: 0 0% 0%;
--shadow-2xs: 0px 8px 15px -3px hsl(0 0% 0% / 0.20);
--shadow-xs: 0px 8px 15px -3px hsl(0 0% 0% / 0.20);
--shadow-sm: 0px 8px 15px -3px hsl(0 0% 0% / 0.40), 0px 1px 2px -4px hsl(0 0% 0% / 0.40);
--shadow: 0px 8px 15px -3px hsl(0 0% 0% / 0.40), 0px 1px 2px -4px hsl(0 0% 0% / 0.40);
--shadow-md: 0px 8px 15px -3px hsl(0 0% 0% / 0.40), 0px 2px 4px -4px hsl(0 0% 0% / 0.40);
--shadow-lg: 0px 8px 15px -3px hsl(0 0% 0% / 0.40), 0px 4px 6px -4px hsl(0 0% 0% / 0.40);
--shadow-xl: 0px 8px 15px -3px hsl(0 0% 0% / 0.40), 0px 8px 10px -4px hsl(0 0% 0% / 0.40);
--shadow-2xl: 0px 8px 15px -3px hsl(0 0% 0% / 1.00);
}
@theme inline {
@ -163,6 +163,13 @@
--shadow-lg: var(--shadow-lg);
--shadow-xl: var(--shadow-xl);
--shadow-2xl: var(--shadow-2xl);
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
--tracking-normal: var(--tracking-normal);
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
}
@layer base {
@ -171,5 +178,6 @@
}
body {
@apply bg-background text-foreground;
letter-spacing: var(--tracking-normal);
}
}