mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-03 08:36:01 +00:00
initial refactor
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { TypeInvalidError, parse, transformObject } from "core/utils";
|
||||
import { transformObject } from "core/utils";
|
||||
import { constructEntity } from "data";
|
||||
import {
|
||||
type TAppDataEntity,
|
||||
@@ -13,8 +13,7 @@ import {
|
||||
import { useBknd } from "ui/client/bknd";
|
||||
import type { TSchemaActions } from "ui/client/schema/actions";
|
||||
import { bkndModals } from "ui/modals";
|
||||
import * as tb from "@sinclair/typebox";
|
||||
const { Type } = tb;
|
||||
import { s, parse, InvalidSchemaError } from "core/object/schema";
|
||||
|
||||
export function useBkndData() {
|
||||
const { config, app, schema, actions: bkndActions } = useBknd();
|
||||
@@ -27,12 +26,10 @@ export function useBkndData() {
|
||||
const actions = {
|
||||
entity: {
|
||||
add: async (name: string, data: TAppDataEntity) => {
|
||||
console.log("create entity", { data });
|
||||
const validated = parse(entitiesSchema, data, {
|
||||
skipMark: true,
|
||||
forceParse: true,
|
||||
});
|
||||
console.log("validated", validated);
|
||||
// @todo: check for existing?
|
||||
return await bkndActions.add("data", `entities.${name}`, validated);
|
||||
},
|
||||
@@ -44,7 +41,6 @@ export function useBkndData() {
|
||||
|
||||
return {
|
||||
config: async (partial: Partial<TAppDataEntity["config"]>): Promise<boolean> => {
|
||||
console.log("patch config", entityName, partial);
|
||||
return await bkndActions.overwrite(
|
||||
"data",
|
||||
`entities.${entityName}.config`,
|
||||
@@ -57,13 +53,11 @@ export function useBkndData() {
|
||||
},
|
||||
relations: {
|
||||
add: async (relation: TAppDataRelation) => {
|
||||
console.log("create relation", { relation });
|
||||
const name = crypto.randomUUID();
|
||||
const validated = parse(Type.Union(relationsSchema), relation, {
|
||||
const validated = parse(s.anyOf(relationsSchema), relation, {
|
||||
skipMark: true,
|
||||
forceParse: true,
|
||||
});
|
||||
console.log("validated", validated);
|
||||
return await bkndActions.add("data", `relations.${name}`, validated);
|
||||
},
|
||||
},
|
||||
@@ -120,17 +114,14 @@ const modals = {
|
||||
function entityFieldActions(bkndActions: TSchemaActions, entityName: string) {
|
||||
return {
|
||||
add: async (name: string, field: TAppDataField) => {
|
||||
console.log("create field", { name, field });
|
||||
const validated = parse(fieldsSchema, field, {
|
||||
skipMark: true,
|
||||
forceParse: true,
|
||||
});
|
||||
console.log("validated", validated);
|
||||
return await bkndActions.add("data", `entities.${entityName}.fields.${name}`, validated);
|
||||
},
|
||||
patch: () => null,
|
||||
set: async (fields: TAppDataEntityFields) => {
|
||||
console.log("set fields", entityName, fields);
|
||||
try {
|
||||
const validated = parse(entityFields, fields, {
|
||||
skipMark: true,
|
||||
@@ -141,11 +132,9 @@ function entityFieldActions(bkndActions: TSchemaActions, entityName: string) {
|
||||
`entities.${entityName}.fields`,
|
||||
validated,
|
||||
);
|
||||
console.log("res", res);
|
||||
//bkndActions.set("data", "entities", fields);
|
||||
} catch (e) {
|
||||
console.error("error", e);
|
||||
if (e instanceof TypeInvalidError) {
|
||||
if (e instanceof InvalidSchemaError) {
|
||||
alert("Error updating fields: " + e.firstToString());
|
||||
} else {
|
||||
alert("An error occured, check console. There will be nice error handling soon.");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Static, parse } from "core/utils";
|
||||
import { parse } from "core/object/schema";
|
||||
import { type TAppFlowSchema, flowSchema } from "flows/flows-schema";
|
||||
import { useBknd } from "../../BkndProvider";
|
||||
|
||||
@@ -8,11 +8,8 @@ export function useFlows() {
|
||||
const actions = {
|
||||
flow: {
|
||||
create: async (name: string, data: TAppFlowSchema) => {
|
||||
console.log("would create", name, data);
|
||||
const parsed = parse(flowSchema, data, { skipMark: true, forceParse: true });
|
||||
console.log("parsed", parsed);
|
||||
const res = await bkndActions.add("flows", `flows.${name}`, parsed);
|
||||
console.log("res", res);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Check, Errors } from "core/utils";
|
||||
import { Check } from "@sinclair/typebox/value";
|
||||
import { Errors } from "@sinclair/typebox/errors";
|
||||
import { FromSchema } from "./from-schema";
|
||||
|
||||
import type {
|
||||
|
||||
@@ -6,15 +6,13 @@ import type { ComponentPropsWithoutRef } from "react";
|
||||
import { Button } from "ui/components/buttons/Button";
|
||||
import { Group, Input, Password, Label } from "ui/components/form/Formy/components";
|
||||
import { SocialLink } from "./SocialLink";
|
||||
import type { ValueError } from "@sinclair/typebox/value";
|
||||
import { type TSchema, Value } from "core/utils";
|
||||
import type { Validator } from "json-schema-form-react";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
import { s } from "core/object/schema";
|
||||
import type { ErrorDetail } from "jsonv-ts";
|
||||
|
||||
class TypeboxValidator implements Validator<ValueError> {
|
||||
async validate(schema: TSchema, data: any) {
|
||||
return Value.Check(schema, data) ? [] : [...Value.Errors(schema, data)];
|
||||
class JsonvTsValidator implements Validator<ErrorDetail> {
|
||||
async validate(schema: s.Schema, data: any) {
|
||||
return schema.validate(data).errors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +25,12 @@ export type LoginFormProps = Omit<ComponentPropsWithoutRef<"form">, "onSubmit" |
|
||||
buttonLabel?: string;
|
||||
};
|
||||
|
||||
const validator = new TypeboxValidator();
|
||||
const schema = Type.Object({
|
||||
email: Type.String({
|
||||
const validator = new JsonvTsValidator();
|
||||
const schema = s.strictObject({
|
||||
email: s.string({
|
||||
pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$",
|
||||
}),
|
||||
password: Type.String({
|
||||
password: s.string({
|
||||
minLength: 8, // @todo: this should be configurable
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -4,12 +4,12 @@ import { useLocation, useSearch as useWouterSearch } from "wouter";
|
||||
import { type s, parse } from "core/object/schema";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
export type UseSearchOptions<Schema extends s.TAnySchema = s.TAnySchema> = {
|
||||
export type UseSearchOptions<Schema extends s.Schema = s.Schema> = {
|
||||
defaultValue?: Partial<s.StaticCoerced<Schema>>;
|
||||
beforeEncode?: (search: Partial<s.StaticCoerced<Schema>>) => object;
|
||||
};
|
||||
|
||||
export function useSearch<Schema extends s.TAnySchema = s.TAnySchema>(
|
||||
export function useSearch<Schema extends s.Schema = s.Schema>(
|
||||
schema: Schema,
|
||||
options?: UseSearchOptions<Schema>,
|
||||
) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { ModalProps } from "@mantine/core";
|
||||
import type { ContextModalProps } from "@mantine/modals";
|
||||
import { type Static, StringEnum, StringIdentifier } from "core/utils";
|
||||
import { entitiesSchema, fieldsSchema, relationsSchema } from "data/data-schema";
|
||||
import { useState } from "react";
|
||||
import { type Modal2Ref, ModalBody, ModalFooter, ModalTitle } from "ui/components/modal/Modal2";
|
||||
@@ -11,58 +10,51 @@ import { StepEntityFields } from "./step.entity.fields";
|
||||
import { StepRelation } from "./step.relation";
|
||||
import { StepSelect } from "./step.select";
|
||||
import Templates from "./templates/register";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
import { s } from "core/object/schema";
|
||||
|
||||
export type CreateModalRef = Modal2Ref;
|
||||
|
||||
export const ModalActions = ["entity", "relation", "media"] as const;
|
||||
|
||||
export const entitySchema = Type.Composite([
|
||||
Type.Object({
|
||||
name: StringIdentifier,
|
||||
}),
|
||||
entitiesSchema,
|
||||
]);
|
||||
|
||||
const schemaAction = Type.Union([
|
||||
StringEnum(["entity", "relation", "media"]),
|
||||
Type.String({ pattern: "^template-" }),
|
||||
]);
|
||||
export type TSchemaAction = Static<typeof schemaAction>;
|
||||
|
||||
const createFieldSchema = Type.Object({
|
||||
entity: StringIdentifier,
|
||||
name: StringIdentifier,
|
||||
field: Type.Array(fieldsSchema),
|
||||
export const entitySchema = s.object({
|
||||
name: s.string(),
|
||||
...entitiesSchema.properties,
|
||||
});
|
||||
export type TFieldCreate = Static<typeof createFieldSchema>;
|
||||
|
||||
const createModalSchema = Type.Object(
|
||||
{
|
||||
action: schemaAction,
|
||||
initial: Type.Optional(Type.Any()),
|
||||
entities: Type.Optional(
|
||||
Type.Object({
|
||||
create: Type.Optional(Type.Array(entitySchema)),
|
||||
}),
|
||||
),
|
||||
relations: Type.Optional(
|
||||
Type.Object({
|
||||
create: Type.Optional(Type.Array(Type.Union(relationsSchema))),
|
||||
}),
|
||||
),
|
||||
fields: Type.Optional(
|
||||
Type.Object({
|
||||
create: Type.Optional(Type.Array(createFieldSchema)),
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
export type TCreateModalSchema = Static<typeof createModalSchema>;
|
||||
// @todo: this union is not fully working, just "string"
|
||||
const schemaAction = s.anyOf([
|
||||
s.string({ enum: ["entity", "relation", "media"] }),
|
||||
s.string({ pattern: "^template-" }),
|
||||
]);
|
||||
export type TSchemaAction = s.Static<typeof schemaAction>;
|
||||
|
||||
const createFieldSchema = s.object({
|
||||
entity: s.string(),
|
||||
name: s.string(),
|
||||
field: s.array(fieldsSchema),
|
||||
});
|
||||
export type TFieldCreate = s.Static<typeof createFieldSchema>;
|
||||
|
||||
const createModalSchema = s.strictObject({
|
||||
action: schemaAction,
|
||||
initial: s.any().optional(),
|
||||
entities: s
|
||||
.object({
|
||||
create: s.array(entitySchema).optional(),
|
||||
})
|
||||
.optional(),
|
||||
relations: s
|
||||
.object({
|
||||
create: s.array(s.anyOf(relationsSchema)).optional(),
|
||||
})
|
||||
.optional(),
|
||||
fields: s
|
||||
.object({
|
||||
create: s.array(createFieldSchema).optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
export type TCreateModalSchema = s.Static<typeof createModalSchema>;
|
||||
|
||||
export function CreateModal({
|
||||
context,
|
||||
@@ -70,7 +62,6 @@ export function CreateModal({
|
||||
innerProps: { initialPath = [], initialState },
|
||||
}: ContextModalProps<{ initialPath?: string[]; initialState?: TCreateModalSchema }>) {
|
||||
const [path, setPath] = useState<string[]>(initialPath);
|
||||
console.log("...", initialPath, initialState);
|
||||
|
||||
function close() {
|
||||
context.closeModal(id);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { type Static, objectCleanEmpty } from "core/utils";
|
||||
//import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { objectCleanEmpty } from "core/utils";
|
||||
import { type TAppDataEntityFields, entitiesSchema } from "data/data-schema";
|
||||
import { mergeWith } from "lodash-es";
|
||||
import { useRef } from "react";
|
||||
@@ -12,9 +12,10 @@ import {
|
||||
} from "ui/routes/data/forms/entity.fields.form";
|
||||
import { ModalBody, ModalFooter, type TCreateModalSchema, useStepContext } from "./CreateModal";
|
||||
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
|
||||
import type { s } from "core/object/schema";
|
||||
|
||||
const schema = entitiesSchema;
|
||||
type Schema = Static<typeof schema>;
|
||||
type Schema = s.Static<typeof schema>;
|
||||
|
||||
export function StepEntityFields() {
|
||||
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
|
||||
@@ -40,7 +41,8 @@ export function StepEntityFields() {
|
||||
setValue,
|
||||
} = useForm({
|
||||
mode: "onTouched",
|
||||
resolver: typeboxResolver(schema),
|
||||
// @todo: add resolver
|
||||
//resolver: typeboxResolver(schema),
|
||||
defaultValues: initial as NonNullable<Schema>,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
//import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
|
||||
import { TextInput, Textarea } from "@mantine/core";
|
||||
import { useFocusTrap } from "@mantine/hooks";
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
entitySchema,
|
||||
useStepContext,
|
||||
} from "./CreateModal";
|
||||
import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect";
|
||||
|
||||
export function StepEntity() {
|
||||
const focusTrapRef = useFocusTrap();
|
||||
@@ -18,7 +17,8 @@ export function StepEntity() {
|
||||
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
|
||||
const { register, handleSubmit, formState, watch, control } = useForm({
|
||||
mode: "onTouched",
|
||||
resolver: typeboxResolver(entitySchema),
|
||||
// @todo: add resolver
|
||||
//resolver: typeboxResolver(entitySchema),
|
||||
defaultValues: state.entities?.create?.[0] ?? {},
|
||||
});
|
||||
/*const data = watch();
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { Switch, TextInput } from "@mantine/core";
|
||||
import { TypeRegistry } from "@sinclair/typebox";
|
||||
import { IconDatabase } from "@tabler/icons-react";
|
||||
import { type Static, StringEnum, StringIdentifier, registerCustomTypeboxKinds } from "core/utils";
|
||||
import { ManyToOneRelation, type RelationType, RelationTypes } from "data";
|
||||
import type { ReactNode } from "react";
|
||||
import { type Control, type FieldValues, type UseFormRegister, useForm } from "react-hook-form";
|
||||
@@ -14,11 +11,7 @@ import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelec
|
||||
import { useStepContext } from "ui/components/steps/Steps";
|
||||
import { useEvent } from "ui/hooks/use-event";
|
||||
import { ModalBody, ModalFooter, type TCreateModalSchema } from "./CreateModal";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
|
||||
// @todo: check if this could become an issue
|
||||
registerCustomTypeboxKinds(TypeRegistry);
|
||||
import { s, stringIdentifier } from "core/object/schema";
|
||||
|
||||
const Relations: {
|
||||
type: RelationType;
|
||||
@@ -47,11 +40,11 @@ const Relations: {
|
||||
},
|
||||
];
|
||||
|
||||
const schema = Type.Object({
|
||||
type: StringEnum(Relations.map((r) => r.type)),
|
||||
source: StringIdentifier,
|
||||
target: StringIdentifier,
|
||||
config: Type.Object({}),
|
||||
const schema = s.strictObject({
|
||||
type: s.string({ enum: Relations.map((r) => r.type) }),
|
||||
source: stringIdentifier,
|
||||
target: stringIdentifier,
|
||||
config: s.object({}),
|
||||
});
|
||||
|
||||
type ComponentCtx<T extends FieldValues = FieldValues> = {
|
||||
@@ -73,8 +66,9 @@ export function StepRelation() {
|
||||
watch,
|
||||
control,
|
||||
} = useForm({
|
||||
resolver: typeboxResolver(schema),
|
||||
defaultValues: (state.relations?.create?.[0] ?? {}) as Static<typeof schema>,
|
||||
// @todo: implement resolver
|
||||
//resolver: typeboxResolver(schema),
|
||||
defaultValues: (state.relations?.create?.[0] ?? {}) as s.Static<typeof schema>,
|
||||
});
|
||||
const data = watch();
|
||||
|
||||
|
||||
+12
-12
@@ -1,6 +1,5 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { Radio, TextInput } from "@mantine/core";
|
||||
import { Default, type Static, StringEnum, StringIdentifier, transformObject } from "core/utils";
|
||||
import { transformObject } from "core/utils";
|
||||
import type { MediaFieldConfig } from "media/MediaField";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -15,16 +14,15 @@ import {
|
||||
type TFieldCreate,
|
||||
useStepContext,
|
||||
} from "../../CreateModal";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
import { s, stringIdentifier } from "core/object/schema";
|
||||
|
||||
const schema = Type.Object({
|
||||
entity: StringIdentifier,
|
||||
cardinality_type: StringEnum(["single", "multiple"], { default: "multiple" }),
|
||||
cardinality: Type.Optional(Type.Number({ minimum: 1 })),
|
||||
name: StringIdentifier,
|
||||
const schema = s.object({
|
||||
entity: stringIdentifier,
|
||||
cardinality_type: s.string({ enum: ["single", "multiple"], default: "multiple" }),
|
||||
cardinality: s.number({ minimum: 1 }).optional(),
|
||||
name: stringIdentifier,
|
||||
});
|
||||
type TCreateModalMediaSchema = Static<typeof schema>;
|
||||
type TCreateModalMediaSchema = s.Static<typeof schema>;
|
||||
|
||||
export function TemplateMediaComponent() {
|
||||
const { stepBack, setState, state, path, nextStep } = useStepContext<TCreateModalSchema>();
|
||||
@@ -36,8 +34,10 @@ export function TemplateMediaComponent() {
|
||||
control,
|
||||
} = useForm({
|
||||
mode: "onChange",
|
||||
resolver: typeboxResolver(schema),
|
||||
defaultValues: Default(schema, state.initial ?? {}) as TCreateModalMediaSchema,
|
||||
// @todo: add resolver
|
||||
//resolver: typeboxResolver(schema),
|
||||
defaultValues: schema.template(state.initial ?? {}) as TCreateModalMediaSchema,
|
||||
//defaultValues: Default(schema, state.initial ?? {}) as TCreateModalMediaSchema,
|
||||
});
|
||||
const [forbidden, setForbidden] = useState<boolean>(false);
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Handle, type Node, type NodeProps, Position } from "@xyflow/react";
|
||||
import { Const, transformObject } from "core/utils";
|
||||
import { transformObject } from "core/utils";
|
||||
import { type Trigger, TriggerMap } from "flows";
|
||||
import type { IconType } from "react-icons";
|
||||
import { TbCircleLetterT } from "react-icons/tb";
|
||||
import { JsonSchemaForm } from "ui/components/form/json-schema";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
import { s } from "core/object/schema";
|
||||
|
||||
export type TaskComponentProps = NodeProps<Node<{ trigger: Trigger }>> & {
|
||||
Icon?: IconType;
|
||||
@@ -14,9 +13,9 @@ export type TaskComponentProps = NodeProps<Node<{ trigger: Trigger }>> & {
|
||||
|
||||
const triggerSchemas = Object.values(
|
||||
transformObject(TriggerMap, (trigger, name) =>
|
||||
Type.Object(
|
||||
s.object(
|
||||
{
|
||||
type: Const(name),
|
||||
type: s.literal(name),
|
||||
config: trigger.cls.schema,
|
||||
},
|
||||
{ title: String(name), additionalProperties: false },
|
||||
@@ -47,7 +46,7 @@ export function TriggerComponent({
|
||||
<div className="flex flex-col gap-2 px-3 py-2">
|
||||
<JsonSchemaForm
|
||||
className="legacy"
|
||||
schema={Type.Union(triggerSchemas)}
|
||||
schema={s.anyOf(triggerSchemas)}
|
||||
onChange={console.log}
|
||||
formData={trigger}
|
||||
{...props}
|
||||
|
||||
@@ -1,30 +1,25 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { Input, NativeSelect, Select, TextInput } from "@mantine/core";
|
||||
import { Input, TextInput } from "@mantine/core";
|
||||
import { useToggle } from "@mantine/hooks";
|
||||
import { IconMinus, IconPlus, IconWorld } from "@tabler/icons-react";
|
||||
import type { Node, NodeProps } from "@xyflow/react";
|
||||
import type { Static } from "core/utils";
|
||||
import { s } from "core/object/schema";
|
||||
import { FetchTask } from "flows";
|
||||
import { useRef, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Button } from "ui/components/buttons/Button";
|
||||
import { JsonViewer } from "ui/components/code/JsonViewer";
|
||||
import { SegmentedControl } from "ui/components/form/SegmentedControl";
|
||||
import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect";
|
||||
import { type TFlowNodeData, useFlowSelector } from "../../../hooks/use-flow";
|
||||
import type { TFlowNodeData } from "../../../hooks/use-flow";
|
||||
import { KeyValueInput } from "../../form/KeyValueInput";
|
||||
import { BaseNode } from "../BaseNode";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
|
||||
const schema = Type.Composite([
|
||||
FetchTask.schema,
|
||||
Type.Object({
|
||||
query: Type.Optional(Type.Record(Type.String(), Type.String())),
|
||||
}),
|
||||
]);
|
||||
const schema = s.object({
|
||||
query: s.record(s.string()).optional(),
|
||||
...FetchTask.schema.properties,
|
||||
});
|
||||
|
||||
type TFetchTaskSchema = Static<typeof FetchTask.schema>;
|
||||
type TFetchTaskSchema = s.Static<typeof FetchTask.schema>;
|
||||
type FetchTaskFormProps = NodeProps<Node<TFlowNodeData>> & {
|
||||
params: TFetchTaskSchema;
|
||||
onChange: (params: any) => void;
|
||||
@@ -42,8 +37,9 @@ export function FetchTaskForm({ onChange, params, ...props }: FetchTaskFormProps
|
||||
watch,
|
||||
control,
|
||||
} = useForm({
|
||||
resolver: typeboxResolver(schema),
|
||||
defaultValues: params as Static<typeof schema>,
|
||||
// @todo: add resolver
|
||||
//resolver: typeboxResolver(schema),
|
||||
defaultValues: params as s.Static<typeof schema>,
|
||||
mode: "onChange",
|
||||
//defaultValues: (state.relations?.create?.[0] ?? {}) as Static<typeof schema>
|
||||
});
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { TypeRegistry } from "@sinclair/typebox";
|
||||
import { type Node, type NodeProps, Position } from "@xyflow/react";
|
||||
import { registerCustomTypeboxKinds } from "core/utils";
|
||||
import type { TAppFlowTaskSchema } from "flows/AppFlows";
|
||||
import { useFlowCanvas, useFlowSelector } from "../../../hooks/use-flow";
|
||||
import { Handle } from "../Handle";
|
||||
import { FetchTaskForm } from "./FetchTaskNode";
|
||||
import { RenderNode } from "./RenderNode";
|
||||
|
||||
registerCustomTypeboxKinds(TypeRegistry);
|
||||
|
||||
const TaskComponents = {
|
||||
fetch: FetchTaskForm,
|
||||
render: RenderNode,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { TextInput } from "@mantine/core";
|
||||
import type { Node, NodeProps } from "@xyflow/react";
|
||||
import { Const, type Static, registerCustomTypeboxKinds, transformObject } from "core/utils";
|
||||
import { transformObject } from "core/utils";
|
||||
import { TriggerMap } from "flows";
|
||||
import type { TAppFlowTriggerSchema } from "flows/AppFlows";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -11,22 +10,18 @@ import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelec
|
||||
import { useFlowCanvas, useFlowSelector } from "../../../hooks/use-flow";
|
||||
import { BaseNode } from "../BaseNode";
|
||||
import { Handle } from "../Handle";
|
||||
import * as tb from "@sinclair/typebox";
|
||||
const { Type, TypeRegistry } = tb;
|
||||
import { s } from "core/object/schema";
|
||||
|
||||
// @todo: check if this could become an issue
|
||||
registerCustomTypeboxKinds(TypeRegistry);
|
||||
|
||||
const schema = Type.Object({
|
||||
trigger: Type.Union(
|
||||
const schema = s.object({
|
||||
trigger: s.anyOf(
|
||||
Object.values(
|
||||
transformObject(TriggerMap, (trigger, name) =>
|
||||
Type.Object(
|
||||
s.strictObject(
|
||||
{
|
||||
type: Const(name),
|
||||
type: s.literal(name),
|
||||
config: trigger.cls.schema,
|
||||
},
|
||||
{ title: String(name), additionalProperties: false },
|
||||
{ title: String(name) },
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -50,13 +45,14 @@ export const TriggerNode = (props: NodeProps<Node<TAppFlowTriggerSchema & { labe
|
||||
watch,
|
||||
control,
|
||||
} = useForm({
|
||||
resolver: typeboxResolver(schema),
|
||||
defaultValues: { trigger: state } as Static<typeof schema>,
|
||||
// @todo: add resolver
|
||||
//resolver: typeboxResolver(schema),
|
||||
defaultValues: { trigger: state } as s.Static<typeof schema>,
|
||||
mode: "onChange",
|
||||
});
|
||||
const data = watch("trigger");
|
||||
|
||||
async function onSubmit(data: Static<typeof schema>) {
|
||||
async function onSubmit(data: s.Static<typeof schema>) {
|
||||
console.log("submit", data.trigger);
|
||||
// @ts-ignore
|
||||
await actions.trigger.update(data.trigger);
|
||||
|
||||
@@ -46,7 +46,7 @@ export const flowStateAtom = atom<TFlowState>({
|
||||
|
||||
const FlowCanvasContext = createContext<FlowContextType>(undefined!);
|
||||
|
||||
const DEFAULT_FLOW = { trigger: {}, tasks: {}, connections: {} };
|
||||
const DEFAULT_FLOW: TAppFlowSchema = { trigger: { type: "manual" }, tasks: {}, connections: {} };
|
||||
export function FlowCanvasProvider({ children, name }: { children: any; name?: string }) {
|
||||
//const [dirty, setDirty] = useState(false);
|
||||
const setFlowState = useSetAtom(flowStateAtom);
|
||||
@@ -71,7 +71,7 @@ export function FlowCanvasProvider({ children, name }: { children: any; name?: s
|
||||
update: async (trigger: TAppFlowTriggerSchema | any) => {
|
||||
console.log("update trigger", trigger);
|
||||
setFlowState((state) => {
|
||||
const flow = state.flow || DEFAULT_FLOW;
|
||||
const flow = state.flow || (DEFAULT_FLOW as any);
|
||||
return { ...state, dirty: true, flow: { ...flow, trigger } };
|
||||
});
|
||||
//return s.actions.patch("flows", `flows.flows.${name}`, { trigger });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StringIdentifier, transformObject, ucFirstAllSnakeToPascalWithSpaces } from "core/utils";
|
||||
import { transformObject, ucFirstAllSnakeToPascalWithSpaces } from "core/utils";
|
||||
import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth";
|
||||
import { Alert } from "ui/components/display/Alert";
|
||||
import { bkndModals } from "ui/modals";
|
||||
@@ -6,6 +6,7 @@ import { Button } from "../../components/buttons/Button";
|
||||
import { CellValue, DataTable } from "../../components/table/DataTable";
|
||||
import * as AppShell from "../../layouts/AppShell/AppShell";
|
||||
import { routes, useNavigate } from "../../lib/routes";
|
||||
import { stringIdentifier } from "core/object/schema";
|
||||
|
||||
export function AuthRolesList() {
|
||||
const [navigate] = useNavigate();
|
||||
@@ -31,7 +32,7 @@ export function AuthRolesList() {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: StringIdentifier,
|
||||
name: stringIdentifier,
|
||||
},
|
||||
required: ["name"],
|
||||
},
|
||||
|
||||
@@ -64,8 +64,7 @@ function AuthStrategiesListInternal() {
|
||||
const config = $auth.config.strategies;
|
||||
const schema = $auth.schema.properties.strategies;
|
||||
const schemas = Object.fromEntries(
|
||||
// @ts-ignore
|
||||
$auth.schema.properties.strategies.additionalProperties.anyOf.map((s) => [
|
||||
$auth.schema.properties.strategies?.additionalProperties?.anyOf.map((s) => [
|
||||
s.properties.type.const,
|
||||
s,
|
||||
]),
|
||||
@@ -76,7 +75,12 @@ function AuthStrategiesListInternal() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Form schema={schema} initialValues={config} onSubmit={handleSubmit} options={formOptions}>
|
||||
<Form
|
||||
schema={schema.toJSON()}
|
||||
initialValues={config}
|
||||
onSubmit={handleSubmit}
|
||||
options={formOptions}
|
||||
>
|
||||
<Subscribe
|
||||
selector={(state) => ({
|
||||
dirty: state.dirty,
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
//import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { Input, Switch, Tooltip } from "@mantine/core";
|
||||
import { guardRoleSchema } from "auth/auth-schema";
|
||||
import { type Static, ucFirst } from "core/utils";
|
||||
import { ucFirst } from "core/utils";
|
||||
import { forwardRef, useImperativeHandle } from "react";
|
||||
import { type UseControllerProps, useController, useForm } from "react-hook-form";
|
||||
import { useBknd } from "ui/client/bknd";
|
||||
import { Button } from "ui/components/buttons/Button";
|
||||
import { MantineSwitch } from "ui/components/form/hook-form-mantine/MantineSwitch";
|
||||
import type { s } from "core/object/schema";
|
||||
|
||||
const schema = guardRoleSchema;
|
||||
type Role = Static<typeof guardRoleSchema>;
|
||||
type Role = s.Static<typeof guardRoleSchema>;
|
||||
|
||||
export type AuthRoleFormRef = {
|
||||
getData: () => Role;
|
||||
@@ -33,7 +34,8 @@ export const AuthRoleForm = forwardRef<
|
||||
reset,
|
||||
getValues,
|
||||
} = useForm({
|
||||
resolver: typeboxResolver(schema),
|
||||
// @todo: add resolver
|
||||
//resolver: typeboxResolver(schema),
|
||||
defaultValues: role,
|
||||
});
|
||||
|
||||
@@ -87,7 +89,7 @@ const Permissions = ({
|
||||
const {
|
||||
field: { value, onChange: fieldOnChange, ...field },
|
||||
fieldState,
|
||||
} = useController<Static<typeof schema>, "permissions">({
|
||||
} = useController<s.Static<typeof schema>, "permissions">({
|
||||
name: "permissions",
|
||||
control,
|
||||
});
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { Tabs, TextInput, Textarea, Tooltip, Switch } from "@mantine/core";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import {
|
||||
Default,
|
||||
type Static,
|
||||
StringIdentifier,
|
||||
objectCleanEmpty,
|
||||
ucFirstAllSnakeToPascalWithSpaces,
|
||||
} from "core/utils";
|
||||
import { objectCleanEmpty, omitKeys, ucFirstAllSnakeToPascalWithSpaces } from "core/utils";
|
||||
import {
|
||||
type TAppDataEntityFields,
|
||||
fieldsSchemaObject as originalFieldsSchemaObject,
|
||||
@@ -26,31 +18,25 @@ import { type SortableItemProps, SortableList } from "ui/components/list/Sortabl
|
||||
import { Popover } from "ui/components/overlay/Popover";
|
||||
import { type TFieldSpec, fieldSpecs } from "ui/modules/data/components/fields-specs";
|
||||
import { dataFieldsUiSchema } from "../../settings/routes/data.settings";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
import { useRoutePathState } from "ui/hooks/use-route-path-state";
|
||||
import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect";
|
||||
import type { TPrimaryFieldFormat } from "data/fields/PrimaryField";
|
||||
const { Type } = tbbox;
|
||||
import { s, stringIdentifier } from "core/object/schema";
|
||||
|
||||
const fieldsSchemaObject = originalFieldsSchemaObject;
|
||||
const fieldsSchema = Type.Union(Object.values(fieldsSchemaObject));
|
||||
const fieldsSchema = s.anyOf(Object.values(fieldsSchemaObject));
|
||||
|
||||
const fieldSchema = Type.Object(
|
||||
{
|
||||
name: StringIdentifier,
|
||||
new: Type.Optional(Type.Boolean({ const: true })),
|
||||
field: fieldsSchema,
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
type TFieldSchema = Static<typeof fieldSchema>;
|
||||
|
||||
const schema = Type.Object({
|
||||
fields: Type.Array(fieldSchema),
|
||||
const fieldSchema = s.strictObject({
|
||||
name: stringIdentifier,
|
||||
new: s.boolean({ const: true }).optional(),
|
||||
field: fieldsSchema,
|
||||
});
|
||||
type TFieldsFormSchema = Static<typeof schema>;
|
||||
type TFieldSchema = s.Static<typeof fieldSchema>;
|
||||
|
||||
const schema = s.strictObject({
|
||||
fields: s.array(fieldSchema),
|
||||
});
|
||||
type TFieldsFormSchema = s.Static<typeof schema>;
|
||||
|
||||
const fieldTypes = Object.keys(fieldsSchemaObject);
|
||||
const defaultType = fieldTypes[0];
|
||||
@@ -58,7 +44,9 @@ const commonProps = ["label", "description", "required", "fillable", "hidden", "
|
||||
|
||||
function specificFieldSchema(type: keyof typeof fieldsSchemaObject) {
|
||||
//console.log("specificFieldSchema", type);
|
||||
return Type.Omit(fieldsSchemaObject[type]?.properties.config, commonProps);
|
||||
return s.object(
|
||||
omitKeys(fieldsSchemaObject[type]?.properties.config.properties, commonProps as any),
|
||||
);
|
||||
}
|
||||
|
||||
export type EntityFieldsFormProps = {
|
||||
@@ -100,7 +88,8 @@ export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsForm
|
||||
reset,
|
||||
} = useForm({
|
||||
mode: "all",
|
||||
resolver: typeboxResolver(schema),
|
||||
// @todo: add resolver
|
||||
//resolver: typeboxResolver(schema),
|
||||
defaultValues: {
|
||||
fields: entityFields,
|
||||
} as TFieldsFormSchema,
|
||||
@@ -135,15 +124,14 @@ export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsForm
|
||||
}));
|
||||
|
||||
function handleAppend(_type: keyof typeof fieldsSchemaObject) {
|
||||
const newField = {
|
||||
append({
|
||||
name: "",
|
||||
new: true,
|
||||
field: {
|
||||
type: _type,
|
||||
config: Default(fieldsSchemaObject[_type]?.properties.config, {}) as any,
|
||||
config: fieldsSchemaObject[_type]?.properties.config.template() as any,
|
||||
},
|
||||
};
|
||||
append(newField);
|
||||
});
|
||||
}
|
||||
|
||||
const formProps = {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||
import { TextInput } from "@mantine/core";
|
||||
import { useFocusTrap } from "@mantine/hooks";
|
||||
import { TypeRegistry } from "@sinclair/typebox";
|
||||
import { type Static, StringEnum, StringIdentifier, registerCustomTypeboxKinds } from "core/utils";
|
||||
import { TRIGGERS } from "flows/flows-schema";
|
||||
import { forwardRef, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -16,18 +13,15 @@ import {
|
||||
ModalTitle,
|
||||
} from "../../../components/modal/Modal2";
|
||||
import { Step, Steps, useStepContext } from "../../../components/steps/Steps";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
|
||||
registerCustomTypeboxKinds(TypeRegistry);
|
||||
import { s, stringIdentifier } from "core/object/schema";
|
||||
|
||||
export type TCreateFlowModalSchema = any;
|
||||
const triggerNames = Object.keys(TRIGGERS) as unknown as (keyof typeof TRIGGERS)[];
|
||||
|
||||
const schema = Type.Object({
|
||||
name: StringIdentifier,
|
||||
trigger: StringEnum(triggerNames),
|
||||
mode: StringEnum(["async", "sync"]),
|
||||
const schema = s.strictObject({
|
||||
name: stringIdentifier,
|
||||
trigger: s.string({ enum: triggerNames }),
|
||||
mode: s.string({ enum: ["async", "sync"] }),
|
||||
});
|
||||
|
||||
export const FlowCreateModal = forwardRef<Modal2Ref>(function FlowCreateModal(props, ref) {
|
||||
@@ -61,16 +55,17 @@ export function StepCreate() {
|
||||
register,
|
||||
formState: { isValid, errors },
|
||||
} = useForm({
|
||||
resolver: typeboxResolver(schema),
|
||||
// @todo: implement resolver
|
||||
//resolver: typeboxResolver(schema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
trigger: "manual",
|
||||
mode: "async",
|
||||
} as Static<typeof schema>,
|
||||
} as s.Static<typeof schema>,
|
||||
mode: "onSubmit",
|
||||
});
|
||||
|
||||
async function onSubmit(data: Static<typeof schema>) {
|
||||
async function onSubmit(data: s.Static<typeof schema>) {
|
||||
console.log(data, isValid);
|
||||
actions.flow.create(data.name, {
|
||||
trigger: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useHotkeys } from "@mantine/hooks";
|
||||
import { type TObject, ucFirst } from "core/utils";
|
||||
import { ucFirst } from "core/utils";
|
||||
import { omit } from "lodash-es";
|
||||
import { type ReactNode, useMemo, useRef, useState } from "react";
|
||||
import { TbSettings } from "react-icons/tb";
|
||||
@@ -18,10 +18,11 @@ import { Link, Route, useLocation } from "wouter";
|
||||
import { extractSchema } from "../utils/schema";
|
||||
import { SettingNewModal, type SettingsNewModalProps } from "./SettingNewModal";
|
||||
import { SettingSchemaModal, type SettingsSchemaModalRef } from "./SettingSchemaModal";
|
||||
import type { s } from "core/object/schema";
|
||||
|
||||
export type SettingProps<
|
||||
Schema extends TObject = TObject,
|
||||
Props = Schema extends TObject<infer TProperties> ? TProperties : any,
|
||||
Schema extends s.ObjectSchema = s.ObjectSchema,
|
||||
Props = Schema extends s.ObjectSchema<infer TProperties> ? TProperties : any,
|
||||
> = {
|
||||
schema: Schema;
|
||||
config: any;
|
||||
@@ -44,7 +45,7 @@ export type SettingProps<
|
||||
};
|
||||
};
|
||||
|
||||
export function Setting<Schema extends TObject = any>({
|
||||
export function Setting<Schema extends s.ObjectSchema = s.ObjectSchema>({
|
||||
schema,
|
||||
uiSchema,
|
||||
config,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useDisclosure, useFocusTrap } from "@mantine/hooks";
|
||||
import type { TObject } from "core/utils";
|
||||
import { omit } from "lodash-es";
|
||||
import { useRef, useState } from "react";
|
||||
import { TbCirclePlus, TbVariable } from "react-icons/tb";
|
||||
import { useBknd } from "ui/client/BkndProvider";
|
||||
import { Button } from "ui/components/buttons/Button";
|
||||
import * as Formy from "ui/components/form/Formy";
|
||||
@@ -10,9 +8,10 @@ import { JsonSchemaForm, type JsonSchemaFormRef } from "ui/components/form/json-
|
||||
import { Dropdown } from "ui/components/overlay/Dropdown";
|
||||
import { Modal } from "ui/components/overlay/Modal";
|
||||
import { useLocation } from "wouter";
|
||||
import type { s } from "core/object/schema";
|
||||
|
||||
export type SettingsNewModalProps = {
|
||||
schema: TObject;
|
||||
schema: s.ObjectSchema;
|
||||
uiSchema?: object;
|
||||
anyOfValues?: Record<string, { label: string; icon?: any }>;
|
||||
path: string[];
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { Static, TObject } from "core/utils";
|
||||
import type { JSONSchema7 } from "json-schema";
|
||||
import { cloneDeep, omit, pick } from "lodash-es";
|
||||
import type { s } from "core/object/schema";
|
||||
|
||||
export function extractSchema<
|
||||
Schema extends TObject,
|
||||
Schema extends s.ObjectSchema,
|
||||
Keys extends keyof Schema["properties"],
|
||||
Config extends Static<Schema>,
|
||||
Config extends s.Static<Schema>,
|
||||
>(
|
||||
schema: Schema,
|
||||
config: Config,
|
||||
@@ -22,12 +22,12 @@ export function extractSchema<
|
||||
},
|
||||
] {
|
||||
if (!schema.properties) {
|
||||
return [{ ...schema }, config, {} as any];
|
||||
return [{ ...schema.toJSON() }, config, {} as any];
|
||||
}
|
||||
|
||||
const newSchema = cloneDeep(schema);
|
||||
const updated = {
|
||||
...newSchema,
|
||||
...newSchema.toJSON(),
|
||||
properties: omit(newSchema.properties, keys),
|
||||
};
|
||||
if (updated.required) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { parse } from "core/utils";
|
||||
import { AppFlows } from "flows/AppFlows";
|
||||
import { useState } from "react";
|
||||
import { JsonViewer } from "../../../components/code/JsonViewer";
|
||||
import { JsonSchemaForm } from "../../../components/form/json-schema";
|
||||
import { Scrollable } from "../../../layouts/AppShell/AppShell";
|
||||
import { parse } from "core/object/schema";
|
||||
|
||||
export default function FlowCreateSchemaTest() {
|
||||
//const schema = flowsConfigSchema;
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function JsonSchemaForm3() {
|
||||
return (
|
||||
<Scrollable>
|
||||
<div className="flex flex-col p-3">
|
||||
<Form schema={_schema.auth} options={formOptions} />
|
||||
<Form schema={_schema.auth.toJSON()} options={formOptions} />
|
||||
|
||||
{/*<Form
|
||||
onChange={(data) => console.log("change", data)}
|
||||
|
||||
Reference in New Issue
Block a user