added format command and added trailing commas to reduce conflicts

This commit is contained in:
dswbx
2025-02-26 20:06:03 +01:00
parent 88b5359f1c
commit 7743f71a11
414 changed files with 3622 additions and 3610 deletions

View File

@@ -6,7 +6,7 @@ import {
type Field,
JsonField,
JsonSchemaField,
RelationField
RelationField,
} from "data";
import { MediaField } from "media/MediaField";
import { type ComponentProps, Suspense } from "react";
@@ -37,7 +37,7 @@ export function EntityForm({
Form,
data,
className,
action
action,
}: EntityFormProps) {
const fields = entity.getFillableFields(action, true);
console.log("data", { data, fields });
@@ -120,7 +120,7 @@ export function EntityForm({
type EntityFormFieldProps<
T extends keyof JSX.IntrinsicElements = "input",
F extends Field = Field
F extends Field = Field,
> = ComponentProps<T> & {
fieldApi: FieldApi<any, any>;
field: F;
@@ -203,7 +203,7 @@ function EntityMediaFormField({
field,
entity,
entityId,
disabled
disabled,
}: {
formApi: FormApi<any>;
field: MediaField;
@@ -232,7 +232,7 @@ function EntityMediaFormField({
entity={{
name: entity.name,
id: entityId,
field: field.name
field: field.name,
}}
/>
</Formy.Group>

View File

@@ -9,7 +9,7 @@ import {
TbChevronsRight,
TbSelector,
TbSquare,
TbSquareCheckFilled
TbSquareCheckFilled,
} from "react-icons/tb";
import { twMerge } from "tailwind-merge";
import { Button } from "ui/components/buttons/Button";
@@ -58,7 +58,7 @@ export const EntityTable: React.FC<EntityTableProps> = ({
perPage = 10,
perPageOptions,
onClickPerPage,
classNames
classNames,
}) => {
select = select && select.length > 0 ? select : entity.getSelect();
total = total || data.length;
@@ -157,7 +157,7 @@ export const EntityTable: React.FC<EntityTableProps> = ({
<Dropdown
items={perPageOptions.map((perPage) => ({
label: String(perPage),
perPage
perPage,
}))}
position="top-end"
onClickItem={(item: any) => onClickPerPage?.(item.perPage)}
@@ -182,7 +182,7 @@ export const EntityTable: React.FC<EntityTableProps> = ({
const SortIndicator = ({
sort,
field
field,
}: {
sort: Pick<EntityTableProps, "sort">["sort"];
field: string;
@@ -220,7 +220,7 @@ const TableNav: React.FC<TableNavProps> = ({ current, total, onClick }: TableNav
{ value: 1, Icon: TbChevronsLeft, disabled: current === 1 },
{ value: current - 1, Icon: TbChevronLeft, disabled: current === 1 },
{ value: current + 1, Icon: TbChevronRight, disabled: current === total },
{ value: total, Icon: TbChevronsRight, disabled: current === total }
{ value: total, Icon: TbChevronsRight, disabled: current === total },
] as const;
return navMap.map((nav, key) => (

View File

@@ -37,7 +37,7 @@ export function EntityTable2({ entity, select, ...props }: EntityTableProps) {
console.warn(
"Couldn't render value",
{ value, property, entity, select, columns, ...props },
e
e,
);
}

View File

@@ -16,7 +16,7 @@ function entitiesToNodes(entities: AppDataConfig["entities"]): Node<TAppDataEnti
dragHandle: ".drag-handle",
position: { x: 0, y: 0 },
sourcePosition: Position.Right,
targetPosition: Position.Left
targetPosition: Position.Left,
};
});
}
@@ -36,46 +36,46 @@ function relationsToEdges(relations: AppDataConfig["relations"]) {
source: relation.source,
target: relation.target,
sourceHandle,
targetHandle: relation.target + ":id"
targetHandle: relation.target + ":id",
};
});
}
const nodeTypes = {
entity: EntityTableNode.Component
entity: EntityTableNode.Component,
} as const;
export function DataSchemaCanvas() {
const {
config: { data }
config: { data },
} = useBknd();
const { theme } = useBkndSystemTheme();
const nodes = entitiesToNodes(data.entities);
const edges = relationsToEdges(data.relations).map((e) => ({
...e,
style: {
stroke: theme === "light" ? "#ccc" : "#666"
stroke: theme === "light" ? "#ccc" : "#666",
},
type: "smoothstep",
markerEnd: {
type: MarkerType.Arrow,
width: 20,
height: 20,
color: theme === "light" ? "#aaa" : "#777"
}
color: theme === "light" ? "#aaa" : "#777",
},
}));
const nodeLayout = layoutWithDagre({
nodes: nodes.map((n) => ({
id: n.id,
...EntityTableNode.getSize(n.data)
...EntityTableNode.getSize(n.data),
})),
edges,
graph: {
rankdir: "LR",
marginx: 50,
marginy: 50
}
marginy: 50,
},
});
nodeLayout.nodes.forEach((node) => {
@@ -95,7 +95,7 @@ export function DataSchemaCanvas() {
maxZoom={2}
fitViewOptions={{
minZoom: 0.1,
maxZoom: 0.8
maxZoom: 0.8,
}}
>
<Panels zoom minimap />

View File

@@ -47,14 +47,14 @@ function NodeComponent(props: NodeProps<Node<TAppDataEntity & { label: string }>
const handleStyle = {
background: "transparent",
border: "none"
border: "none",
};
const TableRow = ({
field,
table,
index,
onHover,
last
last,
}: {
field: TableField;
table: string;
@@ -71,7 +71,7 @@ const TableRow = ({
className={twMerge(
"flex flex-row w-full justify-between font-mono py-1.5 px-2.5 border-b border-primary/15 border-l border-r cursor-auto",
last && "rounded-bl-lg rounded-br-lg",
"hover:bg-primary/5"
"hover:bg-primary/5",
)}
>
{handles && (
@@ -108,7 +108,7 @@ const TableRow = ({
export const HEIGHTS = {
header: 30,
row: 32.5
row: 32.5,
};
export const EntityTableNode = {
@@ -118,7 +118,7 @@ export const EntityTableNode = {
const field_count = Object.keys(fields).length;
return {
width: 320,
height: HEIGHTS.header + HEIGHTS.row * field_count
height: HEIGHTS.header + HEIGHTS.row * field_count,
};
}
},
};

View File

@@ -7,7 +7,7 @@ import {
TbPhoto,
TbSelector,
TbTextCaption,
TbToggleLeft
TbToggleLeft,
} from "react-icons/tb";
export type TFieldSpec = {
@@ -26,55 +26,55 @@ export const fieldSpecs: TFieldSpec[] = [
icon: TbTextCaption,
addable: false,
disabled: ["name"],
hidden: ["virtual"]
hidden: ["virtual"],
},
{
type: "text",
label: "Text",
icon: TbTextCaption
icon: TbTextCaption,
},
{
type: "number",
label: "Number",
icon: TbNumber123
icon: TbNumber123,
},
{
type: "boolean",
label: "Boolean",
icon: TbToggleLeft
icon: TbToggleLeft,
},
{
type: "date",
label: "Date",
icon: TbCalendar
icon: TbCalendar,
},
{
type: "enum",
label: "Enum",
icon: TbSelector
icon: TbSelector,
},
{
type: "json",
label: "JSON",
icon: TbBraces
icon: TbBraces,
},
{
type: "jsonschema",
label: "JSON Schema",
icon: TbCodePlus
icon: TbCodePlus,
},
{
type: "relation",
label: "Relation",
icon: TbCirclesRelation,
addable: false,
hidden: ["virtual"]
hidden: ["virtual"],
},
{
type: "media",
label: "Media",
icon: TbPhoto,
addable: false,
hidden: ["virtual"]
}
hidden: ["virtual"],
},
];

View File

@@ -39,7 +39,7 @@ export function EntityJsonSchemaFormField({
formData={formData}
uiSchema={{
"ui:globalOptions": { flexDirection: "row" },
...field.getJsonUiSchema()
...field.getJsonUiSchema(),
}}
/>
</div>

View File

@@ -190,8 +190,15 @@ type PropoverTableProps = Omit<EntityTableProps, "data"> & {
container: ResponseObject;
query: any;
toggle: () => void;
}
const PopoverTable = ({ container, entity, query, toggle, onClickRow, onClickPage }: PropoverTableProps) => {
};
const PopoverTable = ({
container,
entity,
query,
toggle,
onClickRow,
onClickPage,
}: PropoverTableProps) => {
function handleNext() {
if (query.limit * query.page < container.meta?.count) {
onClickPage?.(query.page + 1);

View File

@@ -18,21 +18,21 @@ export const ModalActions = ["entity", "relation", "media"] as const;
export const entitySchema = Type.Composite([
Type.Object({
name: StringIdentifier
name: StringIdentifier,
}),
entitiesSchema
entitiesSchema,
]);
const schemaAction = Type.Union([
StringEnum(["entity", "relation", "media"]),
Type.String({ pattern: "^template-" })
Type.String({ pattern: "^template-" }),
]);
export type TSchemaAction = Static<typeof schemaAction>;
const createFieldSchema = Type.Object({
entity: StringIdentifier,
name: StringIdentifier,
field: Type.Array(fieldsSchema)
field: Type.Array(fieldsSchema),
});
export type TFieldCreate = Static<typeof createFieldSchema>;
@@ -42,30 +42,30 @@ const createModalSchema = Type.Object(
initial: Type.Optional(Type.Any()),
entities: Type.Optional(
Type.Object({
create: Type.Optional(Type.Array(entitySchema))
})
create: Type.Optional(Type.Array(entitySchema)),
}),
),
relations: Type.Optional(
Type.Object({
create: Type.Optional(Type.Array(Type.Union(relationsSchema)))
})
create: Type.Optional(Type.Array(Type.Union(relationsSchema))),
}),
),
fields: Type.Optional(
Type.Object({
create: Type.Optional(Type.Array(createFieldSchema))
})
)
create: Type.Optional(Type.Array(createFieldSchema)),
}),
),
},
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type TCreateModalSchema = Static<typeof createModalSchema>;
export function CreateModal({
context,
id,
innerProps: { initialPath = [], initialState }
innerProps: { initialPath = [], initialState },
}: ContextModalProps<{ initialPath?: string[]; initialState?: TCreateModalSchema }>) {
const [path, setPath] = useState<string[]>(initialPath);
console.log("...", initialPath, initialState);
@@ -111,7 +111,7 @@ CreateModal.defaultTitle = undefined;
CreateModal.modalProps = {
withCloseButton: false,
size: "xl",
padding: 0
padding: 0,
} satisfies Partial<ModalProps>;
export { ModalBody, ModalFooter, ModalTitle, useStepContext, relationsSchema };

View File

@@ -4,7 +4,7 @@ import {
IconAugmentedReality,
IconBox,
IconCirclesRelation,
IconInfoCircle
IconInfoCircle,
} from "@tabler/icons-react";
import { ucFirst } from "core/utils";
import { useEffect, useState } from "react";
@@ -37,8 +37,8 @@ export function StepCreate() {
type: "Entity",
name: entity.name,
json: entity,
run: async () => await $data.actions.entity.add(entity.name, entity)
}))
run: async () => await $data.actions.entity.add(entity.name, entity),
})),
);
}
if (state.fields?.create) {
@@ -52,8 +52,8 @@ export function StepCreate() {
run: async () =>
await $data.actions.entity
.patch(field.entity)
.fields.add(field.name, field.field as any)
}))
.fields.add(field.name, field.field as any),
})),
);
}
if (state.relations?.create) {
@@ -64,8 +64,8 @@ export function StepCreate() {
type: "Relation",
name: `${rel.source} -> ${rel.target} (${rel.type})`,
json: rel,
run: async () => await $data.actions.relations.add(rel)
}))
run: async () => await $data.actions.relations.add(rel),
})),
);
}
@@ -92,7 +92,7 @@ export function StepCreate() {
items,
states.length,
items.length,
states.every((s) => s === true)
states.every((s) => s === true),
);
if (items.length === states.length && states.every((s) => s === true)) {
b.actions.reload().then(close);
@@ -116,7 +116,7 @@ export function StepCreate() {
nextLabel="Create"
next={{
onClick: handleCreate,
disabled: submitting
disabled: submitting,
}}
prev={{ onClick: stepBack, disabled: submitting }}
debug={{ state }}
@@ -142,7 +142,7 @@ const SummaryItem: React.FC<SummaryItemProps> = ({
json,
state,
action,
initialExpanded = false
initialExpanded = false,
}) => {
const [expanded, handlers] = useDisclosure(initialExpanded);
const error = typeof state !== "undefined" && state !== true;
@@ -153,7 +153,7 @@ const SummaryItem: React.FC<SummaryItemProps> = ({
className={twMerge(
"flex flex-col border border-muted rounded bg-background mb-2",
error && "bg-red-500/20",
done && "bg-green-500/20"
done && "bg-green-500/20",
)}
>
<div className="flex flex-row gap-4 px-2 py-2 items-center">

View File

@@ -8,7 +8,7 @@ import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelec
import { useEvent } from "ui/hooks/use-event";
import {
EntityFieldsForm,
type EntityFieldsFormRef
type EntityFieldsFormRef,
} from "ui/routes/data/forms/entity.fields.form";
import { ModalBody, ModalFooter, type TCreateModalSchema, useStepContext } from "./CreateModal";
@@ -25,9 +25,9 @@ export function StepEntityFields() {
fields: defaultFields,
config: {
sort_field: "id",
sort_dir: "asc"
}
})
sort_dir: "asc",
},
}),
);
const {
control,
@@ -35,11 +35,11 @@ export function StepEntityFields() {
getValues,
handleSubmit,
watch,
setValue
setValue,
} = useForm({
mode: "onTouched",
resolver: typeboxResolver(schema),
defaultValues: initial as NonNullable<Schema>
defaultValues: initial as NonNullable<Schema>,
});
const values = watch();
@@ -58,8 +58,8 @@ export function StepEntityFields() {
return {
...prev,
entities: {
create: [getValues() as any]
}
create: [getValues() as any],
},
};
});
@@ -119,7 +119,7 @@ export function StepEntityFields() {
<ModalFooter
next={{
disabled: !isValid,
type: "submit"
type: "submit",
//onClick: handleNext
}}
prev={{ onClick: stepBack }}

View File

@@ -8,7 +8,7 @@ import {
ModalFooter,
type TCreateModalSchema,
entitySchema,
useStepContext
useStepContext,
} from "./CreateModal";
export function StepEntity() {
@@ -18,7 +18,7 @@ export function StepEntity() {
const { register, handleSubmit, formState, watch } = useForm({
mode: "onTouched",
resolver: typeboxResolver(entitySchema),
defaultValues: state.entities?.create?.[0] ?? {}
defaultValues: state.entities?.create?.[0] ?? {},
});
/*const data = watch();
console.log("state", { isValid });
@@ -83,7 +83,7 @@ export function StepEntity() {
<ModalFooter
next={{
type: "submit",
disabled: !formState.isValid
disabled: !formState.isValid,
//onClick:
}}
prev={{ onClick: stepBack }}

View File

@@ -7,7 +7,7 @@ import {
StringEnum,
StringIdentifier,
Type,
registerCustomTypeboxKinds
registerCustomTypeboxKinds,
} from "core/utils";
import { ManyToOneRelation, type RelationType, RelationTypes } from "data";
import { type ReactNode, startTransition, useEffect } from "react";
@@ -32,30 +32,30 @@ const Relations: {
{
type: RelationTypes.ManyToOne,
label: "Many to One",
component: ManyToOne
component: ManyToOne,
},
{
type: RelationTypes.OneToOne,
label: "One to One",
component: OneToOne
component: OneToOne,
},
{
type: RelationTypes.ManyToMany,
label: "Many to Many",
component: ManyToMany
component: ManyToMany,
},
{
type: RelationTypes.Polymorphic,
label: "Polymorphic",
component: Polymorphic
}
component: Polymorphic,
},
];
const schema = Type.Object({
type: StringEnum(Relations.map((r) => r.type)),
source: StringIdentifier,
target: StringIdentifier,
config: Type.Object({})
config: Type.Object({}),
});
type ComponentCtx<T extends FieldValues = FieldValues> = {
@@ -75,10 +75,10 @@ export function StepRelation() {
formState: { isValid },
setValue,
watch,
control
control,
} = useForm({
resolver: typeboxResolver(schema),
defaultValues: (state.relations?.create?.[0] ?? {}) as Static<typeof schema>
defaultValues: (state.relations?.create?.[0] ?? {}) as Static<typeof schema>,
});
const data = watch();
@@ -88,8 +88,8 @@ export function StepRelation() {
return {
...prev,
relations: {
create: [data]
}
create: [data],
},
};
});
console.log("data", data);
@@ -131,7 +131,7 @@ export function StepRelation() {
data={Object.entries(entities ?? {}).map(([name, entity]) => ({
value: name,
label: entity.config?.name ?? name,
disabled: data.target === name
disabled: data.target === name,
}))}
/>
<div className="flex flex-col gap-1">
@@ -159,7 +159,7 @@ export function StepRelation() {
data={Object.entries(entities ?? {}).map(([name, entity]) => ({
value: name,
label: entity.config?.name ?? name,
disabled: data.source === name
disabled: data.source === name,
}))}
/>
</div>
@@ -169,7 +169,7 @@ export function StepRelation() {
Relations.find((r) => r.type === data.type)?.component({
register,
control,
data
data,
})}
</div>
</ModalBody>
@@ -177,7 +177,7 @@ export function StepRelation() {
next={{
type: "submit",
disabled: !isValid,
onClick: handleNext
onClick: handleNext,
}}
prev={{ onClick: stepBack }}
debug={{ state, path, data }}
@@ -267,8 +267,8 @@ function OneToOne({
data: {
source,
target,
config: { mappedBy, required }
}
config: { mappedBy, required },
},
}: ComponentCtx) {
return (
<>

View File

@@ -7,7 +7,7 @@ import {
ModalFooter,
type TCreateModalSchema,
type TSchemaAction,
useStepContext
useStepContext,
} from "./CreateModal";
import Templates from "./templates/register";
@@ -70,7 +70,7 @@ export function StepSelect() {
<ModalFooter
next={{
onClick: selected && nextStep(selected),
disabled: !selected
disabled: !selected,
}}
prev={{ onClick: stepBack }}
prevLabel="Cancel"
@@ -87,7 +87,7 @@ const RadioCard = ({
onClick,
selected,
compact = false,
disabled = false
disabled = false,
}: {
Icon: IconType;
title: string;
@@ -104,7 +104,7 @@ const RadioCard = ({
"flex gap-3 border border-primary/10 rounded cursor-pointer",
compact ? "flex-row p-4 items-center" : "flex-col p-5",
selected ? "bg-primary/10 border-primary/50" : "hover:bg-primary/5",
disabled && "opacity-50"
disabled && "opacity-50",
)}
>
<Icon className="size-10" />

View File

@@ -6,7 +6,7 @@ import {
StringEnum,
StringIdentifier,
Type,
transformObject
transformObject,
} from "core/utils";
import type { MediaFieldConfig } from "media/MediaField";
import { useEffect, useState } from "react";
@@ -20,14 +20,14 @@ import {
ModalFooter,
type TCreateModalSchema,
type TFieldCreate,
useStepContext
useStepContext,
} from "../../CreateModal";
const schema = Type.Object({
entity: StringIdentifier,
cardinality_type: StringEnum(["single", "multiple"], { default: "multiple" }),
cardinality: Type.Optional(Type.Number({ minimum: 1 })),
name: StringIdentifier
name: StringIdentifier,
});
type TCreateModalMediaSchema = Static<typeof schema>;
@@ -38,11 +38,11 @@ export function TemplateMediaComponent() {
handleSubmit,
formState: { isValid, errors },
watch,
control
control,
} = useForm({
mode: "onChange",
resolver: typeboxResolver(schema),
defaultValues: Default(schema, state.initial ?? {}) as TCreateModalMediaSchema
defaultValues: Default(schema, state.initial ?? {}) as TCreateModalMediaSchema,
});
const [forbidden, setForbidden] = useState<boolean>(false);
@@ -50,7 +50,7 @@ export function TemplateMediaComponent() {
const media_enabled = config.media.enabled ?? false;
const media_entity = config.media.entity_name ?? "media";
const entities = transformObject(config.data.entities ?? {}, (entity, name) =>
name !== media_entity ? entity : undefined
name !== media_entity ? entity : undefined,
);
const data = watch();
const forbidden_field_names = Object.keys(config.data.entities?.[data.entity]?.fields ?? {});
@@ -66,7 +66,7 @@ export function TemplateMediaComponent() {
setState((prev) => ({
...prev,
fields: { create: [field] },
relations: { create: [relation] }
relations: { create: [relation] },
}));
nextStep("create")();
@@ -92,7 +92,7 @@ export function TemplateMediaComponent() {
required
data={Object.entries(entities).map(([name, entity]) => ({
value: name,
label: entity.config?.name ?? name
label: entity.config?.name ?? name,
}))}
/>
<MantineRadio.Group
@@ -141,10 +141,10 @@ export function TemplateMediaComponent() {
<ModalFooter
next={{
type: "submit",
disabled: !isValid || !media_enabled || forbidden
disabled: !isValid || !media_enabled || forbidden,
}}
prev={{
onClick: stepBack
onClick: stepBack,
}}
debug={{ state, path, data }}
/>
@@ -169,9 +169,9 @@ function convert(media_entity: string, data: TCreateModalMediaSchema) {
hidden: false,
mime_types: [],
virtual: true,
entity: data.entity
}
}
entity: data.entity,
},
},
};
const relation = {
@@ -180,8 +180,8 @@ function convert(media_entity: string, data: TCreateModalMediaSchema) {
target: media_entity,
config: {
mappedBy: data.name,
targetCardinality: data.cardinality_type === "single" ? 1 : undefined
}
targetCardinality: data.cardinality_type === "single" ? 1 : undefined,
},
};
if (data.cardinality_type === "multiple") {

View File

@@ -5,5 +5,5 @@ export const TemplateMediaMeta = {
id: "template-media",
title: "Attach Media",
description: "Attach media to an entity",
Icon: TbPhoto
Icon: TbPhoto,
} satisfies StepTemplate;

View File

@@ -9,7 +9,7 @@ export type StepTemplate = {
};
const Templates: [() => JSX.Element, StepTemplate][] = [
[TemplateMediaComponent, TemplateMediaMeta]
[TemplateMediaComponent, TemplateMediaMeta],
];
export default Templates;