diff --git a/server/src/features/skills/model/skills_database_model.ts b/server/src/features/skills/model/skills_database_model.ts index 19cdaf9..b4ae4d2 100644 --- a/server/src/features/skills/model/skills_database_model.ts +++ b/server/src/features/skills/model/skills_database_model.ts @@ -18,6 +18,12 @@ export const SkillsSchema = new Schema({ param: { type: Schema.Types.Mixed, }, + bgColor: { + type:Schema.Types.String, + }, + borderColor:{ + type:Schema.Types.String + } }).plugin(require("mongoose-autopopulate")); export const skillsSchema = "skills"; diff --git a/ui/public/index.html b/ui/public/index.html index c7814ac..750de6c 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -143,14 +143,14 @@ } .dotted { - background-image: -webkit-repeating-radial-gradient( + background-image: -webkit-repeating-radial-gradient( center center, rgba(147, 147, 147, 30%), rgba(147, 147, 147, 30%) 2px, transparent 2px, transparent 100% ); - + background-image: -moz-repeating-radial-gradient( center center, rgba(147, 147, 147, 30%), @@ -179,5 +179,9 @@ width: 100%; height: 100%; } + ::-webkit-scrollbar-thumb { + background-color: #d6dee1; + border-radius: 20px; + } diff --git a/ui/src/core/extensions/array.ts b/ui/src/core/extensions/array.ts index cfb32fd..b64cd54 100644 --- a/ui/src/core/extensions/array.ts +++ b/ui/src/core/extensions/array.ts @@ -104,4 +104,9 @@ export const ArrayExtensions = () => { return this; }; } + if ([].updateAll === undefined) { + Array.prototype.updateAll = function (element) { + return this.map((el) => Object.assign(el, element)); + }; + } }; diff --git a/ui/src/core/extensions/extensions.ts b/ui/src/core/extensions/extensions.ts index b37b0c0..f8ea695 100644 --- a/ui/src/core/extensions/extensions.ts +++ b/ui/src/core/extensions/extensions.ts @@ -28,6 +28,7 @@ declare global { indexOfR(element: T): Result>; replacePropIndex(property: Partial, index: number): T[]; someR(predicate: (value: T) => boolean): Result>; + updateAll(value: Partial): Array; } interface Date { formatDate(): string; diff --git a/ui/src/core/helper/use_store.tsx b/ui/src/core/helper/use_store.tsx new file mode 100644 index 0000000..5afe24d --- /dev/null +++ b/ui/src/core/helper/use_store.tsx @@ -0,0 +1,18 @@ +import { ClassConstructor } from "class-transformer"; +import React from "react"; + +interface LifeCycleStore { + init?: () => void; + dispose?: () => void; +} + +export const useStore = (storeConstructor: ClassConstructor) => { + const [store] = React.useState(new storeConstructor()); + React.useEffect(() => { + store?.init?.(); + return () => { + store?.dispose?.(); + }; + }, []); + return store; +}; diff --git a/ui/src/core/model/skill_model.ts b/ui/src/core/model/skill_model.ts index 5ed4485..31587b6 100644 --- a/ui/src/core/model/skill_model.ts +++ b/ui/src/core/model/skill_model.ts @@ -1,12 +1,22 @@ -import { IsArray, IsEnum, IsNotEmpty, IsOptional, IsString, ValidateNested } from "class-validator"; +import { + IsArray, + IsEnum, + IsNotEmpty, + IsObject, + IsOptional, + IsString, + ValidateNested, + isMongoId, +} from "class-validator"; import { Type } from "class-transformer"; import { ISkillView } from "../../features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree"; import { v4 } from "uuid"; import { Result } from "../helper/result"; import { ValidationModel } from "./validation_model"; -import { TopicViewModel } from "../../features/topics/topic_view_model"; +import { ITopicModel, TopicViewModel } from "../../features/topics/topic_view_model"; import makeAutoObservable from "mobx-store-inheritance"; import clone from "just-clone"; +import { BehaviorTreeBuilderStore } from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_store"; export interface IDependency { skills: ISkillDependency[]; @@ -34,19 +44,27 @@ export interface IWeightsDependency { export interface IDeviceDependency { sid: string; } - +export interface IDependency {} export interface IParam { + isFilled: boolean; type: string; - dependency?: Object; + dependency?: DependencyViewModel; +} +export class DependencyViewModel { + static empty = () => new DependencyViewModel(); + toView = (store: BehaviorTreeBuilderStore | undefined): React.ReactNode => "string"; } export class ParamViewModel implements IParam { type: string; - dependency: Object; - constructor(type: string, dependency: Object) { + + @Type(() => DependencyViewModel) + dependency: DependencyViewModel; + isFilled: boolean = false; + constructor(type: string, dependency: DependencyViewModel) { this.type = type; this.dependency = dependency; } - static empty = () => new ParamViewModel("", {}); + static empty = () => new ParamViewModel("", DependencyViewModel.empty()); } export interface IBTAction { name: string; @@ -69,6 +87,7 @@ export class BtActionViewModel extends ValidationModel implements IBTAction { @IsNotEmpty() @IsString() type: string; + @Type(() => ParamViewModel) param: IParam[]; @IsNotEmpty() @IsString() @@ -144,6 +163,11 @@ export class Module implements IModule { name: string; @IsString() description: string; + constructor(name: string, description: string) { + this.name = name; + this.description = description; + } + static empty = () => new Module("", ""); } export class BTAction implements IBTAction { @IsString() @@ -186,6 +210,9 @@ export class Setting implements ISetting { } export class SkillModel extends ValidationModel implements ISkill { + @IsOptional() + @IsString() + _id?: string; constructor() { super(); makeAutoObservable(this); @@ -202,12 +229,14 @@ export class SkillModel extends ValidationModel implements ISkill { @ValidateNested() @Type(() => Module) Module: IModule; + @Type(() => BtActionViewModel) BTAction: BtActionViewModel[]; topicsOut: TopicViewModel[] = []; static empty() { const skillModel = new SkillModel(); skillModel.BTAction = []; skillModel.SkillPackage = SkillPackage.empty(); + skillModel.Module = Module.empty(); return skillModel; } public static isEmpty(skill: SkillModel): Result { @@ -223,6 +252,7 @@ export class SkillModel extends ValidationModel implements ISkill { result.sid = sid; return result; }; + emptyParam = () => this.BTAction.at(0)?.param.at(0)?.dependency === undefined; } export class SkillDependency implements IDependency { @@ -239,6 +269,13 @@ export class SkillDependency implements IDependency { } export class Skills { + constructor() { + makeAutoObservable(this); + } + @IsOptional() + @IsArray() + @Type(() => TopicViewModel) + topicsStack: ITopicModel[] = []; @IsArray() @Type(() => SkillModel) skills: SkillModel[]; @@ -247,6 +284,16 @@ export class Skills { skills.skills = skilsModel; return skills; }; + deleteTopic = (sid: string) => (this.topicsStack = this.topicsStack.filter((el) => !el.sid?.isEqual(sid))); + + getSkillAtSid = (sid: string): Result => { + const result = this.skills.filter((skill) => skill.sid?.isEqual(sid)).at(0); + if (result === undefined) { + return Result.error(undefined); + } + return Result.ok(result); + }; + validation = (): Result => { const errors: string[] = []; this.skills.forEach((skill) => { @@ -330,8 +377,9 @@ export class Skills { getSkillDo = (name: string) => this.skills.reduce((acc, el) => { if (el.BTAction.find((el) => el.name.isEqual(name))) { + acc = el.Module.name; - } + } return acc; }, "error"); @@ -357,14 +405,14 @@ export class Skills { .flat(1) .filter((el) => el !== ""); - getDependencyBySkillLabelAndType = (skillType: string, sid: string) => + getDependencyBySkillLabelAndType = (skillType: string, sid: string): DependencyViewModel => this.skills - .reduce((acc, skill) => { + .reduce((acc, skill) => { if (skill.sid?.isEqual(sid)) { skill.BTAction.map((action) => { action.param.map((param) => { if (param.type.isEqualR(skillType)) { - acc.push(param?.dependency ?? {}); + acc.push(param?.dependency ?? DependencyViewModel.empty()); } return param; }); @@ -374,7 +422,7 @@ export class Skills { return acc; }, []) - .at(0) as T; + .at(0) ?? DependencyViewModel.empty(); static isEmpty(model: Skills): Result { if (model.skills.isEmpty()) { return Result.error(undefined); @@ -392,13 +440,10 @@ export class Skills { if (skill.sid?.isEqual(sid)) { skill.BTAction.forEach((action) => { action.param.forEach((param) => { - if (param.type.isEqual(skillType)) { - acc = Object.keys(param?.dependency ?? {}).isNotEmpty(); - } + acc = param.isFilled; }); }); } - return acc; }, false); @@ -406,7 +451,6 @@ export class Skills { this.skills.reduce((acc, skill) => { skill.BTAction.forEach((action) => action.param.forEach((param) => { - // acc.incrementValue(param.sid ?? "empty"); return param; }) ); @@ -434,4 +478,11 @@ export class Skills { } return acc; }, 0); + skillIsDependencyFilled = (label: string): Result => + this.getSkill(label).fold( + (skill) => { + return Result.ok(skill.BTAction.find((el) => el.name.isEqual(label))?.param.isEmpty()); + }, + () => Result.error(false) + ); } diff --git a/ui/src/core/ui/button/button_v2.tsx b/ui/src/core/ui/button/button_v2.tsx index 80b72d9..91fc1d8 100644 --- a/ui/src/core/ui/button/button_v2.tsx +++ b/ui/src/core/ui/button/button_v2.tsx @@ -3,6 +3,7 @@ import { themeStore } from "../../.."; import { CoreText, CoreTextType } from "../text/text"; export enum ButtonV2Type { default = "default", + empty = "empty", } export const ButtonV2 = ({ text, @@ -10,7 +11,7 @@ export const ButtonV2 = ({ onClick, style, icon, - isFilled, + type, }: { text?: string; @@ -18,27 +19,36 @@ export const ButtonV2 = ({ onClick: () => void; style?: React.CSSProperties; icon?: React.ReactNode; - isFilled?: boolean; type?: ButtonV2Type; }) => { return (
15) - .otherwise(() => 5), - borderRadius: match(type) - .with(ButtonV2Type.default, () => 5) - .otherwise(() => 100), - display: "flex", - paddingRight: 10, - paddingLeft: 10, - }} + style={Object.assign( + { + cursor: "pointer", + backgroundColor: match(type) + .with(ButtonV2Type.empty, () => undefined) + .with(ButtonV2Type.default, () => themeStore.theme.greenWhite) + .otherwise(() => themeStore.theme.greenWhite), + color: textColor ?? themeStore.theme.black, + width: "max-content", + height: "max-content", + padding: match(type) + .with(ButtonV2Type.default, () => 15) + .otherwise(() => 5), + border: match(type) + .with(ButtonV2Type.empty, () => `1px solid ${themeStore.theme.greenWhite}`) + .with(ButtonV2Type.default, () => undefined) + .otherwise(() => undefined), + borderRadius: match(type) + .with(ButtonV2Type.default, () => 5) + .otherwise(() => 100), + display: "flex", + paddingRight: 10, + paddingLeft: 10, + }, + style + )} onClick={() => onClick()} > {icon} diff --git a/ui/src/core/ui/drawer/drawer.tsx b/ui/src/core/ui/drawer/drawer.tsx new file mode 100644 index 0000000..d825d59 --- /dev/null +++ b/ui/src/core/ui/drawer/drawer.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { themeStore } from "../../.."; +import { CoreText, CoreTextType } from "../text/text"; + +export const DrawerV2: React.FC<{ + isOpen?: boolean; + title?: string; + onClose: () => void; + children: React.ReactNode; +}> = ({ isOpen, onClose, children, title }) => { + return ( +
+
+
+
+ + +
+
+
+ {children} +
+
+ ); +}; diff --git a/ui/src/core/ui/form_builder/forms/select_detail/model/select_details_model.ts b/ui/src/core/ui/form_builder/forms/select_detail/model/select_details_model.ts index 791008d..92fb941 100644 --- a/ui/src/core/ui/form_builder/forms/select_detail/model/select_details_model.ts +++ b/ui/src/core/ui/form_builder/forms/select_detail/model/select_details_model.ts @@ -4,7 +4,6 @@ import { Parts } from "../../../../../../features/details/details_http_repositor export class SelectDetailViewModel { details: Parts[]; constructor(parts: Parts[]) { - console.log(parts) this.details = parts; makeAutoObservable(this); } diff --git a/ui/src/core/ui/icons/icons.tsx b/ui/src/core/ui/icons/icons.tsx index dcca5b6..33b571b 100644 --- a/ui/src/core/ui/icons/icons.tsx +++ b/ui/src/core/ui/icons/icons.tsx @@ -8,11 +8,12 @@ export interface IIconsProps extends IStyle { height?: number; width?: number; color?: string; + iconStyle?: React.CSSProperties; isNeedStopPropagation?: boolean; } export function Icon(props: IIconsProps) { - return getIconSvg(props.type, props.height, props.width, props.color).fold( + return getIconSvg(props.type, props.height, props.width, props.color, props.iconStyle).fold( (node) => { return (
=> { switch (type) { case "": return Result.ok(); + case "Floppy": + return Result.ok( + + + + ); case "LeftIcon": return Result.ok( @@ -80,8 +91,9 @@ const getIconSvg = ( case "Bucket": return Result.ok( this.icons.replacePropIndex({ left: left }, index); } -export const MainPageV2: React.FC<{ children: React.ReactNode; bgColor: string; style?: React.CSSProperties | void }> = - observer(({ children, bgColor, style }) => { - const [store] = React.useState(() => new MainPageStore()); - useEffect(() => { - store.init(4); - }, []); - return ( +export const MainPageV2: React.FC<{ + children: React.ReactNode; + bgColor: string; + style?: React.CSSProperties | void; + rightChild?: React.ReactNode; +}> = observer(({ children, bgColor, style, rightChild }) => { + const [store] = React.useState(() => new MainPageStore()); + useEffect(() => { + store.init(4); + }, []); + return ( +
-
- - {store.icons.map((el, i) => ( -
- {el.isActive === true ? ( - <> - store.updateLeftStyle(Number(text), i)} - > - + {store.icons.map((el, i) => ( +
+ {el.isActive === true ? ( + <> + store.updateLeftStyle(Number(text), i)} + > + { - store.icons - .map((element) => { - element.isActive = false; - return element; - }) - .map((element, index) => - i.isEqualR(index).fold( - () => { - element.isActive = true; - }, - () => element - ) - ); - }} - /> - - - ) : ( - <> - store.updateLeftStyle(Number(text), i)} - > - store.selectIndex(i)} /> - - - )} -
- ))} - - } - /> -
- { - store.offsetLeftCircle = Number(text); - }} + position: "relative", + }} + onClick={() => { + store.icons + .map((element) => { + element.isActive = false; + return element; + }) + .map((element, index) => + i.isEqualR(index).fold( + () => { + element.isActive = true; + }, + () => element + ) + ); + }} + /> + + + ) : ( + <> + store.updateLeftStyle(Number(text), i)} + > + store.selectIndex(i)} /> + + + )} +
+ ))} + + } + /> +
+ { + store.offsetLeftCircle = Number(text); + }} + style={{ + position: "relative", + top: -30, + left: store.leftCircle, + backgroundColor: bgColor, + width: 60, + height: 60, + borderRadius: "50%", + paddingTop: 10, + zIndex: 21, + }} + > +
-
- -
+ >
+
+
+
+ {rightChild}
-
{children}
- ); - }); + +
{children}
+
+ ); +}); diff --git a/ui/src/features/all_projects/presentation/all_projects_screen.tsx b/ui/src/features/all_projects/presentation/all_projects_screen.tsx index cadc55a..7aa844a 100644 --- a/ui/src/features/all_projects/presentation/all_projects_screen.tsx +++ b/ui/src/features/all_projects/presentation/all_projects_screen.tsx @@ -12,16 +12,13 @@ import { DigitalTwinsScreenPath } from "../../digital_twins/digital_twins_screen import { TopicsScreenPath } from "../../topics/topics_screen"; import { SkillsScreenPath } from "../../skills/skills_screen"; import { CalculationsTemplateScreenPath } from "../../calculations_template/calculations_template_screen"; +import { useStore } from "../../../core/helper/use_store"; export const AllProjectScreenPath = "/"; export const AllProjectScreen: React.FunctionComponent = observer(() => { - const [store] = React.useState(() => new AllProjectStore()); + const store = useStore(AllProjectStore); const navigate = useNavigate(); - React.useEffect(() => { - store.init(); - }, []); - return ( >; - deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, this.featureApi); + deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`); editBt = async (model: BehaviorTreeModel) => { await this._jsonRequest(HttpMethod.POST, `${this.featureApi}/fill/tree`, model); + model.__v = undefined return await this._jsonRequest(HttpMethod.PUT, this.featureApi, model); }; } diff --git a/ui/src/features/behavior_tree_builder/model/behavior_tree_model.ts b/ui/src/features/behavior_tree_builder/model/behavior_tree_model.ts index c564362..8efe11d 100644 --- a/ui/src/features/behavior_tree_builder/model/behavior_tree_model.ts +++ b/ui/src/features/behavior_tree_builder/model/behavior_tree_model.ts @@ -8,6 +8,7 @@ import { message } from "antd"; import { ValidationModel } from "../../../core/model/validation_model"; export class BehaviorTreeModel extends ValidationModel { + __v?: string; @IsNumber() unixTime: number = Date.now(); @IsString() diff --git a/ui/src/features/behavior_tree_builder/model/editor_view.ts b/ui/src/features/behavior_tree_builder/model/editor_view.ts index 3e42a7e..c171d20 100644 --- a/ui/src/features/behavior_tree_builder/model/editor_view.ts +++ b/ui/src/features/behavior_tree_builder/model/editor_view.ts @@ -37,6 +37,7 @@ export class BehaviorTreeBuilderModel { this.result = ""; this.getFirstSequence(editor).map((sortedSequence) => { const firstNodeId = sortedSequence.getKeyFromValueIsExists(1) as string; + this.findSequence(firstNodeId, editor, sortedSequence, 2); this.result += `<${this.getNodeLabelAtId(editor, firstNodeId, skills)}>`; this.toXML(sortedSequence as Map, editor, area, firstNodeId, skills); @@ -49,12 +50,16 @@ export class BehaviorTreeBuilderModel { } public static getNodeLabelAtId(editor: NodeEditor, id: string, skills?: Skills) { - if (skills?.getSkillsNames().find((el) => el.name.isEqual(editor.getNode(id).label))) { - return `Action ID="RbsAction" do="${skills.getSkillDo(editor.getNode(id).label)}" command="${ - editor.getNode(id).label - }" sid=${id}"`; + try { + if (skills?.getSkillsNames().find((el) => el.name.isEqual(editor.getNode(id).label))) { + return `Action ID="RbsAction" do="${skills.getSkillDo(editor.getNode(id).label)}" command="${ + editor.getNode(id).label + }" sid=${id}"`; + } + return editor.getNode(id).label; + } catch (error) { + console.log(error); } - return editor.getNode(id).label; } static toXML( diff --git a/ui/src/features/behavior_tree_builder/model/primitive_view_model.ts b/ui/src/features/behavior_tree_builder/model/primitive_view_model.ts index 29fceab..21b1f39 100644 --- a/ui/src/features/behavior_tree_builder/model/primitive_view_model.ts +++ b/ui/src/features/behavior_tree_builder/model/primitive_view_model.ts @@ -35,9 +35,9 @@ export class PrimitiveViewModel { input: true, output: true, css: { - border: "1px solid #003E61", - borderRadius: 33, - background: "#BDE8AE", + border: "1px solid 6A4C93", + borderRadius: 5, + background: "rgba(21, 15, 29, 1)", }, }, { @@ -46,23 +46,132 @@ export class PrimitiveViewModel { input: true, output: true, css: { - border: `1px solid white`, + border: "1px solid 6A4C93", + borderRadius: 5, + background: "rgba(21, 15, 29, 1)", + }, + }, + { + label: SystemPrimitive.ForceFailure, + group: SystemPrimitive.Decorator, + input: true, + output: true, + css: { + border: "1px solid 6A4C93", + borderRadius: 5, + background: "rgba(21, 15, 29, 1)", + }, + }, + { + label: SystemPrimitive.Repeat, + group: SystemPrimitive.Decorator, + input: true, + output: true, + css: { + border: "1px solid 6A4C93", + borderRadius: 5, + background: "rgba(21, 15, 29, 1)", + }, + }, + { + label: SystemPrimitive.RetryUntilSuccessful, + group: SystemPrimitive.Decorator, + input: true, + output: true, + css: { + border: "1px solid 6A4C93", + borderRadius: 5, + background: "rgba(21, 15, 29, 1)", + }, + }, + { + label: SystemPrimitive.KeepRunningUntilFailure, + group: SystemPrimitive.Decorator, + input: true, + output: true, + css: { + border: "1px solid 6A4C93", + borderRadius: 5, + background: "rgba(21, 15, 29, 1)", + }, + }, + { + label: SystemPrimitive.Delay, + group: SystemPrimitive.Decorator, + input: true, + output: true, + css: { + border: "1px solid 6A4C93", + borderRadius: 5, + background: "rgba(21, 15, 29, 1)", + }, + }, + { + label: SystemPrimitive.RunOnce, + group: SystemPrimitive.Decorator, + input: true, + output: true, + css: { + border: "1px solid 6A4C93", + borderRadius: 5, + background: "rgba(21, 15, 29, 1)", + }, + }, + { + label: SystemPrimitive.ReactiveSequence, + group: SystemPrimitive.Sequence, + input: true, + output: true, + css: { + background: "rgba(28, 40, 8, 1)", + border: "1px solid rgba(138, 201, 38, 1)", + borderRadius: 5, + }, + }, + { + label: SystemPrimitive.Sequence, + group: SystemPrimitive.Sequence, + input: true, + output: true, + css: { + background: "rgba(28, 40, 8, 1)", + border: "1px solid rgba(138, 201, 38, 1)", + borderRadius: 5, + }, + }, + { + label: SystemPrimitive.SequenceWithMemory, + group: SystemPrimitive.Sequence, + input: true, + output: true, + css: { + background: "rgba(28, 40, 8, 1)", + border: "1px solid rgba(138, 201, 38, 1)", + borderRadius: 5, + }, + }, + { + label: SystemPrimitive.ReactiveFallback, + group: SystemPrimitive.Fallback, + input: true, + output: true, + css: { + background: "rgba(40, 8, 8, 1)", + border: "1px solid rgba(201, 38, 38, 1)", + borderRadius: 5, + }, + }, + { + label: SystemPrimitive.Fallback, + group: SystemPrimitive.Fallback, + input: true, + output: true, + css: { + background: "rgba(40, 8, 8, 1)", + border: "1px solid rgba(201, 38, 38, 1)", borderRadius: 5, - color: "white", - background: "rgba(106, 76, 147, 1)", }, }, - { label: SystemPrimitive.ForceFailure, group: SystemPrimitive.Decorator, input: true, output: true }, - { label: SystemPrimitive.Repeat, group: SystemPrimitive.Decorator, input: true, output: true }, - { label: SystemPrimitive.RetryUntilSuccessful, group: SystemPrimitive.Decorator, input: true, output: true }, - { label: SystemPrimitive.KeepRunningUntilFailure, group: SystemPrimitive.Decorator, input: true, output: true }, - { label: SystemPrimitive.Delay, group: SystemPrimitive.Decorator, input: true, output: true }, - { label: SystemPrimitive.RunOnce, group: SystemPrimitive.Decorator, input: true, output: true }, - { label: SystemPrimitive.ReactiveSequence, group: SystemPrimitive.Sequence, input: true, output: true }, - { label: SystemPrimitive.Sequence, group: SystemPrimitive.Sequence, input: true, output: true }, - { label: SystemPrimitive.SequenceWithMemory, group: SystemPrimitive.Sequence, input: true, output: true }, - { label: SystemPrimitive.ReactiveFallback, group: SystemPrimitive.Fallback, input: true, output: true }, - { label: SystemPrimitive.Fallback, group: SystemPrimitive.Fallback, input: true, output: true }, ]; getPrimitiveAtLabel = (name: string) => { return this.primitives.find((el) => el.label.isEqual(name)); @@ -73,7 +182,9 @@ export class PrimitiveViewModel { .rFind((el) => el.name.isEqual(primitive.group)) .fold( () => { - acc.at(acc.findIndex((element) => element.name === primitive.group))?.children?.push({ name: primitive.label }); + acc + .at(acc.findIndex((element) => element.name === primitive.group)) + ?.children?.push({ name: primitive.label }); }, () => { acc.push({ name: primitive.group, children: [{ name: primitive.label }] }); diff --git a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx index 14a41c1..82aa1e8 100644 --- a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx @@ -3,25 +3,20 @@ import { useRete } from "rete-react-plugin"; import { createEditor } from "./ui/editor/editor"; import { SkillTree } from "./ui/skill_tree/skill_tree"; import { BehaviorTreeBuilderStore, DrawerState, StoreUIType } from "./behavior_tree_builder_store"; -import { MainPage } from "../../../core/ui/pages/main_page"; -import { CoreButton } from "../../../core/ui/button/button"; import { observer } from "mobx-react-lite"; import { match } from "ts-pattern"; import { Icon } from "../../../core/ui/icons/icons"; -import { Drawer, theme } from "antd"; -import { CoreInput } from "../../../core/ui/input/input"; import { CoreText, CoreTextType } from "../../../core/ui/text/text"; import { useNavigate, useParams } from "react-router-dom"; import { IForms, forms } from "./ui/forms/forms"; -import { CoreSelect } from "../../../core/ui/select/select"; import { ButtonV2, ButtonV2Type } from "../../../core/ui/button/button_v2"; import { CoreCard } from "../../../core/ui/card/card"; import { themeStore } from "../../.."; import { CoreModal } from "../../../core/ui/modal/modal"; import { InputV2 } from "../../../core/ui/input/input_v2"; import { SelectV2 } from "../../../core/ui/select/select_v2"; -import { BehaviorTreeModel } from "../model/behavior_tree_model"; import { MainPageV2 } from "../../../core/ui/pages/main_page_v2"; +import { DrawerV2 } from "../../../core/ui/drawer/drawer"; export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path"; export interface DOMReact { @@ -42,24 +37,24 @@ export const BehaviorTreeBuilderPath = (id?: string) => { } return `/behavior/tree/`; }; - +export const isBtScreen = () => + Boolean(window.location.href.match(new RegExp(BehaviorTreeBuilderPath()))?.at(0)) ?? false; export const BehaviorTreeBuilderScreen = observer(() => { const navigate = useNavigate(); const { id } = useParams(); // @ts-expect-error const [ref] = useRete(createEditor); const store = behaviorTreeBuilderStore; - + React.useEffect(() => { + if (ref.current) { + // @ts-expect-error + const domReact: DOMReact = ref.current.getBoundingClientRect(); + store.dragZoneSetOffset(0, domReact.y, domReact.width, domReact.height); + } + }, [store.type]); React.useEffect(() => { store.init(navigate).then(() => { - store.initParam(id).then(() => { - if (ref.current) { - // @ts-expect-error - const domReact: DOMReact = ref.current.getBoundingClientRect(); - - store.dragZoneSetOffset(0, domReact.y, domReact.width, domReact.height); - } - }); + store.initParam(id).then(() => {}); }); return () => { store.dispose(); @@ -67,7 +62,52 @@ export const BehaviorTreeBuilderScreen = observer(() => { }, [id, navigate, ref, store]); return ( + {match(store.type) + .with(StoreUIType.ViewBehaviorTree, () => ( +
+ {}} text="Запуск" textColor={themeStore.theme.black} /> +
+ {}} + text="Стоп" + type={ButtonV2Type.empty} + textColor={themeStore.theme.greenWhite} + /> +
+
+ {store.isNeedSaveBtn ? ( + store.onClickSaveBehaviorTree()} type="Floppy" /> + ) : undefined} +
+
+ )) + .with(StoreUIType.SelectBehaviorTree, () => <>) + .otherwise(() => ( + <> + ))} + + } + style={{ position: "absolute", height: "100%", overflow: "hidden" }} bgColor={themeStore.theme.black} children={ <> @@ -98,9 +138,6 @@ export const BehaviorTreeBuilderScreen = observer(() => {
{store.skillTemplates ? : null}
-
- store.onClickSaveBehaviorTree()} text="Сохранить" /> -
{ )) .with(StoreUIType.SelectBehaviorTree, () => ( - <> +
} @@ -124,7 +161,7 @@ export const BehaviorTreeBuilderScreen = observer(() => {
- {store.btTreeModels?.repeat(100).map((el, index) => ( + {store.btTreeModels?.map((el, index) => ( store.deleteBt(el._id)} @@ -135,7 +172,7 @@ export const BehaviorTreeBuilderScreen = observer(() => { /> ))}
- +
)) .otherwise(() => ( <> @@ -187,14 +224,13 @@ export const BehaviorTreeBuilderScreen = observer(() => { } /> - store.editDrawer(DrawerState.editThreadBehaviorTree, false)} - open={store.drawers.find((el) => el.name === DrawerState.editThreadBehaviorTree)?.status} + isOpen={store.drawers.find((el) => el.name === DrawerState.editThreadBehaviorTree)?.status} >
-
+
{store.skillTemplates?.getForms(store.selected ?? "").map((formType, index) => forms( store.filledOutTemplates?.getDependencyBySkillLabelAndType( @@ -206,13 +242,21 @@ export const BehaviorTreeBuilderScreen = observer(() => { ) .rFind((form) => form.name.isEqual(formType)) .fold( - (s) =>
{s.component}
, - () =>
Error: Unknown form type {formType}
+ (s) => ( +
+ {s.component} +
+ ), + () => ( +
+ Error: Unknown form type {formType} +
+ ) ) )}
- + } /> diff --git a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx index 1b87b0b..5bb17de 100644 --- a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx @@ -1,5 +1,9 @@ import React from "react"; +import { message } from "antd"; +import { NavigateFunction } from "react-router-dom"; import makeAutoObservable from "mobx-store-inheritance"; +import { v4 } from "uuid"; +import clone from "just-clone"; import { CoreError, UiDrawerFormState } from "../../../core/store/base_store"; import { BehaviorTreeBuilderModel, @@ -13,15 +17,11 @@ import { AreaExtra, Schemes } from "./ui/editor/editor"; import { AreaPlugin } from "rete-area-plugin"; import { NodeBehaviorTree } from "../model/node_behavior_tree"; import { BehaviorTreeBuilderHttpRepository } from "../data/behavior_tree_builder_http_repository"; -import { IParam, Skills } from "../../../core/model/skill_model"; +import { DependencyViewModel, IParam, Skills } from "../../../core/model/skill_model"; import { ISkillView } from "./ui/skill_tree/skill_tree"; -import { message } from "antd"; -import { NavigateFunction } from "react-router-dom"; import { BehaviorTreeViewModel } from "../model/behavior_tree_view_model"; import { UiBaseError } from "../../../core/model/ui_base_error"; -import { v4 } from "uuid"; import { BehaviorTreeBuilderPath, behaviorTreeBuilderStore } from "./behavior_tree_builder_screen"; -import clone from "just-clone"; import { BehaviorTreeModel } from "../model/behavior_tree_model"; import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model"; import { SceneModel } from "../../scene_manager/model/scene_model"; @@ -60,6 +60,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState this.filledOutTemplates.topicsStack; errorHandingStrategy = (_: CoreError) => {}; dragEnd = (e: EventTarget) => { @@ -125,13 +125,19 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { - const modelSetId = clone(m).setSid(sid); - modelSetId.BTAction = m.BTAction.filter((el) => el.name.isEqual(name)); + (skill) => { + const skillModel = clone(skill).setSid(sid); + skillModel.BTAction = skill.BTAction.filter((el) => el.name.isEqual(name)); - this.filledOutTemplates?.skills.push(modelSetId); + this.filledOutTemplates?.skills.push(skillModel); + if (skill.emptyParam()) { + this.filledOutTemplates.topicsStack.push( + ...skillModel.topicsOut.updateAll({ sid: skillModel.sid ?? "" }) + ); + } }, () => console.log("error") ); @@ -139,7 +145,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { + async init(navigate: NavigateFunction): Promise { (await this.behaviorTreeBuilderHttpRepository.getActiveProjectId()) // eslint-disable-next-line array-callback-return .map((el) => { @@ -175,14 +181,15 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { this.errors.push(new UiBaseError(`не найдено дерево с id:${id}`)); } ); } else { + this.type = StoreUIType.SelectBehaviorTree; await this.mapOk("scenes", this.behaviorTreeBuilderHttpRepository.getAllScenes()); - // this.type = StoreUIType.SelectBehaviorTree; } }; dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) { @@ -193,11 +200,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState {}); - document.removeEventListener("keydown", () => {}); - } - onClickSaveBehaviorTree = async (): Promise => { + onClickSaveBehaviorTree = async () => this.filledOutTemplates.validation().fold( async () => { ( @@ -224,7 +227,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState message.error(`Дерево поведения не заполнено`) ); - }; + validateBt() {} createNewBehaviorTree = async () => { this.viewModel.project = this.activeProject; @@ -239,21 +242,24 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { - // this.selectedSid = sid; - // this.selected = label; + this.selectedSid = sid; + this.selected = label; // if (!Object.keys(SystemPrimitive).includes(label) && this.skillTemplates.skillHasForm(label)) { this.editDrawer(DrawerState.editThreadBehaviorTree, true); - this.selected = label; // } }; deleteBtAction = (sid: string) => { - this.nodeBehaviorTree = this.nodeBehaviorTree.filter((el) => !el.id.isEqual(sid)); this.filledOutTemplates.skills = this.filledOutTemplates.deleteSid(sid); + this.filledOutTemplates.deleteTopic(sid); this.nodeUpdateObserver?.emit({ type: UpdateEvent.DELETE, id: sid }); + + Array.from(this.areaPlugin!.nodeViews.keys()).forEach((id) => { + this.areaPlugin?.update("node", id); + }); }; - formUpdateDependency = (dependency: Object, formType: string) => { + formUpdateDependency = (dependency: DependencyViewModel, formType: string) => { this.filledOutTemplates?.skillBySid(this.selectedSid ?? "").fold( (m) => { const model = clone(m); @@ -263,15 +269,15 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState console.log("UNKNOWN SID: " + this.selectedSid) ); @@ -312,27 +318,19 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState
-
- ))} - - {/* {this.skillTemplates?.getSkilsOut(label).map((el, index) => ( -
-
OUT
-
- {el} + > + {behaviorTreeBuilderStore.isFilledInput(el.type, sid) + ? this.filledOutTemplates.getDependencyBySkillLabelAndType(label, sid).toView(behaviorTreeBuilderStore) + : undefined}
- ))} */} + ))}
); } - isFilledInput = (type: string, sid: string): boolean => { - return this.filledOutTemplates?.dependencyIsFilled(type, sid) ?? false; - }; + isFilledInput = (type: string, sid: string): boolean => + this.filledOutTemplates?.dependencyIsFilled(type, sid) ?? false; + getInputs(name: string) { const result = this.primitiveViewModel.getPrimitiveAtLabel(name); if (result) { @@ -347,19 +345,10 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState {}; @@ -370,7 +359,10 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { this.isModalOpen = false; }; - deleteBt = (id: string) => this.behaviorTreeBuilderHttpRepository.deleteBt(id); + deleteBt = async (id: string) => ( + await this.behaviorTreeBuilderHttpRepository.deleteBt(id), + await this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances()) + ); saveNewBt = async () => (await this.viewModel.valid()).fold( async (model) => { diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx index d6a054c..892afcf 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx @@ -5,6 +5,7 @@ import { $nodewidth, $socketmargin, $socketsize } from "./vars"; import { behaviorTreeBuilderStore } from "../../../behavior_tree_builder_screen"; import { themeStore } from "../../../../../.."; import { Icon } from "../../../../../../core/ui/icons/icons"; +import { SystemPrimitive } from "../../../../model/primitive_view_model"; const { RefSocket, RefControl } = Presets.classic; type NodeExtraData = { width?: number; height?: number }; @@ -152,18 +153,51 @@ export function SequenceNode(props: Props)
{label}
-
- -
-
+ {Object.keys(SystemPrimitive) + .rFind((el) => el.isEqual(label)) + .fold( + () => ( + <> +
+ behaviorTreeBuilderStore.deleteBtAction(id)} + /> +
+ + ), + () => ( + <> +
{ + if (result) { + return "none"; + } + }, + () => undefined + ), + }} + ref={refSelect} + > + +
+
-
- behaviorTreeBuilderStore.deleteBtAction(id)} - /> -
+
+ behaviorTreeBuilderStore.deleteBtAction(id)} + /> +
+ + ) + )}
{behaviorTreeBuilderStore.getBodyNode(label, id)}
diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/camera_device_form/camera_device_form_form.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/camera_device_form/camera_device_form_form.tsx index fd0ddc9..8ae4d07 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/camera_device_form/camera_device_form_form.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/camera_device_form/camera_device_form_form.tsx @@ -29,7 +29,7 @@ export const CameraDeviceForm = observer((props: IPropsForm { (await store.viewModel.valid()).fold( (s) => { - props.onChange(s); + // props.onChange(s); }, (e) => message.error(e) ); diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/forms.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/forms.tsx index f69a647..be6f8ad 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/forms.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/forms.tsx @@ -1,3 +1,4 @@ +import { DependencyViewModel } from "../../../../../core/model/skill_model"; import { BehaviorTreeBuilderStore } from "../../behavior_tree_builder_store"; import { CameraDeviceForm } from "./camera_device_form/camera_device_form_form"; import { MoveToPose } from "./move_to_pose/move_to_pose_form"; @@ -9,7 +10,7 @@ import { WeightsForm } from "./weights_form/weights_form"; export interface IPropsForm { dependency: T; store?: BehaviorTreeBuilderStore; - onChange: (dependency: Object) => void; + onChange: (dependency: DependencyViewModel) => void; } export interface IForms { @@ -28,7 +29,7 @@ export interface BtDependencyFormBuilder { component?: React.ReactNode; } -export const btDependencyFormBuilder = (onChange: (dependency: Object) => void) => [ +export const btDependencyFormBuilder = (onChange: (dependency: DependencyViewModel) => void) => [ { form: Form.weights, component: null }, { form: Form.topic, @@ -37,12 +38,12 @@ export const btDependencyFormBuilder = (onChange: (dependency: Object) => void) ]; export const forms = ( props: any, - onChange: (dependency: Object) => void, + onChange: (dependency: DependencyViewModel) => void, store: BehaviorTreeBuilderStore ): IForms[] => [ - { name: Form.weights, component: }, - { name: Form.robotName, component: }, - { name: Form.cameraDeviceForm, component: }, + // { name: Form.weights, component: }, + // { name: Form.robotName, component: }, + // { name: Form.cameraDeviceForm, component: }, { name: Form.topic, component: }, - { name: Form.moveToPose, component: }, + // { name: Form.moveToPose, component: }, ]; diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/move_to_pose/move_to_pose_form.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/move_to_pose/move_to_pose_form.tsx index 1a87217..c0d46bf 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/move_to_pose/move_to_pose_form.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/move_to_pose/move_to_pose_form.tsx @@ -39,7 +39,7 @@ export const MoveToPose = observer((props: IPropsForm { store.viewModel.valid().fold( (s) => { - props.onChange(s); + // props.onChange(s); }, (e) => message.error(e) ); diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.ts b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.ts deleted file mode 100644 index f33753b..0000000 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.ts +++ /dev/null @@ -1,21 +0,0 @@ -import makeAutoObservable from "mobx-store-inheritance"; -import { ValidationModel } from "../../../../../../core/model/validation_model"; -import { IsNotEmpty, IsString } from "class-validator"; -import { StoreTopicType } from "./topics_form_store"; - -export class TopicDependencyViewModel extends ValidationModel { - @IsNotEmpty() - @IsString() - type: string; - mode?: StoreTopicType; - constructor(type: string, mode: StoreTopicType) { - super(); - makeAutoObservable(this); - this.type = type; - this.mode = mode; - } - - static empty() { - return new TopicDependencyViewModel("", StoreTopicType.specifiesDependencies); - } -} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.tsx new file mode 100644 index 0000000..b2cef23 --- /dev/null +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.tsx @@ -0,0 +1,36 @@ +import makeAutoObservable from "mobx-store-inheritance"; +import { ValidationModel } from "../../../../../../core/model/validation_model"; +import { IsNotEmpty, IsString } from "class-validator"; +import { StoreTopicType } from "./topics_form_store"; +import { DependencyViewModel } from "../../../../../../core/model/skill_model"; +import { BehaviorTreeBuilderStore } from "../../../behavior_tree_builder_store"; +import { CoreText, CoreTextType } from "../../../../../../core/ui/text/text"; + +export class TopicDependencyViewModel extends ValidationModel implements DependencyViewModel { + @IsNotEmpty() + @IsString() + type: string; + mode?: StoreTopicType; + sid?: string; + topicOut?: string; + constructor(type: string, mode: StoreTopicType) { + super(); + makeAutoObservable(this); + this.type = type; + this.mode = mode; + } + toView = (store: BehaviorTreeBuilderStore | undefined): React.ReactNode => { + if (store && this.sid) { + if (store.filledOutTemplates.topicsStack.some((el) => el.sid?.isEqual(this.sid ?? ""))) { + return ; + } else { + return ; + } + } + return ; + }; + + static empty() { + return new TopicDependencyViewModel("", StoreTopicType.specifiesDependencies); + } +} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form.tsx index e9d5c6b..cabfad7 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form.tsx @@ -8,22 +8,71 @@ import { match } from "ts-pattern"; import { CoreInput } from "../../../../../../core/ui/input/input"; import { CoreButton } from "../../../../../../core/ui/button/button"; import { message } from "antd"; +import { themeStore } from "../../../../../.."; +import { isBtScreen } from "../../../behavior_tree_builder_screen"; export const TopicsForm = observer((props: IPropsForm>) => { const [store] = React.useState(() => new TopicsFormStore()); React.useEffect(() => { store.init(); + store.initParam(isBtScreen(), props.store); + store.loadClassInstance(TopicDependencyViewModel, props.dependency as TopicDependencyViewModel); }, [props]); return ( -
- - {match(store.viewModel?.mode ?? "") +
+ + {match(store.type) .with(StoreTopicType.btExecute, () => ( <> - + + {store + .getAllTopicsMatched() + + ?.map((el) => ( +
(store.updateForm({ topicOut: el.name }), store.updateForm({ sid: el.sid }))} + > + + +
+ ))} + { + (await store.viewModel.valid()).fold( + (model) => props.onChange(model), + (e) => message.error(e) + ); + }} + /> )) .with(StoreTopicType.specifiesDependencies, () => ( diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form_store.ts b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form_store.ts index 089b8b9..e308bb6 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form_store.ts +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form_store.ts @@ -4,20 +4,30 @@ import { TopicDependencyViewModel } from "./topic_dependency_view_model"; import { FormState, CoreError } from "../../../../../../core/store/base_store"; import { TopicsFormHttpRepository } from "./topics_form_http_repository"; import { Topics } from "../../../../../../core/model/topics"; +import { BehaviorTreeBuilderStore } from "../../../behavior_tree_builder_store"; +import { ITopicModel } from "../../../../../topics/topic_view_model"; export enum StoreTopicType { specifiesDependencies = "specifiesDependencies", btExecute = "btExecute", } export class TopicsFormStore extends FormState { + type: StoreTopicType = StoreTopicType.btExecute; + topics?: ITopicModel[]; + behaviorTreeBuilderStore?: BehaviorTreeBuilderStore; + viewModel: TopicDependencyViewModel = TopicDependencyViewModel.empty(); constructor() { super(); makeAutoObservable(this); } - topics?: Topics; - viewModel: TopicDependencyViewModel = TopicDependencyViewModel.empty(); - cameraDeviceHttpRepository: TopicsFormHttpRepository = new TopicsFormHttpRepository(); + topicsFormHttpRepository: TopicsFormHttpRepository = new TopicsFormHttpRepository(); errorHandingStrategy = (error: CoreError) => {}; init = async (navigate?: NavigateFunction | undefined) => { // await this.mapOk('topics', this.cameraDeviceHttpRepository.getAllTopics()) }; + getAllTopicsMatched = () => this.topics?.filter((el) => el.type.isEqual(this.viewModel.type)); + initParam = (isBtScreen: boolean, behaviorTreeBuilderStore: BehaviorTreeBuilderStore | undefined) => { + isBtScreen ? (this.type = StoreTopicType.btExecute) : (this.type = StoreTopicType.specifiesDependencies); + this.behaviorTreeBuilderStore = behaviorTreeBuilderStore; + this.topics = behaviorTreeBuilderStore?.getAllTopics(); + }; } diff --git a/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx b/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx index 5569d9a..18717a3 100644 --- a/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx +++ b/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx @@ -14,6 +14,7 @@ import { match } from "ts-pattern"; import { TemplateModelCard } from "./ui/template_model_card"; import { Icon } from "../../../core/ui/icons/icons"; import { FormBuilderValidationModel } from "../../dataset/dataset_model"; +import { useStore } from "../../../core/helper/use_store"; interface IItem { name: string; @@ -25,11 +26,8 @@ const skills: IItem[] = [{ name: "ML", isActive: true }]; export const CalculationInstanceScreenPath = "/calculation"; export const CalculationInstanceScreen = observer(() => { - const [store] = React.useState(() => new CalculationInstanceStore()); + const store = useStore(CalculationInstanceStore); - React.useEffect(() => { - store.init(); - }, []); return ( <> { - const [store] = React.useState(() => new CalculationsTemplateStore()); - React.useEffect(() => { - store.init(); - }, []); + const store = useStore(CalculationsTemplateStore); + return <>; }); diff --git a/ui/src/features/dataset/dataset_screen.tsx b/ui/src/features/dataset/dataset_screen.tsx index dc73e58..b5b78b3 100644 --- a/ui/src/features/dataset/dataset_screen.tsx +++ b/ui/src/features/dataset/dataset_screen.tsx @@ -7,15 +7,13 @@ import { MainPage } from "../../core/ui/pages/main_page"; import { CardDataSet, CardDataSetType } from "./card_dataset"; import { CoreInput } from "../../core/ui/input/input"; import { CoreButton } from "../../core/ui/button/button"; +import { useStore } from "../../core/helper/use_store"; export const DatasetsScreenPath = "/dataset"; export const DataSetScreen: React.FunctionComponent = observer(() => { - const [store] = React.useState(() => new DataSetStore()); + const store = useStore(DataSetStore); - React.useEffect(() => { - store.init(); - }, []); return ( <> { filled={true} />
-
diff --git a/ui/src/features/details/details_screen.tsx b/ui/src/features/details/details_screen.tsx index 552cbc5..36fc665 100644 --- a/ui/src/features/details/details_screen.tsx +++ b/ui/src/features/details/details_screen.tsx @@ -6,11 +6,12 @@ import { CoreText, CoreTextType } from "../../core/ui/text/text"; import { CoreButton } from "../../core/ui/button/button"; import { Drawer, Upload } from "antd"; import { CoreInput } from "../../core/ui/input/input"; +import { useStore } from "../../core/helper/use_store"; export const DetailsScreenPath = "/details"; export const DetailsScreen = observer(() => { - const [store] = React.useState(() => new DetailsStore()); + const store = useStore(DetailsStore); React.useEffect(() => { store.loadScene(canvasRef.current!); }, []); diff --git a/ui/src/features/digital_twins/digital_twins_screen.tsx b/ui/src/features/digital_twins/digital_twins_screen.tsx index 94fbbe7..cea035b 100644 --- a/ui/src/features/digital_twins/digital_twins_screen.tsx +++ b/ui/src/features/digital_twins/digital_twins_screen.tsx @@ -8,14 +8,13 @@ import { CoreInput } from "../../core/ui/input/input"; import { CoreSelect } from "../../core/ui/select/select"; import { DigitalTwinsModel, DigitalTwinsTypes } from "./digital_twins_model"; import { match } from "ts-pattern"; +import { useStore } from "../../core/helper/use_store"; export const DigitalTwinsScreenPath = "/digital_twins"; export const DigitalTwinsScreen = observer(() => { - const [store] = React.useState(() => new DigitalTwinsStore()); - React.useEffect(() => { - store.init(); - }, []); + const store = useStore(DigitalTwinsStore); + return ( <>
diff --git a/ui/src/features/digital_twins/digital_twins_store.ts b/ui/src/features/digital_twins/digital_twins_store.ts index 8c9d87b..77d6016 100644 --- a/ui/src/features/digital_twins/digital_twins_store.ts +++ b/ui/src/features/digital_twins/digital_twins_store.ts @@ -43,8 +43,7 @@ export class DigitalTwinsStore extends UiDrawerFormState { - console.log(200); - + // this.editDrawer(DrawersDigitalTwin.newInstanceTwinTemplate, false), // await ( // await this.digitalTwinsHttpRepository.createNewDigitalTwinsInstance(this.viewModel) diff --git a/ui/src/features/scene_manager/presentation/scene_manager.tsx b/ui/src/features/scene_manager/presentation/scene_manager.tsx index 7ce08a9..6a25803 100644 --- a/ui/src/features/scene_manager/presentation/scene_manager.tsx +++ b/ui/src/features/scene_manager/presentation/scene_manager.tsx @@ -11,18 +11,18 @@ import { DrawersDataset } from "../../dataset/dataset_store"; import { Icon } from "../../../core/ui/icons/icons"; import { sceneManagerForms } from "./forms/scene_manager_forms"; import { SceneMode } from "../model/scene_view"; +import { useStore } from "../../../core/helper/use_store"; export const SceneManagerPath = "/scene/manager/"; export const SceneManger = observer(() => { const canvasRef = React.useRef(null); - const [store] = React.useState(() => new SceneMangerStore()); + const store = useStore(SceneMangerStore); const id = useParams().id as string; const navigate = useNavigate(); store.initParam(id); - React.useEffect(() => { - store.init(); + React.useEffect(() => { store.loadScene(canvasRef.current!); document.body.style.overflow = "hidden"; return () => { diff --git a/ui/src/features/simulations/simulations_screen.tsx b/ui/src/features/simulations/simulations_screen.tsx index 9fd1644..ef02ce3 100644 --- a/ui/src/features/simulations/simulations_screen.tsx +++ b/ui/src/features/simulations/simulations_screen.tsx @@ -1,15 +1,15 @@ import * as React from "react"; import { MainPage } from "../../core/ui/pages/main_page"; import { SimulationStore } from "./simulations_store"; -interface ISimulationScreenProps { } +import { useStore } from "../../core/helper/use_store"; +interface ISimulationScreenProps {} export const SimulationScreenPath = "/simulation"; export const SimulationScreen = (props: ISimulationScreenProps) => { - const [store] = React.useState(() => new SimulationStore()); + const store = useStore(SimulationStore); const canvasRef = React.useRef(null); React.useEffect(() => { - store.init(); store.loadScene(canvasRef.current!); }, []); return } />; -} \ No newline at end of file +}; diff --git a/ui/src/features/skills/skills_http_repository.ts b/ui/src/features/skills/skills_http_repository.ts index 765ea75..1cb29d0 100644 --- a/ui/src/features/skills/skills_http_repository.ts +++ b/ui/src/features/skills/skills_http_repository.ts @@ -4,5 +4,6 @@ import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repo export class SkillsHttpRepository extends HttpRepository { featureApi = "/skills"; createNewSkill = (model: SkillModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model); + deleteSkill = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`); getAllSkills = () => this._arrayJsonToClassInstanceRequest(HttpMethod.GET, this.featureApi, SkillModel); } diff --git a/ui/src/features/skills/skills_screen.tsx b/ui/src/features/skills/skills_screen.tsx index f9fbd9e..facccee 100644 --- a/ui/src/features/skills/skills_screen.tsx +++ b/ui/src/features/skills/skills_screen.tsx @@ -9,14 +9,12 @@ import { BtAction, BtActionViewModel } from "../../core/model/skill_model"; import { btDependencyFormBuilder } from "../behavior_tree_builder/presentation/ui/forms/forms"; import { CoreButton } from "../../core/ui/button/button"; import { CoreSelect } from "../../core/ui/select/select"; +import { useStore } from "../../core/helper/use_store"; export const SkillsScreenPath = "/skills"; export const SkillsScreen = observer(() => { - const [store] = React.useState(() => new SkillsStore()); - React.useEffect(() => { - store.init(); - }, []); + const store = useStore(SkillsStore); return ( <> @@ -36,10 +34,17 @@ export const SkillsScreen = observer(() => { borderRadius: 20, }} > + store.deleteSkill(el._id as string)} />
+ + {el.topicsOut.map((el) => ( +
+
{el.name}
+
{el.type}
+
+ ))} - {el.BTAction.map((el) => (
@@ -83,6 +88,16 @@ export const SkillsScreen = observer(() => { store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { version: text }) }) } /> + + store.updateForm({ Module: Object.assign(store.viewModel.Module, { description: text }) }) + } + /> + store.updateForm({ Module: Object.assign(store.viewModel.Module, { name: text }) })} + /> { } init = async (navigate?: NavigateFunction | undefined) => { this.mapOk("skills", this.skillsHttpRepository.getAllSkills()); + }; + deleteSkill = async (id: string) => (await this.skillsHttpRepository.deleteSkill(id), this.init()); modalShow = () => { this.isModalOpen = true; }; @@ -42,7 +44,7 @@ export class SkillsStore extends UiDrawerFormState { this.activeIndex = index; this.modalShow(); }; - onChangeBtDependency = (dependency: Object) => + onChangeBtDependency = (dependency: DependencyViewModel) => this.viewModel.BTAction.at(this.activeIndex ?? 0) ?.validParam(this.selectParam?.form ?? "") .fold( @@ -67,11 +69,11 @@ export class SkillsStore extends UiDrawerFormState { clickParam(el: { form: Form; component: null } | { form: Form; component: React.JSX.Element }): void { this.selectParam = el; - if (el.component === null) this.onChangeBtDependency({}); + if (el.component === null) this.onChangeBtDependency(DependencyViewModel.empty()); } saveNewSkill = async () => { (await this.viewModel.valid()).fold( - async (model) => this.skillsHttpRepository.createNewSkill(model), + async (model) => (this.skillsHttpRepository.createNewSkill(model), this.handleCancel(), this.init()), async (e) => message.error(e) ); }; diff --git a/ui/src/features/socket_listener/socket_listener.tsx b/ui/src/features/socket_listener/socket_listener.tsx index c1db646..991ab86 100644 --- a/ui/src/features/socket_listener/socket_listener.tsx +++ b/ui/src/features/socket_listener/socket_listener.tsx @@ -3,11 +3,7 @@ import { socketListenerStore } from "./socket_listener_store"; import { observer } from "mobx-react-lite"; import { CoreText, CoreTextType } from "../../core/ui/text/text"; -export interface ISocketListenerProps { - children?: JSX.Element; -} - -export const SocketListener = observer((props: ISocketListenerProps) => { +export const SocketListener: React.FC<{ children?: JSX.Element }> = observer(({ children }) => { React.useEffect(() => { socketListenerStore.init(); }, []); @@ -18,7 +14,7 @@ export const SocketListener = observer((props: ISocketListenerProps) => {
Loading…
@@ -27,7 +23,7 @@ export const SocketListener = observer((props: ISocketListenerProps) => { <> )} - {props.children} + {children} ); }); diff --git a/ui/src/features/topics/topic_view_model.ts b/ui/src/features/topics/topic_view_model.ts index e47c852..44c8bc2 100644 --- a/ui/src/features/topics/topic_view_model.ts +++ b/ui/src/features/topics/topic_view_model.ts @@ -8,6 +8,7 @@ export class TopicViewModel extends ValidationModel { @IsNotEmpty() @IsString() type: string; + sid?: string; constructor(name: string, type: string) { super(); this.name = name; @@ -19,6 +20,7 @@ export class TopicViewModel extends ValidationModel { } export interface ITopicModel { digitalTwinId?: string; + sid?: string; name: string; type: string; } diff --git a/ui/src/features/topics/topics_screen.tsx b/ui/src/features/topics/topics_screen.tsx index 5f4d415..2fb82c8 100644 --- a/ui/src/features/topics/topics_screen.tsx +++ b/ui/src/features/topics/topics_screen.tsx @@ -1,14 +1,13 @@ +import React from "react"; import { observer } from "mobx-react-lite"; import { TopicsStore } from "./topics_store"; -import React from "react"; +import { useStore } from "../../core/helper/use_store"; export const TopicsScreenPath = "/topics"; export const TopicsScreen = observer(() => { - const [store] = React.useState(() => new TopicsStore()); - React.useEffect(() => { - store.init(); - }, []); + const store = useStore(TopicsStore); + return ( <> {store.topics?.map((el) => ( diff --git a/ui/src/index.tsx b/ui/src/index.tsx index 0a5d52b..a8d3fea 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -16,6 +16,7 @@ extensions(); const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); export const themeStore = new ThemeStore(); + root.render( <>