import { typeboxResolver } from "@hookform/resolvers/typebox"; import { Select, Switch, TextInput } from "@mantine/core"; import { TypeRegistry } from "@sinclair/typebox"; import { type Static, StringEnum, StringIdentifier, Type, registerCustomTypeboxKinds } from "core/utils"; import { ManyToOneRelation, type RelationType, RelationTypes } from "data"; import { type ReactNode, useEffect } from "react"; import { type Control, type FieldValues, type UseFormRegister, useForm } from "react-hook-form"; import { useBknd } from "ui/client"; import { MantineNumberInput } from "ui/components/form/hook-form-mantine/MantineNumberInput"; import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect"; import { useStepContext } from "ui/components/steps/Steps"; import { ModalBody, ModalFooter, type TCreateModalSchema } from "./CreateModal"; // @todo: check if this could become an issue registerCustomTypeboxKinds(TypeRegistry); const Relations: { type: RelationType; label: string; component: (props: ComponentCtx) => ReactNode; }[] = [ { type: RelationTypes.ManyToOne, label: "Many to One", component: ManyToOne }, { type: RelationTypes.OneToOne, label: "One to One", component: OneToOne }, { type: RelationTypes.ManyToMany, label: "Many to Many", component: ManyToMany }, { type: RelationTypes.Polymorphic, label: "Polymorphic", component: Polymorphic } ]; const schema = Type.Object({ type: StringEnum(Relations.map((r) => r.type)), source: StringIdentifier, target: StringIdentifier, config: Type.Object({}) }); type ComponentCtx = { register: UseFormRegister; control: Control; data: T; }; export function StepRelation() { const { config } = useBknd(); const entities = config.data.entities; const { nextStep, stepBack, state, setState } = useStepContext(); const { register, handleSubmit, formState: { isValid }, setValue, watch, control } = useForm({ resolver: typeboxResolver(schema), defaultValues: (state.relations?.create?.[0] ?? {}) as Static }); const data = watch(); console.log("data", { data, schema }); function handleNext() { if (isValid) { setState((prev) => { return { ...prev, relations: { create: [data] } }; }); console.log("data", data); nextStep("create")(); } } return ( <>
({ value: name, label: entity.config?.name ?? name, disabled: data.target === name }))} /> setValue("config", {})} label="Relation Type" data={Relations.map((r) => ({ value: r.type, label: r.label }))} allowDeselect={false} /> ({ value: name, label: entity.config?.name ?? name, disabled: data.source === name }))} />
{data.type && Relations.find((r) => r.type === data.type)?.component({ register, control, data })}
); } const Pre = ({ children }: { children: ReactNode }) => ( {children} ); const Callout = ({ children }: { children: ReactNode }) => (
{children}
); function ManyToOne({ register, control, data: { source, target, config } }: ComponentCtx) { return ( <>
{source && target && config && ( <>

Many

{source}
will each have one reference to
{target}
.

A property

{config.mappedBy || target}_id
will be added to{" "}
{source}
(which references
{target}
).

When creating

{source}
, a reference to
{target}
is{" "} {config.required ? "required" : "optional"}.

{config.sourceCardinality ? (

{source}
should not have more than{" "}
{config.sourceCardinality}
referencing entr {config.sourceCardinality === 1 ? "y" : "ies"} to
{source}
.

) : null}
)} ); } function OneToOne({ register, control, data: { source, target, config: { mappedBy, required } } }: ComponentCtx) { return ( <>
{source && target && ( <>

A single entry of

{source}
will have a reference to{" "}
{target}
.

A property

{mappedBy || target}_id
will be added to{" "}
{source}
(which references
{target}
).

When creating

{source}
, a reference to
{target}
is{" "} {required ? "required" : "optional"}.

)} ); } function ManyToMany({ register, control, data: { source, target, config } }: ComponentCtx) { const table = config.connectionTable ? config.connectionTable : source && target ? `${source}_${target}` : ""; return ( <>
{/**/}
{/**/}
{source && target && ( <>

Many

{source}
will have many
{target}
.

A connection table

{table}
will be created to store the relations.

)} ); } function Polymorphic({ register, control, data: { type, source, target, config } }: ComponentCtx) { return ( <>
{source && target && ( <>

{source}
will have many
{target}
.

{target}
will get additional properties
reference
and{" "}
entity_id
to make the (polymorphic) reference.

{config.targetCardinality ? (

{source}
should not have more than{" "}
{config.targetCardinality}
reference {config.targetCardinality === 1 ? "" : "s"} to
{target}
.

) : null}
)} ); }