mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-04 00:56:01 +00:00
fix json field form, replaced auth form
This commit is contained in:
+2
-3
@@ -60,8 +60,7 @@
|
|||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"fast-xml-parser": "^5.0.8",
|
"fast-xml-parser": "^5.0.8",
|
||||||
"hono": "^4.7.11",
|
"hono": "4.8.3",
|
||||||
"json-schema-form-react": "^0.0.2",
|
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
"kysely": "^0.27.6",
|
"kysely": "^0.27.6",
|
||||||
@@ -100,7 +99,7 @@
|
|||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"jotai": "^2.12.2",
|
"jotai": "^2.12.2",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
"jsonv-ts": "^0.2.2",
|
"jsonv-ts": "link:jsonv-ts",
|
||||||
"kysely-d1": "^0.3.0",
|
"kysely-d1": "^0.3.0",
|
||||||
"kysely-generic-sqlite": "^1.2.1",
|
"kysely-generic-sqlite": "^1.2.1",
|
||||||
"libsql-stateless-easy": "^1.8.0",
|
"libsql-stateless-easy": "^1.8.0",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { PrimaryFieldType } from "core";
|
import type { PrimaryFieldType } from "core";
|
||||||
import { $console } from "core/utils";
|
import { $console, isPlainObject } from "core/utils";
|
||||||
import { isDebug } from "core/env";
|
import { isDebug } from "core/env";
|
||||||
import { encodeSearch } from "core/utils/reqres";
|
import { encodeSearch } from "core/utils/reqres";
|
||||||
import type { ApiFetcher } from "Api";
|
import type { ApiFetcher } from "Api";
|
||||||
@@ -95,7 +95,11 @@ export abstract class ModuleApi<Options extends BaseModuleApiOptions = BaseModul
|
|||||||
let body: any = _init?.body;
|
let body: any = _init?.body;
|
||||||
if (_init && "body" in _init && ["POST", "PATCH", "PUT"].includes(method)) {
|
if (_init && "body" in _init && ["POST", "PATCH", "PUT"].includes(method)) {
|
||||||
const requestContentType = (headers.get("Content-Type") as string) ?? undefined;
|
const requestContentType = (headers.get("Content-Type") as string) ?? undefined;
|
||||||
if (!requestContentType || requestContentType.startsWith("application/json")) {
|
if (
|
||||||
|
!requestContentType ||
|
||||||
|
requestContentType.startsWith("application/json") ||
|
||||||
|
isPlainObject(body) // @todo: not entirely sure about this
|
||||||
|
) {
|
||||||
body = JSON.stringify(_init.body);
|
body = JSON.stringify(_init.body);
|
||||||
headers.set("Content-Type", "application/json");
|
headers.set("Content-Type", "application/json");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,13 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|||||||
|
|
||||||
private renderFallback() {
|
private renderFallback() {
|
||||||
if (this.props.fallback) {
|
if (this.props.fallback) {
|
||||||
return typeof this.props.fallback === "function"
|
return typeof this.props.fallback === "function" ? (
|
||||||
? this.props.fallback({ error: this.state.error!, resetError: this.resetError })
|
this.props.fallback({ error: this.state.error!, resetError: this.resetError })
|
||||||
: this.props.fallback;
|
) : (
|
||||||
|
<BaseError>{this.props.fallback}</BaseError>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return <BaseError>Error</BaseError>;
|
return <BaseError>Error1</BaseError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useEvent } from "ui/hooks/use-event";
|
|
||||||
import {
|
import {
|
||||||
type CleanOptions,
|
type CleanOptions,
|
||||||
type InputElement,
|
type InputElement,
|
||||||
|
|||||||
@@ -1,20 +1,11 @@
|
|||||||
import type { AppAuthOAuthStrategy, AppAuthSchema } from "auth/auth-schema";
|
import type { AppAuthOAuthStrategy, AppAuthSchema } from "auth/auth-schema";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { Form } from "json-schema-form-react";
|
import { NativeForm } from "ui/components/form/native-form/NativeForm";
|
||||||
import { transform } from "lodash-es";
|
import { transform } from "lodash-es";
|
||||||
import type { ComponentPropsWithoutRef } from "react";
|
import type { ComponentPropsWithoutRef } from "react";
|
||||||
import { Button } from "ui/components/buttons/Button";
|
import { Button } from "ui/components/buttons/Button";
|
||||||
import { Group, Input, Password, Label } from "ui/components/form/Formy/components";
|
import { Group, Input, Password, Label } from "ui/components/form/Formy/components";
|
||||||
import { SocialLink } from "./SocialLink";
|
import { SocialLink } from "./SocialLink";
|
||||||
import type { Validator } from "json-schema-form-react";
|
|
||||||
import { s } from "bknd/core";
|
|
||||||
import type { ErrorDetail } from "jsonv-ts";
|
|
||||||
|
|
||||||
class JsonvTsValidator implements Validator<ErrorDetail> {
|
|
||||||
async validate(schema: s.Schema, data: any) {
|
|
||||||
return schema.validate(data).errors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type LoginFormProps = Omit<ComponentPropsWithoutRef<"form">, "onSubmit" | "action"> & {
|
export type LoginFormProps = Omit<ComponentPropsWithoutRef<"form">, "onSubmit" | "action"> & {
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -25,16 +16,6 @@ export type LoginFormProps = Omit<ComponentPropsWithoutRef<"form">, "onSubmit" |
|
|||||||
buttonLabel?: string;
|
buttonLabel?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validator = new JsonvTsValidator();
|
|
||||||
const schema = s.strictObject({
|
|
||||||
email: s.string({
|
|
||||||
pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$",
|
|
||||||
}),
|
|
||||||
password: s.string({
|
|
||||||
minLength: 8, // @todo: this should be configurable
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export function AuthForm({
|
export function AuthForm({
|
||||||
formData,
|
formData,
|
||||||
className,
|
className,
|
||||||
@@ -79,24 +60,20 @@ export function AuthForm({
|
|||||||
<Or />
|
<Or />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Form
|
<NativeForm
|
||||||
method={method}
|
method={method}
|
||||||
action={password.action}
|
action={password.action}
|
||||||
{...(props as any)}
|
{...(props as any)}
|
||||||
schema={schema}
|
validateOn="change"
|
||||||
validator={validator}
|
|
||||||
validationMode="change"
|
|
||||||
className={clsx("flex flex-col gap-3 w-full", className)}
|
className={clsx("flex flex-col gap-3 w-full", className)}
|
||||||
>
|
>
|
||||||
{({ errors, submitting }) => (
|
|
||||||
<>
|
|
||||||
<Group>
|
<Group>
|
||||||
<Label htmlFor="email">Email address</Label>
|
<Label htmlFor="email">Email address</Label>
|
||||||
<Input type="email" name="email" />
|
<Input type="email" name="email" required />
|
||||||
</Group>
|
</Group>
|
||||||
<Group>
|
<Group>
|
||||||
<Label htmlFor="password">Password</Label>
|
<Label htmlFor="password">Password</Label>
|
||||||
<Password name="password" />
|
<Password name="password" required minLength={8} />
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -104,13 +81,10 @@ export function AuthForm({
|
|||||||
variant="primary"
|
variant="primary"
|
||||||
size="large"
|
size="large"
|
||||||
className="w-full mt-2 justify-center"
|
className="w-full mt-2 justify-center"
|
||||||
disabled={errors.length > 0 || submitting}
|
|
||||||
>
|
>
|
||||||
{buttonLabel}
|
{buttonLabel}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</NativeForm>
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelec
|
|||||||
import type { TPrimaryFieldFormat } from "data/fields/PrimaryField";
|
import type { TPrimaryFieldFormat } from "data/fields/PrimaryField";
|
||||||
import { s, stringIdentifier } from "bknd/core";
|
import { s, stringIdentifier } from "bknd/core";
|
||||||
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
|
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
|
||||||
|
import ErrorBoundary from "ui/components/display/ErrorBoundary";
|
||||||
|
|
||||||
const fieldsSchemaObject = originalFieldsSchemaObject;
|
const fieldsSchemaObject = originalFieldsSchemaObject;
|
||||||
const fieldsSchema = s.anyOf(Object.values(fieldsSchemaObject));
|
const fieldsSchema = s.anyOf(Object.values(fieldsSchemaObject));
|
||||||
@@ -453,12 +454,9 @@ function EntityField({
|
|||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
<Tabs.Panel value="specific">
|
<Tabs.Panel value="specific">
|
||||||
<div className="flex flex-col gap-2 pt-3 pb-1">
|
<div className="flex flex-col gap-2 pt-3 pb-1">
|
||||||
<JsonSchemaForm
|
<ErrorBoundary fallback={`Error rendering JSON Schema for ${type}`}>
|
||||||
key={type}
|
<SpecificForm
|
||||||
schema={specificFieldSchema(type as any)}
|
field={field}
|
||||||
formData={specificData}
|
|
||||||
uiSchema={dataFieldsUiSchema.config}
|
|
||||||
className="legacy hide-required-mark fieldset-alternative mute-root"
|
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setValue(`${prefix}.config`, {
|
setValue(`${prefix}.config`, {
|
||||||
...getValues([`fields.${index}.config`])[0],
|
...getValues([`fields.${index}.config`])[0],
|
||||||
@@ -466,6 +464,7 @@ function EntityField({
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
<Tabs.Panel value="code">
|
<Tabs.Panel value="code">
|
||||||
@@ -490,3 +489,25 @@ function EntityField({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SpecificForm = ({
|
||||||
|
field,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
field: FieldArrayWithId<TFieldsFormSchema, "fields", "id">;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
}) => {
|
||||||
|
const type = field.field.type;
|
||||||
|
const specificData = omit(field.field.config, commonProps);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<JsonSchemaForm
|
||||||
|
key={type}
|
||||||
|
schema={specificFieldSchema(type as any)?.toJSON()}
|
||||||
|
formData={specificData}
|
||||||
|
uiSchema={dataFieldsUiSchema.config}
|
||||||
|
className="legacy hide-required-mark fieldset-alternative mute-root"
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user