alex find bugs

This commit is contained in:
IDONTSUDO 2024-09-24 17:18:56 +03:00
parent d804aa2edf
commit 4e6132a872
27 changed files with 485 additions and 296 deletions

View file

@ -1,15 +1,17 @@
import { ClassConstructor } from "class-transformer"; import { ClassConstructor } from "class-transformer";
import React from "react"; import React from "react";
import { NavigateFunction, useNavigate } from "react-router-dom";
interface LifeCycleStore { interface LifeCycleStore {
init?: () => void; init?: (navigate?: NavigateFunction | undefined) => void;
dispose?: () => void; dispose?: () => void;
} }
export const useStore = <S extends LifeCycleStore>(storeConstructor: ClassConstructor<S>) => { export const useStore = <S extends LifeCycleStore>(storeConstructor: ClassConstructor<S>) => {
const [store] = React.useState(new storeConstructor()); const [store] = React.useState(new storeConstructor());
const navigate = useNavigate();
React.useEffect(() => { React.useEffect(() => {
store?.init?.(); store?.init?.(navigate);
return () => { return () => {
store?.dispose?.(); store?.dispose?.();
}; };

View file

@ -0,0 +1,58 @@
import { IsNotEmpty, IsString } from "class-validator";
import { BehaviorTreeBuilderStore } from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_store";
import { datasetFormMockContext, datasetFormMockResult, defaultFormValue } from "../../features/dataset/dataset_model";
import { DependencyViewModel } from "./skill_model";
import { ValidationModel } from "./validation_model";
export class FormBuilderValidationModel extends ValidationModel implements DependencyViewModel {
@IsNotEmpty()
@IsString()
public result: string;
@IsNotEmpty()
@IsString()
public context: string;
public form: string[];
public output: any;
constructor(context: string, result: string, form: string[], output: string) {
super();
this.context = context;
this.result = result;
this.form = form;
this.output = output;
}
toView = (store: BehaviorTreeBuilderStore | undefined) => <>IsFilled</>;
static isEmpty = (formBuilderValidationModel: FormBuilderValidationModel) =>
formBuilderValidationModel.context.isEmpty() &&
formBuilderValidationModel.result.isEmpty() &&
formBuilderValidationModel.form.isEmpty();
static datasetEmpty() {
return new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], defaultFormValue);
}
static empty() {
return new FormBuilderValidationModel("", "", [], "");
}
static emptyTest() {
return new FormBuilderValidationModel(``, ``, [], defaultFormValue);
}
static creteDataSetTest() {
return new FormBuilderValidationModel(``, scene, [], "");
}
static vision(): FormBuilderValidationModel {
return new FormBuilderValidationModel(
`ENUM PRETRAIN = "true","false";`,
`{
"numberOfEpochs": \${numberOfEpochs:number:10},
"selectDataset": \${<SelectDataset/>:OBJECT:{"dataset": {}},
"pretrain": \${pretrain:Enum<PRETRAIN>:true}
}`,
[],
""
);
}
}
export const scene = `{
"center_shell": [\${CENTER_SHELL_1:number:0}, \${CENTER_SHELL_2:number:0}, \${CENTER_SHELL_3:number:0}],
"scene":\${<SelectScene/>:OBJECT:{"details": []}
}`;

View file

@ -70,9 +70,7 @@ export class BtActionViewModel extends ValidationModel implements IBTAction {
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
name: string; name: string;
@IsNotEmpty()
@IsString()
format: string;
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
type: string; type: string;
@ -84,16 +82,15 @@ export class BtActionViewModel extends ValidationModel implements IBTAction {
@IsNotEmpty() @IsNotEmpty()
@IsEnum(BtAction) @IsEnum(BtAction)
typeAction: BtAction; typeAction: BtAction;
constructor(name: string, format: string, type: string, param: IParam[], result: string[], typeAction: BtAction) { constructor(name: string, type: string, param: IParam[], result: string[], typeAction: BtAction) {
super(); super();
this.name = name; this.name = name;
this.format = format;
this.type = type; this.type = type;
this.param = param; this.param = param;
this.result = result; this.result = result;
this.typeAction = typeAction; this.typeAction = typeAction;
} }
static empty = () => new BtActionViewModel("", "", "", [], [], BtAction.ACTION); static empty = () => new BtActionViewModel("", "", [], [], BtAction.ACTION);
public validParam = (type: string) => this.param.someR((param) => param.type === type); public validParam = (type: string) => this.param.someR((param) => param.type === type);
} }
export interface IInterface { export interface IInterface {
@ -228,6 +225,8 @@ export class SkillModel extends ValidationModel implements ISkill {
@ValidateNested() @ValidateNested()
@Type(() => Module) @Type(() => Module)
Module: IModule; Module: IModule;
@IsNotEmpty()
@IsArray()
@Type(() => BtActionViewModel) @Type(() => BtActionViewModel)
BTAction: BtActionViewModel[]; BTAction: BtActionViewModel[];
topicsOut: TopicViewModel[] = []; topicsOut: TopicViewModel[] = [];
@ -337,7 +336,7 @@ export class Skills {
toSkillView = (): ISkillView[] => toSkillView = (): ISkillView[] =>
this.skills.map((el) => { this.skills.map((el) => {
return { return {
name: el.SkillPackage.name, name: el.Module.name,
children: el.BTAction.map((act) => { children: el.BTAction.map((act) => {
return { name: act.name, uuid: v4() }; return { name: act.name, uuid: v4() };
}), }),
@ -487,4 +486,3 @@ export class Skills {
() => Result.error(false) () => Result.error(false)
); );
} }

View file

@ -18,6 +18,7 @@ import { DigitalTwinsScreen, DigitalTwinsScreenPath } from "../../features/digit
import { TopicsScreen, TopicsScreenPath } from "../../features/topics/topics_screen"; import { TopicsScreen, TopicsScreenPath } from "../../features/topics/topics_screen";
import { SkillsScreen, SkillsScreenPath } from "../../features/skills/skills_screen"; import { SkillsScreen, SkillsScreenPath } from "../../features/skills/skills_screen";
import { CalculationsTemplateScreenPath } from "../../features/calculations_template/calculations_template_screen"; import { CalculationsTemplateScreenPath } from "../../features/calculations_template/calculations_template_screen";
import { BehaviorTreeManagerScreen, BehaviorTreeManagerScreenPath } from "../../features/behavior_tree_manager/behavior_tree_manager_screen";
const idURL = ":id"; const idURL = ":id";
export const router = createBrowserRouter([ export const router = createBrowserRouter([
@ -85,4 +86,8 @@ export const router = createBrowserRouter([
path: CalculationsTemplateScreenPath, path: CalculationsTemplateScreenPath,
element: <CalculationInstanceScreen />, element: <CalculationInstanceScreen />,
}, },
{
path: BehaviorTreeManagerScreenPath,
element: <BehaviorTreeManagerScreen />,
},
]); ]);

View file

@ -2,12 +2,12 @@ import * as React from "react";
import { FormViewModel, InputBuilderViewModel, InputType } from "./form_view_model"; import { FormViewModel, InputBuilderViewModel, InputType } from "./form_view_model";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { FormBuilderStore } from "./form_builder_store"; import { FormBuilderStore } from "./form_builder_store";
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model"; import { CoreSelect } from "../select/select";
import { CoreSelect } from "../select/select";
import { CoreInput } from "../input/input"; import { CoreInput } from "../input/input";
import { Icon } from "../icons/icons"; import { Icon } from "../icons/icons";
import { CoreText, CoreTextType } from "../text/text"; import { CoreText, CoreTextType } from "../text/text";
import { getFormBuilderComponents } from "./forms/form_builder_components"; import { getFormBuilderComponents } from "./forms/form_builder_components";
import { FormBuilderValidationModel } from "../../model/form_builder_validation_model";
export interface IFormBuilder { export interface IFormBuilder {
formBuilder: FormBuilderValidationModel; formBuilder: FormBuilderValidationModel;

View file

@ -1,8 +1,8 @@
import { makeAutoObservable } from "mobx"; import { makeAutoObservable } from "mobx";
import { FormViewModel } from "./form_view_model"; import { FormViewModel } from "./form_view_model";
import { TypedEvent } from "../../helper/typed_event"; import { TypedEvent } from "../../helper/typed_event";
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model"; import { FormBuilderValidationModel } from "../../model/form_builder_validation_model";
export class ChangerForm extends TypedEvent<FormBuilderValidationModel | undefined> {} export class ChangerForm extends TypedEvent<FormBuilderValidationModel | undefined> {}
export class FormBuilderStore { export class FormBuilderStore {

View file

@ -1,8 +1,8 @@
import { makeAutoObservable, observable } from "mobx"; import { makeAutoObservable, observable } from "mobx";
import { Result } from "../../helper/result"; import { Result } from "../../helper/result";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model"; import { FormBuilderValidationModel } from "../../model/form_builder_validation_model";
export enum InputType { export enum InputType {
NUMBER = "number", NUMBER = "number",
STRING = "string", STRING = "string",

View file

@ -14,6 +14,7 @@ interface IInputProps extends IStyle {
validation?: (value: string) => boolean; validation?: (value: string) => boolean;
error?: string; error?: string;
type?: CoreInputType; type?: CoreInputType;
trim?: boolean;
} }
export const CoreInput = (props: IInputProps) => { export const CoreInput = (props: IInputProps) => {
@ -49,7 +50,7 @@ export const CoreInput = (props: IInputProps) => {
<div <div
ref={ref} ref={ref}
contentEditable={true} contentEditable={true}
style={{ style={{
backgroundColor: "#00008000", backgroundColor: "#00008000",
border: 1, border: 1,
fontSize: isSmall ? 12 : 16, fontSize: isSmall ? 12 : 16,
@ -63,10 +64,13 @@ export const CoreInput = (props: IInputProps) => {
top: isSmall ? -8 : undefined, top: isSmall ? -8 : undefined,
}} }}
onInput={(e) => { onInput={(e) => {
const val = e.currentTarget.innerText; let val = e.currentTarget.innerText;
setValue(val); setValue(val);
if (val) { if (val) {
if (props.trim) {
val = val.trim().replaceAll("\n", "");
}
if (props.validation !== undefined && props.validation(val) && props.onChange) { if (props.validation !== undefined && props.validation(val) && props.onChange) {
props.onChange(val); props.onChange(val);
return; return;

View file

@ -5,10 +5,11 @@ import { CoreText, CoreTextType, FontType } from "../text/text";
interface InputV2Props { interface InputV2Props {
label: string; label: string;
value?: string; value?: string;
trim?: boolean;
height?: number; height?: number;
onChange?: (text: string) => void; onChange?: (text: string) => void;
} }
export const InputV2: React.FC<InputV2Props> = ({ label, height, value, onChange }) => { export const InputV2: React.FC<InputV2Props> = ({ label, height, value, onChange, trim }) => {
return ( return (
<div <div
style={{ style={{
@ -29,7 +30,9 @@ export const InputV2: React.FC<InputV2Props> = ({ label, height, value, onChange
/> />
<CoreText <CoreText
onChange={(text) => { onChange={(text) => {
if (onChange) onChange(text); let result = text;
if (trim) result = result.trim().replaceAll("\n", "");
if (onChange) onChange(result);
}} }}
style={{ style={{
backgroundColor: themeStore.theme.surfaceContainerHighest, backgroundColor: themeStore.theme.surfaceContainerHighest,

View file

@ -15,7 +15,6 @@ export class BehaviorTreeBuilderHttpRepository extends CoreHttpRepository {
SkillModel SkillModel
)) as unknown as Promise<Result<HttpError, SkillModel[]>>; )) as unknown as Promise<Result<HttpError, SkillModel[]>>;
}; };
saveNewBt = async (model: BehaviorTreeViewModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model);
getBtById = async (id: string): Promise<Result<HttpError, BehaviorTreeModel>> => getBtById = async (id: string): Promise<Result<HttpError, BehaviorTreeModel>> =>
this._jsonToClassInstanceRequest<BehaviorTreeModel>( this._jsonToClassInstanceRequest<BehaviorTreeModel>(
HttpMethod.GET, HttpMethod.GET,

View file

@ -2,7 +2,7 @@ import * as React from "react";
import { useRete } from "rete-react-plugin"; import { useRete } from "rete-react-plugin";
import { createEditor } from "./ui/editor/editor"; import { createEditor } from "./ui/editor/editor";
import { SkillTree } from "./ui/skill_tree/skill_tree"; import { SkillTree } from "./ui/skill_tree/skill_tree";
import { BehaviorTreeBuilderStore, DrawerState, StoreUIType } from "./behavior_tree_builder_store"; import { BehaviorTreeBuilderStore, DrawerState } from "./behavior_tree_builder_store";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { match } from "ts-pattern"; import { match } from "ts-pattern";
import { Icon } from "../../../core/ui/icons/icons"; import { Icon } from "../../../core/ui/icons/icons";
@ -51,179 +51,97 @@ export const BehaviorTreeBuilderScreen = observer(() => {
const domReact: DOMReact = ref.current.getBoundingClientRect(); const domReact: DOMReact = ref.current.getBoundingClientRect();
store.dragZoneSetOffset(0, domReact.y, domReact.width, domReact.height); store.dragZoneSetOffset(0, domReact.y, domReact.width, domReact.height);
} }
}, [store.type]); }, [ref.current]);
React.useEffect(() => { React.useEffect(() => {
store.init(navigate).then(() => { store.init(navigate).then(() => {
store.initParam(id).then(() => {}); store.initParam(id);
}); });
return () => { return () => {
store.dispose(); store.dispose();
}; };
}, [id, navigate, ref, store]); }, []);
return ( return (
<MainPageV2 <MainPageV2
rightChild={ rightChild={
<> <>
{match(store.type) <div
.with(StoreUIType.ViewBehaviorTree, () => ( style={{
<div width: "100%",
style={{ display: "flex",
width: "100%", flexDirection: "row",
display: "flex", justifyContent: "end",
flexDirection: "row", alignItems: "center",
justifyContent: "end", }}
alignItems: "center", >
}} <ButtonV2 style={{ height: 40 }} onClick={() => {}} text="Запуск" textColor={themeStore.theme.black} />
> <div style={{ width: 10 }} />
<ButtonV2 style={{ height: 40 }} onClick={() => {}} text="Запуск" textColor={themeStore.theme.black} /> <ButtonV2
<div style={{ width: 10 }} /> style={{ height: 40 }}
<ButtonV2 onClick={() => {}}
style={{ height: 40 }} text="Стоп"
onClick={() => {}} type={ButtonV2Type.empty}
text="Стоп" textColor={themeStore.theme.greenWhite}
type={ButtonV2Type.empty} />
textColor={themeStore.theme.greenWhite} <div style={{ width: 10 }} />
/> <div
<div style={{ width: 10 }} /> style={{
<div backgroundColor: store.isNeedSaveBtn ? themeStore.theme.greenWhite : undefined,
style={{ height: 40,
backgroundColor: store.isNeedSaveBtn ? themeStore.theme.greenWhite : undefined, textAlign: "center",
height: 40, alignContent: "center",
textAlign: "center", width: 40,
alignContent: "center", borderRadius: 100,
width: 40, }}
borderRadius: 100, >
}} {store.isNeedSaveBtn ? (
> <Icon style={{ height: 21 }} onClick={() => store.onClickSaveBehaviorTree()} type="Floppy" />
{store.isNeedSaveBtn ? ( ) : undefined}
<Icon style={{ height: 21 }} onClick={() => store.onClickSaveBehaviorTree()} type="Floppy" /> </div>
) : undefined} </div>
</div>
</div>
))
.with(StoreUIType.SelectBehaviorTree, () => <></>)
.otherwise(() => (
<></>
))}
</> </>
} }
style={{ position: "absolute", height: "100%", overflow: "hidden" }} style={{ position: "absolute", height: "100%", overflow: "hidden" }}
bgColor={themeStore.theme.black} bgColor={themeStore.theme.black}
children={ children={
<> <>
{match(store.type) <>
.with(StoreUIType.ViewBehaviorTree, () => ( <div
<> style={{
<div height: "100%",
style={{ width: 300,
height: "100%", zIndex: 10,
width: 300, position: "absolute",
zIndex: 10, display: "flex",
position: "absolute", flexDirection: "column",
display: "flex", justifyContent: "space-between",
flexDirection: "column", backgroundColor: themeStore.theme.surfaceContainerLow,
justifyContent: "space-between", }}
backgroundColor: themeStore.theme.surfaceContainerLow, >
}} <div style={{ margin: 20 }}>
> <div style={{ height: 58, alignContent: "center" }}>
<div style={{ margin: 20 }}> <CoreText
<div style={{ height: 58, alignContent: "center" }}> text="Поведение"
<CoreText type={CoreTextType.mediumV2}
text="Поведение" color={themeStore.theme.onSurfaceVariant}
type={CoreTextType.mediumV2} style={{ marginLeft: 20 }}
color={themeStore.theme.onSurfaceVariant} />
style={{ marginLeft: 20 }} <div style={{ width: "100%", height: 1, backgroundColor: themeStore.theme.outlineVariant }} />
/>
<div style={{ width: "100%", height: 1, backgroundColor: themeStore.theme.outlineVariant }} />
</div>
{store.skillTemplates ? <SkillTree skills={store.skillTree} dragEnd={store.dragEnd} /> : null}
</div>
</div>
<div
ref={ref}
className="dotted"
style={{
width: "100%",
height: "100%",
backgroundSize: "20px 20px",
}}
/>
</>
))
.with(StoreUIType.SelectBehaviorTree, () => (
<div style={{ height: "100%", overflowY: "auto", overflowX: "hidden" }}>
<div style={{ height: 20 }} />
<ButtonV2
icon={<Icon type={"Plus"} style={{ alignSelf: "center", marginLeft: 10, marginRight: 10 }} />}
text="СОЗДАТЬ ДЕРЕВО ПОВЕДЕНИЯ"
onClick={() => store.modalShow()}
/>
<div style={{ height: 50 }} />
<div style={{ display: "inline-grid", gridTemplateColumns: "1fr 1fr 1fr", width: "100%" }}>
{store.btTreeModels?.map((el, index) => (
<CoreCard
key={index}
clickDeleteIcon={() => store.deleteBt(el._id)}
clickGoToIcon={() => store.goToBt(el._id)}
descriptionMiddle={el.description}
descriptionTop={el.name}
date={el.unixTime}
/>
))}
</div> </div>
{store.skillTemplates ? <SkillTree skills={store.skillTree} dragEnd={store.dragEnd} /> : null}
</div> </div>
)) </div>
.otherwise(() => ( <div
<></> ref={ref}
))} className="dotted"
<CoreModal style={{
isOpen={store.isModalOpen} width: "100%",
onClose={() => store.modalCancel()} height: "100%",
children={ backgroundSize: "20px 20px",
<> }}
<div style={{ height: 30 }} /> />
<CoreText </>
text="Создание дерева поведения"
type={CoreTextType.header}
color={themeStore.theme.white}
style={{ textAlignLast: "center" }}
/>
<div style={{ height: 30 }} />
<div
style={{
height: 1,
width: "calc(100% - 30px)",
backgroundColor: themeStore.theme.outlineVariant,
}}
/>
<div style={{ height: 20 }} />
<InputV2 label={"Название"} onChange={(text) => store.updateForm({ name: text })} />
<div style={{ height: 20 }} />
<InputV2 label={"Описание"} onChange={(text) => store.updateForm({ description: text })} />
<div style={{ height: 20 }} />
<SelectV2
items={store.scenes?.map((el) => ({ name: el.name, value: el._id })) ?? []}
initialValue={""}
label={"Сцена"}
onChange={(value: string) => store.updateForm({ sceneId: value })}
/>
<div style={{ height: 30 }} />
<div
style={{
height: 1,
width: "calc(100% - 30px)",
backgroundColor: themeStore.theme.outlineVariant,
}}
/>
<div style={{ display: "flex", paddingTop: 20, justifyContent: "center" }}>
<ButtonV2 text="Сохранить" onClick={() => store.saveNewBt()} type={ButtonV2Type.default} />
<div style={{ width: 20 }} />
<ButtonV2 text="Отменить" onClick={() => store.modalCancel()} type={ButtonV2Type.default} />
</div>
</>
}
/>
<DrawerV2 <DrawerV2
title={store.titleDrawer} title={store.titleDrawer}
onClose={() => store.editDrawer(DrawerState.editThreadBehaviorTree, false)} onClose={() => store.editDrawer(DrawerState.editThreadBehaviorTree, false)}

View file

@ -24,7 +24,6 @@ import { UiBaseError } from "../../../core/model/ui_base_error";
import { BehaviorTreeBuilderPath, behaviorTreeBuilderStore } from "./behavior_tree_builder_screen"; import { BehaviorTreeBuilderPath, behaviorTreeBuilderStore } from "./behavior_tree_builder_screen";
import { BehaviorTreeModel } from "../model/behavior_tree_model"; import { BehaviorTreeModel } from "../model/behavior_tree_model";
import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model"; import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model";
import { SceneModel } from "../../scene_manager/model/scene_model";
import { SceneAsset } from "../../../core/model/scene_asset"; import { SceneAsset } from "../../../core/model/scene_asset";
import { themeStore } from "../../.."; import { themeStore } from "../../..";
@ -36,20 +35,12 @@ interface I2DArea {
} }
export enum DrawerState { export enum DrawerState {
newBehaviorTree = "Новое дерево поведения",
editThreadBehaviorTree = "Редактирование", editThreadBehaviorTree = "Редактирование",
} }
export enum StoreUIType {
SelectBehaviorTree,
ViewBehaviorTree,
}
export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeViewModel, CoreError> { export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeViewModel, CoreError> {
type: StoreUIType = StoreUIType.ViewBehaviorTree;
sceneAsset?: SceneAsset; sceneAsset?: SceneAsset;
viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty(); viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty();
scenes?: SceneModel[];
behaviorTreeModel: BehaviorTreeModel = BehaviorTreeModel.empty(); behaviorTreeModel: BehaviorTreeModel = BehaviorTreeModel.empty();
skillTemplates: Skills = Skills.empty(); skillTemplates: Skills = Skills.empty();
filledOutTemplates: Skills = Skills.empty(); filledOutTemplates: Skills = Skills.empty();
@ -68,7 +59,6 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
editor?: NodeEditor<Schemes>; editor?: NodeEditor<Schemes>;
areaPlugin?: AreaPlugin<Schemes, AreaExtra>; areaPlugin?: AreaPlugin<Schemes, AreaExtra>;
nodeUpdateObserver?: NodeRerenderObserver; nodeUpdateObserver?: NodeRerenderObserver;
isModalOpen: boolean = false;
primitiveViewModel: PrimitiveViewModel; primitiveViewModel: PrimitiveViewModel;
skillTree: ISkillView = { skillTree: ISkillView = {
name: "", name: "",
@ -144,7 +134,20 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
} }
} }
}; };
dispose(): void {
this.sceneAsset = undefined;
this.behaviorTreeModel = BehaviorTreeModel.empty();
this.skillTemplates = Skills.empty();
this.filledOutTemplates = Skills.empty();
this.area = undefined;
this.reteForceUpdateObserver = undefined;
this.btTreeModels = [];
this.nodeBehaviorTree = [];
this.editor = undefined;
this.areaPlugin = undefined;
this.nodeUpdateObserver = undefined;
}
async init(navigate: NavigateFunction): Promise<void> { async init(navigate: NavigateFunction): Promise<void> {
(await this.behaviorTreeBuilderHttpRepository.getActiveProjectId()) (await this.behaviorTreeBuilderHttpRepository.getActiveProjectId())
// eslint-disable-next-line array-callback-return // eslint-disable-next-line array-callback-return
@ -180,16 +183,13 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
await this.mapOk("sceneAsset", this.behaviorTreeBuilderHttpRepository.getSceneAsset(model.sceneId)); await this.mapOk("sceneAsset", this.behaviorTreeBuilderHttpRepository.getSceneAsset(model.sceneId));
this.isLoading = false; this.isLoading = false;
this.reteForceUpdateObserver?.emit(""); this.reteForceUpdateObserver?.emit("");
this.type = StoreUIType.ViewBehaviorTree;
}, },
async () => { async () => {
this.errors.push(new UiBaseError(`не найдено дерево с id:${id}`)); this.errors.push(new UiBaseError(`не найдено дерево с id:${id}`));
} }
); );
} else {
this.type = StoreUIType.SelectBehaviorTree;
await this.mapOk("scenes", this.behaviorTreeBuilderHttpRepository.getAllScenes());
} }
}; };
dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) { dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) {
@ -211,7 +211,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
) )
).fold( ).fold(
(xml) => { (xml) => {
console.log(xml) console.log(xml);
this.behaviorTreeModel.skills = this.filledOutTemplates; this.behaviorTreeModel.skills = this.filledOutTemplates;
this.behaviorTreeModel.scene = NodeBehaviorTree.fromReteScene( this.behaviorTreeModel.scene = NodeBehaviorTree.fromReteScene(
this.editor as NodeEditor<Schemes>, this.editor as NodeEditor<Schemes>,
@ -356,29 +356,4 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
return this.filledOutTemplates.getCssAtLabel(label); return this.filledOutTemplates.getCssAtLabel(label);
} }
changeSceneViewModel = (text: string) => {}; changeSceneViewModel = (text: string) => {};
modalShow = () => {
this.isModalOpen = true;
};
modalCancel = () => {
this.isModalOpen = false;
};
deleteBt = async (id: string) => (
await this.behaviorTreeBuilderHttpRepository.deleteBt(id),
await this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances())
);
saveNewBt = async () =>
(await this.viewModel.valid<BehaviorTreeViewModel>()).fold(
async (model) => {
await this.messageHttp(this.behaviorTreeBuilderHttpRepository.saveNewBt(model), {
successMessage: "Новое дерево создано",
});
await this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances());
this.modalCancel();
},
async (error) => message.error(error)
);
goToBt = (id: string) => {
if (this.navigate) this.navigate(BehaviorTreeBuilderPath(id));
};
} }

View file

@ -0,0 +1,56 @@
import React, { useEffect } from "react";
import { observer } from "mobx-react-lite";
import { FormBuilderStore } from "./form_builder_store";
import { IPropsForm } from "../forms";
import { useStore } from "../../../../../../core/helper/use_store";
import { isBtScreen } from "../../../behavior_tree_builder_screen";
import { CoreInput } from "../../../../../../core/ui/input/input";
import { CoreButton } from "../../../../../../core/ui/button/button";
import { Modal, message } from "antd";
import { FormBuilder } from "../../../../../../core/ui/form_builder/form_builder";
import { FormBuilderValidationModel } from "../../../../../../core/model/form_builder_validation_model";
export const FormBuilderForm = observer((props: IPropsForm<Partial<FormBuilderValidationModel>>) => {
const store = useStore(FormBuilderStore);
useEffect(() => {
store.initParam(isBtScreen(), props.store);
}, []);
return (
<div>
<div>FormBuilder</div>
{store.isBtScreen ? (
<>
<CoreInput label="Result" onChange={(text) => store.updateForm({ context: text })} />
<CoreInput label="Context" onChange={(text) => store.updateForm({ result: text })} />
<div style={{ width: 100 }}>
<CoreButton text="preview" onClick={() => store.openModal()} />
<CoreButton
text="save"
onClick={async () =>
(await store.viewModel.valid<FormBuilderValidationModel>()).fold(
(model) => props.onChange(model),
(error) => message.error(error)
)
}
/>
</div>
<Modal
destroyOnClose={true}
open={store.isModalOpen}
footer={null}
closable={false}
closeIcon={null}
onCancel={() => {
store.isModalOpen = false;
}}
>
<FormBuilder formBuilder={store.viewModel} onChange={() => {}} />
</Modal>
</>
) : (
<></>
)}
</div>
);
});

View file

@ -0,0 +1,23 @@
import makeAutoObservable from "mobx-store-inheritance";
import { NavigateFunction } from "react-router-dom";
import { FormState, CoreError } from "../../../../../../core/store/base_store";
import { BehaviorTreeBuilderStore } from "../../../behavior_tree_builder_store";
import { FormBuilderValidationModel } from "../../../../../../core/model/form_builder_validation_model";
export class FormBuilderStore extends FormState<FormBuilderValidationModel, CoreError> {
openModal() {
this.isModalOpen = true;
}
isBtScreen = false;
isModalOpen = false;
viewModel: FormBuilderValidationModel = FormBuilderValidationModel.empty();
constructor() {
super();
makeAutoObservable(this);
}
errorHandingStrategy = (error: CoreError) => {};
init = async (navigate?: NavigateFunction | undefined) => {};
initParam = (isSkillScreen: boolean, store: BehaviorTreeBuilderStore | undefined) => {
this.isBtScreen = true;
};
}

View file

@ -1,11 +1,9 @@
import { FormBuilderValidationModel } from "../../../../../core/model/form_builder_validation_model";
import { DependencyViewModel } from "../../../../../core/model/skill_model"; import { DependencyViewModel } from "../../../../../core/model/skill_model";
import { BehaviorTreeBuilderStore } from "../../behavior_tree_builder_store"; import { BehaviorTreeBuilderStore } from "../../behavior_tree_builder_store";
import { CameraDeviceForm } from "./camera_device_form/camera_device_form_form"; import { FormBuilderForm } from "./form_builder/form_builder_form";
import { MoveToPose } from "./move_to_pose/move_to_pose_form";
import { RobotDeviceForm } from "./robot_device_form/robot_device_form_form";
import { TopicDependencyViewModel } from "./topics_form/topic_dependency_view_model"; import { TopicDependencyViewModel } from "./topics_form/topic_dependency_view_model";
import { TopicsForm } from "./topics_form/topics_form"; import { TopicsForm } from "./topics_form/topics_form";
import { WeightsForm } from "./weights_form/weights_form";
export interface IPropsForm<T> { export interface IPropsForm<T> {
dependency: T; dependency: T;
@ -22,6 +20,7 @@ export enum Form {
robotName = "robot_name", robotName = "robot_name",
cameraDeviceForm = "camera", cameraDeviceForm = "camera",
topic = "topic", topic = "topic",
formBuilder = "formBuilder",
moveToPose = "move_to_pose", moveToPose = "move_to_pose",
} }
export interface BtDependencyFormBuilder { export interface BtDependencyFormBuilder {
@ -35,15 +34,18 @@ export const btDependencyFormBuilder = (onChange: (dependency: DependencyViewMod
form: Form.topic, form: Form.topic,
component: <TopicsForm store={undefined} dependency={TopicDependencyViewModel.empty()} onChange={onChange} />, component: <TopicsForm store={undefined} dependency={TopicDependencyViewModel.empty()} onChange={onChange} />,
}, },
{
form: Form.formBuilder,
component: (
<FormBuilderForm store={undefined} dependency={FormBuilderValidationModel.empty()} onChange={onChange} />
),
},
]; ];
export const forms = ( export const forms = (
props: any, props: any,
onChange: (dependency: DependencyViewModel) => void, onChange: (dependency: DependencyViewModel) => void,
store: BehaviorTreeBuilderStore store: BehaviorTreeBuilderStore
): IForms[] => [ ): IForms[] => [
// { name: Form.weights, component: <WeightsForm dependency={props} onChange={onChange} /> },
// { name: Form.robotName, component: <RobotDeviceForm store={store} dependency={props} onChange={onChange} /> },
// { name: Form.cameraDeviceForm, component: <CameraDeviceForm store={store} dependency={props} onChange={onChange} /> },
{ name: Form.topic, component: <TopicsForm store={store} dependency={props} onChange={onChange} /> }, { name: Form.topic, component: <TopicsForm store={store} dependency={props} onChange={onChange} /> },
// { name: Form.moveToPose, component: <MoveToPose store={store} dependency={props} onChange={onChange} /> }, { name: Form.formBuilder, component: <FormBuilderForm store={store} dependency={props} onChange={onChange} /> },
]; ];

View file

@ -22,7 +22,7 @@ export class TopicDependencyViewModel extends ValidationModel implements Depende
toView = (store: BehaviorTreeBuilderStore | undefined): React.ReactNode => { toView = (store: BehaviorTreeBuilderStore | undefined): React.ReactNode => {
if (store && this.sid) { if (store && this.sid) {
if (store.filledOutTemplates.topicsStack.some((el) => el.sid?.isEqual(this.sid ?? ""))) { if (store.filledOutTemplates.topicsStack.some((el) => el.sid?.isEqual(this.sid ?? ""))) {
return <CoreText type={CoreTextType.header} text={this.topicOut} color="red" />; return <div style={{ color: "white" }}>{this.topicOut}</div>;
} else { } else {
return <CoreText type={CoreTextType.header} text="Error" color="red" />; return <CoreText type={CoreTextType.header} text="Error" color="red" />;
} }

View file

@ -33,7 +33,7 @@ export const TopicsForm = observer((props: IPropsForm<Partial<TopicDependencyVie
.with(StoreTopicType.btExecute, () => ( .with(StoreTopicType.btExecute, () => (
<> <>
<CoreText <CoreText
text={"Выберите тип топика"} text={"Выберите топик"}
type={CoreTextType.header} type={CoreTextType.header}
style={{ padding: 10 }} style={{ padding: 10 }}
color={themeStore.theme.darkOnSurfaceVariant} color={themeStore.theme.darkOnSurfaceVariant}

View file

@ -0,0 +1,10 @@
import { CoreHttpRepository, HttpMethod } from "../../core/repository/core_http_repository";
import { BehaviorTreeModel } from "../behavior_tree_builder/model/behavior_tree_model";
import { BehaviorTreeViewModel } from "../behavior_tree_builder/model/behavior_tree_view_model";
export class BehaviorTreeManagerHttpRepository extends CoreHttpRepository {
featureApi = `/behavior/trees`;
deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`);
saveNewBt = async (model: BehaviorTreeViewModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model);
getAllBtInstances = async () => this._jsonRequest<BehaviorTreeModel[]>(HttpMethod.GET, this.featureApi);
}

View file

@ -0,0 +1,100 @@
import { observer } from "mobx-react-lite";
import { BehaviorTreeManagerStore } from "./behavior_tree_manager_store";
import React from "react";
import { useStore } from "../../core/helper/use_store";
import { ButtonV2, ButtonV2Type } from "../../core/ui/button/button_v2";
import { CoreCard } from "../../core/ui/card/card";
import { Icon } from "../../core/ui/icons/icons";
import { themeStore } from "../..";
import { InputV2 } from "../../core/ui/input/input_v2";
import { CoreModal } from "../../core/ui/modal/modal";
import { SelectV2 } from "../../core/ui/select/select_v2";
import { CoreText, CoreTextType } from "../../core/ui/text/text";
import { MainPageV2 } from "../../core/ui/pages/main_page_v2";
export const BehaviorTreeManagerScreenPath = "/behavior/tree/manager";
export const BehaviorTreeManagerScreen = observer(() => {
const store = useStore(BehaviorTreeManagerStore);
return (
<>
<MainPageV2
children={
<>
<div style={{ height: "100%", overflowY: "auto", overflowX: "hidden" }}>
<div style={{ height: 20 }} />
<ButtonV2
icon={<Icon type={"Plus"} style={{ alignSelf: "center", marginLeft: 10, marginRight: 10 }} />}
text="СОЗДАТЬ ДЕРЕВО ПОВЕДЕНИЯ"
onClick={() => store.modalShow()}
/>
<div style={{ height: 50 }} />
<div style={{ display: "inline-grid", gridTemplateColumns: "1fr 1fr 1fr", width: "100%" }}>
{store.btTreeModels?.map((el, index) => (
<CoreCard
key={index}
clickDeleteIcon={() => store.deleteBt(el._id)}
clickGoToIcon={() => store.goToBt(el._id)}
descriptionMiddle={el.description}
descriptionTop={el.name}
date={el.unixTime}
/>
))}
</div>
</div>
<CoreModal
isOpen={store.isModalOpen}
onClose={() => store.modalCancel()}
children={
<>
<div style={{ height: 30 }} />
<CoreText
text="Создание дерева поведения"
type={CoreTextType.header}
color={themeStore.theme.white}
style={{ textAlignLast: "center" }}
/>
<div style={{ height: 30 }} />
<div
style={{
height: 1,
width: "calc(100% - 30px)",
backgroundColor: themeStore.theme.outlineVariant,
}}
/>
<div style={{ height: 20 }} />
<InputV2 trim={true} label={"Название"} onChange={(text) => store.updateForm({ name: text })} />
<div style={{ height: 20 }} />
<InputV2 trim={true} label={"Описание"} onChange={(text) => store.updateForm({ description: text })} />
<div style={{ height: 20 }} />
<SelectV2
items={store.scenes?.map((el) => ({ name: el.name, value: el._id })) ?? []}
initialValue={""}
label={"Сцена"}
onChange={(value: string) => store.updateForm({ sceneId: value })}
/>
<div style={{ height: 30 }} />
<div
style={{
height: 1,
width: "calc(100% - 30px)",
backgroundColor: themeStore.theme.outlineVariant,
}}
/>
<div style={{ display: "flex", paddingTop: 20, justifyContent: "center" }}>
<ButtonV2 text="Сохранить" onClick={() => store.saveNewBt()} type={ButtonV2Type.default} />
<div style={{ width: 20 }} />
<ButtonV2 text="Отменить" onClick={() => store.modalCancel()} type={ButtonV2Type.default} />
</div>
</>
}
/>
</>
}
bgColor={themeStore.theme.black}
/>
</>
);
});

View file

@ -0,0 +1,60 @@
import makeAutoObservable from "mobx-store-inheritance";
import { UiDrawerFormState, CoreError } from "../../core/store/base_store";
import { BehaviorTreeViewModel } from "../behavior_tree_builder/model/behavior_tree_view_model";
import { NavigateFunction } from "react-router-dom";
import { BehaviorTreeBuilderPath } from "../behavior_tree_builder/presentation/behavior_tree_builder_screen";
import { BehaviorTreeModel } from "../behavior_tree_builder/model/behavior_tree_model";
import { SceneModel } from "../scene_manager/model/scene_model";
import { BehaviorTreeManagerHttpRepository } from "./behavior_tree_manager_repository";
import { message } from "antd";
export enum DrawerState {
newBehaviorTree = "Новое дерево поведения",
}
export class BehaviorTreeManagerStore extends UiDrawerFormState<BehaviorTreeViewModel, CoreError> {
viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty();
navigate?: NavigateFunction;
btTreeModels: BehaviorTreeModel[] = [];
scenes?: SceneModel[];
behaviorTreeManagerHttpRepository = new BehaviorTreeManagerHttpRepository();
isModalOpen: boolean = false;
activeProject?: string;
constructor() {
super(DrawerState);
makeAutoObservable(this);
}
init = async (navigate?: NavigateFunction | undefined): Promise<void> => {
this.navigate = navigate;
(await this.behaviorTreeManagerHttpRepository.getActiveProjectId()).map((el) => {
this.activeProject = el.id;
this.viewModel.project = this.activeProject;
});
await this.mapOk("btTreeModels", this.behaviorTreeManagerHttpRepository.getAllBtInstances());
await this.mapOk("scenes", this.behaviorTreeManagerHttpRepository.getAllScenes());
};
deleteBt = async (id: string) => (
await this.behaviorTreeManagerHttpRepository.deleteBt(id),
await this.mapOk("btTreeModels", this.behaviorTreeManagerHttpRepository.getAllBtInstances())
);
saveNewBt = async () =>
(await this.viewModel.valid<BehaviorTreeViewModel>()).fold(
async (model) => {
await this.messageHttp(this.behaviorTreeManagerHttpRepository.saveNewBt(model), {
successMessage: "Новое дерево создано",
});
await this.mapOk("btTreeModels", this.behaviorTreeManagerHttpRepository.getAllBtInstances());
},
async (error) => message.error(error)
);
goToBt = (id: string) => {
console.log(this.navigate);
if (this.navigate) this.navigate(BehaviorTreeBuilderPath(id));
};
modalShow = () => {
this.isModalOpen = true;
};
modalCancel = () => {
this.isModalOpen = false;
};
}

View file

@ -0,0 +1,11 @@
import { Result } from "../../core/helper/result";
export class NumberTriviaModel {
constructor() {}
isValid(): Result<string, void> {
return Result.ok();
}
static empty() {
return new NumberTriviaModel();
}
}

View file

@ -2,7 +2,7 @@ import { TypedEvent } from "../../../core/helper/typed_event";
import { SocketRepository, socketRepository } from "../../../core/repository/core_socket_repository"; import { SocketRepository, socketRepository } from "../../../core/repository/core_socket_repository";
import { ProcessStatus } from "../../dataset/dataset_model"; import { ProcessStatus } from "../../dataset/dataset_model";
export interface ProcessUpdate { export interface ProcessUpdate {
id: string; id: string;
status: ProcessStatus; status: ProcessStatus;
} }

View file

@ -1,6 +1,6 @@
import { FormBuilderValidationModel } from "../../dataset/dataset_model"; import { IsNotEmpty, IsString } from "class-validator";
import { IsNotEmpty, IsString } from "class-validator";
import { ValidationModel } from "../../../core/model/validation_model"; import { ValidationModel } from "../../../core/model/validation_model";
import { FormBuilderValidationModel } from "../../../core/model/form_builder_validation_model";
export enum ModelMachineLearningTypes { export enum ModelMachineLearningTypes {
OBJECT_DETECTION = "OBJECT_DETECTION", OBJECT_DETECTION = "OBJECT_DETECTION",

View file

@ -13,8 +13,8 @@ import { FormBuilder } from "../../../core/ui/form_builder/form_builder";
import { match } from "ts-pattern"; import { match } from "ts-pattern";
import { TemplateModelCard } from "./ui/template_model_card"; import { TemplateModelCard } from "./ui/template_model_card";
import { Icon } from "../../../core/ui/icons/icons"; import { Icon } from "../../../core/ui/icons/icons";
import { FormBuilderValidationModel } from "../../dataset/dataset_model"; import { useStore } from "../../../core/helper/use_store";
import { useStore } from "../../../core/helper/use_store"; import { FormBuilderValidationModel } from "../../../core/model/form_builder_validation_model";
interface IItem { interface IItem {
name: string; name: string;

View file

@ -1,5 +1,6 @@
import { Result } from "../../core/helper/result"; import { Result } from "../../core/helper/result";
import makeAutoObservable from "mobx-store-inheritance"; import makeAutoObservable from "mobx-store-inheritance";
import { FormBuilderValidationModel } from "../../core/model/form_builder_validation_model";
export enum ProcessStatus { export enum ProcessStatus {
END = "END", END = "END",
@ -37,52 +38,7 @@ export interface Asset {
image: string; image: string;
} }
export class FormBuilderValidationModel {
public result: string;
public context: string;
public form: string[];
public output: any;
constructor(context: string, result: string, form: string[], output: string) {
this.context = context;
this.result = result;
this.form = form;
this.output = output;
}
static isEmpty = (formBuilderValidationModel: FormBuilderValidationModel) =>
formBuilderValidationModel.context.isEmpty() &&
formBuilderValidationModel.result.isEmpty() &&
formBuilderValidationModel.form.isEmpty();
static datasetEmpty() {
return new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], defaultFormValue);
}
static empty() {
return new FormBuilderValidationModel("", "", [], "");
}
static emptyTest() {
return new FormBuilderValidationModel(``, ``, [], defaultFormValue);
}
static creteDataSetTest() {
return new FormBuilderValidationModel(``, scene, [], "");
}
static vision(): FormBuilderValidationModel {
return new FormBuilderValidationModel(
`ENUM PRETRAIN = "true","false";`,
`{
"numberOfEpochs": \${numberOfEpochs:number:10},
"selectDataset": \${<SelectDataset/>:OBJECT:{"dataset": {}},
"pretrain": \${pretrain:Enum<PRETRAIN>:true}
}`,
[],
""
);
}
}
export const scene = `{
"center_shell": [\${CENTER_SHELL_1:number:0}, \${CENTER_SHELL_2:number:0}, \${CENTER_SHELL_3:number:0}],
"scene":\${<SelectScene/>:OBJECT:{"details": []}
}`;
export class DataSetModel { export class DataSetModel {
dataSetObjects: string[]; dataSetObjects: string[];
datasetType: string; datasetType: string;
@ -209,5 +165,3 @@ export const defaultFormValue: any = {
camera_position: { center_shell: [0, 0, 0], radius_range: [1, 1.4], elevation_range: [10, 90] }, camera_position: { center_shell: [0, 0, 0], radius_range: [1, 1.4], elevation_range: [10, 90] },
generation: { n_cam_pose: 5, n_sample_on_pose: 3, n_series: 100, image_format: "JPEG", image_size_wh: [640, 480] }, generation: { n_cam_pose: 5, n_sample_on_pose: 3, n_series: 100, image_format: "JPEG", image_size_wh: [640, 480] },
}; };

View file

@ -1,8 +1,8 @@
import { ValidationModel } from "../../core/model/validation_model"; import { ValidationModel } from "../../core/model/validation_model";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsEnum, IsNotEmpty, IsString } from "class-validator"; import { IsEnum, IsNotEmpty, IsString } from "class-validator";
import { FormBuilderValidationModel } from "../dataset/dataset_model"; import { FormBuilderValidationModel } from "../../core/model/form_builder_validation_model";
export enum DigitalTwinsTypes { export enum DigitalTwinsTypes {
CAMERA = "CAMERA", CAMERA = "CAMERA",
ROBOT = "ROBOT", ROBOT = "ROBOT",

View file

@ -1,4 +1,3 @@
import React from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { DrawersSkills, SkillsStore } from "./skills_store"; import { DrawersSkills, SkillsStore } from "./skills_store";
import { Drawer, Modal } from "antd"; import { Drawer, Modal } from "antd";
@ -73,44 +72,58 @@ export const SkillsScreen = observer(() => {
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}> <div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<div> <div>
<CoreInput <CoreInput
trim={true}
label={"name"} label={"name"}
onChange={(text) => onChange={(text) =>
store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { name: text }) }) store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { name: text }) })
} }
/> />
<CoreInput <CoreInput
trim={true}
label={"format"} label={"format"}
onChange={(text) => onChange={(text) =>
store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { format: text }) }) store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { format: text }) })
} }
/> />
<CoreInput <CoreInput
trim={true}
label="version" label="version"
onChange={(text) => onChange={(text) =>
store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { version: text }) }) store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { version: text }) })
} }
/> />
<CoreInput <CoreInput
trim={true}
label={"Module description"} label={"Module description"}
onChange={(text) => onChange={(text) =>
store.updateForm({ Module: Object.assign(store.viewModel.Module, { description: text }) }) store.updateForm({ Module: Object.assign(store.viewModel.Module, { description: text }) })
} }
/> />
<CoreInput <CoreInput
trim={true}
label={"Module name"} label={"Module name"}
onChange={(text) => store.updateForm({ Module: Object.assign(store.viewModel.Module, { name: text }) })} onChange={(text) => store.updateForm({ Module: Object.assign(store.viewModel.Module, { name: text }) })}
/> />
<CoreInput <CoreInput
trim={true}
label={"Module node name"} label={"Module node name"}
onChange={(text) => store.updateForm({ Module: Object.assign(store.viewModel.Module, { node_name: text }) })} onChange={(text) =>
store.updateForm({ Module: Object.assign(store.viewModel.Module, { node_name: text }) })
}
/> />
<CoreInput <CoreInput
trim={true}
label="Launch package" label="Launch package"
onChange={(text) => store.updateForm({ Launch: Object.assign(store.viewModel.Launch, { package: text }) })} onChange={(text) =>
store.updateForm({ Launch: Object.assign(store.viewModel.Launch, { package: text }) })
}
/> />
<CoreInput <CoreInput
trim={true}
label="Launch executable" label="Launch executable"
onChange={(text) => store.updateForm({ Launch: Object.assign(store.viewModel.Launch, { executable: text }) })} onChange={(text) =>
store.updateForm({ Launch: Object.assign(store.viewModel.Launch, { executable: text }) })
}
/> />
<CoreText <CoreText
@ -122,6 +135,7 @@ export const SkillsScreen = observer(() => {
{store.viewModel.topicsOut.map((el, index) => ( {store.viewModel.topicsOut.map((el, index) => (
<div key={index} style={{ marginTop: 10 }}> <div key={index} style={{ marginTop: 10 }}>
<CoreInput <CoreInput
trim={true}
value={el.name} value={el.name}
label={"name"} label={"name"}
onChange={(text) => onChange={(text) =>
@ -129,6 +143,7 @@ export const SkillsScreen = observer(() => {
} }
/> />
<CoreInput <CoreInput
trim={true}
value={el.type} value={el.type}
label={"type"} label={"type"}
onChange={(text) => onChange={(text) =>
@ -146,6 +161,7 @@ export const SkillsScreen = observer(() => {
{store.viewModel.BTAction.map((el, index) => ( {store.viewModel.BTAction.map((el, index) => (
<div key={index} style={{ marginTop: 10 }}> <div key={index} style={{ marginTop: 10 }}>
<CoreInput <CoreInput
trim={true}
value={el.name} value={el.name}
label={"name"} label={"name"}
onChange={(text) => onChange={(text) =>
@ -153,20 +169,14 @@ export const SkillsScreen = observer(() => {
} }
/> />
<CoreInput <CoreInput
trim={true}
value={el.type} value={el.type}
label={"type"} label={"type"}
onChange={(text) => onChange={(text) =>
store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ type: text }, index) }) store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ type: text }, index) })
} }
/> />
<CoreInput
value={el.format}
label={"format"}
onChange={(text) =>
store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ format: text }, index) })
}
/>
<CoreSelect <CoreSelect
items={Object.keys(BtAction)} items={Object.keys(BtAction)}
value={el.typeAction} value={el.typeAction}
@ -214,3 +224,4 @@ export const SkillsScreen = observer(() => {
</> </>
); );
}); });