initial refactor

This commit is contained in:
dswbx
2025-06-21 13:35:58 +02:00
parent b2086c4da7
commit 42edce904f
93 changed files with 1021 additions and 1216 deletions
@@ -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();
@@ -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 });