Files
bknd/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx
T
dswbx 4c11789ea8 Fix Release 0.11.1 (#150)
* fix strategy forms handling, add register route and hidden fields

Refactored strategy forms to include hidden fields for type and name. Added a registration route with necessary adjustments to the admin controller and routes. Corrected field handling within relevant forms and components.

* fix admin access permissions and refactor routing structure

display a fixed error for unmet permissions when retrieving the schema. moved auth routes outside of BkndProvider and reorganized remaining routes to include BkndWrapper.

* fix: properly type BkndWrapper

* bump fix release version

* ModuleManager: update diff checking and AppData validation

Revised diff handling includes validation of diffs, reverting changes on failure, and enforcing module constraints with onBeforeUpdate hooks. Introduced `validateDiffs` and backup of stable configs. Applied changes in related modules, tests, and UI layer to align with updated diff logic.

* fix: cli: running from config file were using invalid args

* fix: cli: improve sequence of onBuilt trigger to allow custom routes from cli

* fix e2e tests
2025-04-20 09:29:58 +02:00

138 lines
4.2 KiB
TypeScript

import { IconBug } from "@tabler/icons-react";
import type { JsonSchema } from "json-schema-library";
import { Children, type ReactElement, type ReactNode, cloneElement, isValidElement } from "react";
import { IconButton } from "ui/components/buttons/IconButton";
import { JsonViewer } from "ui/components/code/JsonViewer";
import * as Formy from "ui/components/form/Formy";
import {
useFormContext,
useFormError,
useFormValue,
} from "ui/components/form/json-schema-form/Form";
import { Popover } from "ui/components/overlay/Popover";
import { getLabel } from "./utils";
export type FieldwrapperProps = {
name: string;
label?: string | ReactNode | false;
required?: boolean;
schema?: JsonSchema;
debug?: object | boolean;
wrapper?: "group" | "fieldset";
hidden?: boolean;
children: ReactElement | ReactNode;
errorPlacement?: "top" | "bottom";
description?: string;
descriptionPlacement?: "top" | "bottom";
fieldId?: string;
};
export function FieldWrapper({
name,
label: _label,
required,
schema,
wrapper,
hidden,
errorPlacement = "bottom",
descriptionPlacement = "bottom",
children,
fieldId,
...props
}: FieldwrapperProps) {
const errors = useFormError(name, { strict: true });
const examples = schema?.examples || [];
const examplesId = `${name}-examples`;
const description = props?.description ?? schema?.description;
const label = typeof _label !== "undefined" ? _label : schema ? getLabel(name, schema) : name;
const Errors = errors.length > 0 && (
<Formy.ErrorMessage>{errors.map((e) => e.message).join(", ")}</Formy.ErrorMessage>
);
const Description = description && (
<Formy.Help className={descriptionPlacement === "top" ? "-mt-1 mb-1" : "mb-2"}>
{description}
</Formy.Help>
);
return (
<Formy.Group
error={errors.length > 0}
as={wrapper === "fieldset" ? "fieldset" : "div"}
className={hidden ? "hidden" : "relative"}
>
{errorPlacement === "top" && Errors}
<FieldDebug name={name} schema={schema} required={required} />
{label && (
<Formy.Label
as={wrapper === "fieldset" ? "legend" : "label"}
htmlFor={fieldId}
className="self-start"
>
{label} {required && <span className="font-medium opacity-30">*</span>}
</Formy.Label>
)}
{descriptionPlacement === "top" && Description}
<div className="flex flex-row gap-2">
<div className="flex flex-1 flex-col gap-3">
{Children.count(children) === 1 && isValidElement(children)
? cloneElement(children, {
// @ts-ignore
list: examples.length > 0 ? examplesId : undefined,
})
: children}
{examples.length > 0 && (
<datalist id={examplesId}>
{examples.map((e, i) => (
<option key={i} value={e as any} />
))}
</datalist>
)}
</div>
</div>
{descriptionPlacement === "bottom" && Description}
{errorPlacement === "bottom" && Errors}
</Formy.Group>
);
}
const FieldDebug = ({
name,
schema,
required,
}: Pick<FieldwrapperProps, "name" | "schema" | "required">) => {
const { options } = useFormContext();
if (!options?.debug) return null;
const { value } = useFormValue(name);
const errors = useFormError(name, { strict: true });
return (
<div className="absolute top-0 right-0">
<Popover
overlayProps={{
className: "max-w-none",
}}
position="bottom-end"
target={({ toggle }) => (
<JsonViewer
className="bg-background pr-3 text-sm"
json={{
name,
value,
required,
schema,
errors,
}}
expand={6}
/>
)}
>
<IconButton Icon={IconBug} size="xs" className="opacity-30" />
</Popover>
</div>
);
};