diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..2b7bafa --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-react"] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f42fad --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules +/src/assets/scenario-builder/dist +/build + +# local env files +.env.local +.env.*.local +/src/env.js + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..8ab3485 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 6 +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a7f81a1 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +js: + yarn install + yarn build-prod diff --git a/README.md b/README.md index 139e909..ce36567 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,27 @@ php bin/command.php api:generate_access php bin/command.php application:seed ``` +### Building assets + +Install dependencies inside module folder `yarn install` +Be sure to have scenario config filled in a global scope. It should look like this: + +```js +window.Scenario = { + config: { + AUTH_TOKEN: '', + CRM_HOST: '', + CANCEL_PATH: '', + SEGMENT_ID: null + } +}; +``` + +Run `yarn build-prod` if you want to build whole application for the deployment. Then, at the root of the project, copy assets using: +```shell +php bin/command.php application:install_assets +``` + ### Engine Scenarios module requires Scenario engine worker to run separately in a background to process graph execution flows. diff --git a/package.json b/package.json new file mode 100644 index 0000000..a35ca7c --- /dev/null +++ b/package.json @@ -0,0 +1,57 @@ +{ + "name": "remp-scenario-builder", + "version": "0.3.0", + "private": true, + "dependencies": { + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.56", + "@projectstorm/react-diagrams": "^5.3.2", + "axios": "^0.18.0", + "eslint-plugin-react-hooks": "^2.3.0", + "less": "^4.1.3", + "less-loader": "^11.1.3", + "lodash": "^4.17.11", + "react": "^16.6.0", + "react-dom": "^16.6.0", + "react-redux": "^7.1.3", + "react-scripts": "^3.4.3", + "react-select": "^2.1.2", + "recompose": "^0.30.0", + "redux": "^4.0.1", + "redux-logger": "^3.0.6", + "redux-thunk": "^2.3.0", + "storm-react-diagrams": "^5.2.1" + }, + "scripts": { + "serve": "webpack serve --mode development", + "build-dev": "webpack --mode development", + "build-prod": "webpack --mode production" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ], + "devDependencies": { + "@babel/core": "^7.24.7", + "@babel/preset-env": "^7.24.7", + "@babel/preset-react": "^7.24.7", + "babel-loader": "^9.1.3", + "cpx": "^1.5.0", + "css-loader": "^7.1.2", + "eslint-config-rallycoding": "^3.2.0", + "html-webpack-plugin": "^5.6.0", + "sass": "^1.77.6", + "sass-loader": "^14.2.1", + "style-loader": "^4.0.0", + "url-loader": "^4.1.1", + "webpack": "^5.92.1", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.0.4" + } +} diff --git a/src/ScenariosModule.php b/src/ScenariosModule.php index 45c3a82..8ed18f1 100644 --- a/src/ScenariosModule.php +++ b/src/ScenariosModule.php @@ -164,7 +164,7 @@ public function registerLazyEventHandlers(LazyEventEmitter $emitter) public function registerAssets(AssetsManager $assetsManager) { - $assetsManager->copyAssets(__DIR__ . '/assets/scenariobuilder', 'layouts/admin/scenariobuilder'); + $assetsManager->copyAssets(__DIR__ . '/assets/scenario-builder/dist', 'layouts/admin/scenario-builder'); } public function registerScenariosCriteria(ScenariosCriteriaStorage $scenariosCriteriaStorage) diff --git a/src/assets/scenario-builder/App.js b/src/assets/scenario-builder/App.js new file mode 100644 index 0000000..57ecafe --- /dev/null +++ b/src/assets/scenario-builder/App.js @@ -0,0 +1,63 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; + +import BodyWidget from './components/widgets/BodyWidget'; +import { Application } from './components/Application'; +import * as config from './config'; +import { + fetchSegments, + fetchGoals, + fetchBanners, + fetchTriggers, + fetchBeforeTriggers, + fetchCriteria, + fetchScenario, + setScenarioName, + fetchMails, + fetchGenerics, + fetchPushNotifications, + fetchStatistics +} from './actions'; + +class App extends Component { + componentDidMount() { + const { dispatch } = this.props; + + dispatch(fetchSegments()); + dispatch(fetchCriteria()); + dispatch(fetchGoals()); + dispatch(fetchTriggers()); + dispatch(fetchBeforeTriggers()); + dispatch(fetchMails()); + dispatch(fetchGenerics()); + + if (config.BANNER_ENABLED) { + dispatch(fetchBanners()); + } + + if (config.PUSH_NOTIFICATION_ENABLED) { + dispatch(fetchPushNotifications()); + } + + if (config.SCENARIO_ID) { + dispatch(fetchScenario(config.SCENARIO_ID)); + dispatch(fetchStatistics(config.SCENARIO_ID)); + } else { + dispatch(setScenarioName('Unnamed scenario')); + } + } + + render() { + var app = new Application(this.props.scenarioPayload); + + return ; + } +} + +function mapStateToProps(state) { + return { + scenarioPayload: state.scenario.payload + }; +} + +export default connect(mapStateToProps)(App); diff --git a/src/assets/scenario-builder/actions/BannersActions.js b/src/assets/scenario-builder/actions/BannersActions.js new file mode 100644 index 0000000..eb2d958 --- /dev/null +++ b/src/assets/scenario-builder/actions/BannersActions.js @@ -0,0 +1,35 @@ +import axios from 'axios'; +import * as config from '../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; +import { BANNERS_CHANGED } from './types'; + +export function updateBanners(banners) { + return { + type: BANNERS_CHANGED, + payload: banners + }; +} + +export function fetchBanners() { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(config.URL_BANNERS_INDEX) + .then(response => { + dispatch(updateBanners(response.data.banners)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + console.log(error); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Banners fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/BeforeTriggersActions.js b/src/assets/scenario-builder/actions/BeforeTriggersActions.js new file mode 100644 index 0000000..6b4f805 --- /dev/null +++ b/src/assets/scenario-builder/actions/BeforeTriggersActions.js @@ -0,0 +1,36 @@ +import axios from 'axios'; +import * as config from './../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; + +import { BEFORE_TRIGGERS_CHANGED } from './types'; + +export function updateBeforeTriggers(beforeTriggers) { + return { + type: BEFORE_TRIGGERS_CHANGED, + payload: beforeTriggers + }; +} + +export function fetchBeforeTriggers() { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(`${config.URL_BEFORE_TRIGGERS_INDEX}`) + .then(response => { + dispatch(updateBeforeTriggers(response.data.events)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + console.log(error); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Triggers fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/CanvasActions.js b/src/assets/scenario-builder/actions/CanvasActions.js new file mode 100644 index 0000000..1de6e77 --- /dev/null +++ b/src/assets/scenario-builder/actions/CanvasActions.js @@ -0,0 +1,34 @@ +import { + CANVAS_PANNABLE, + CANVAS_ZOOMABLE, + CANVAS_ZOOMABLE_PANNABLE, + CANVAS_NOTIFICATION +} from './types'; + +export function setCanvasZoomable(zoomable) { + return { + type: CANVAS_ZOOMABLE, + payload: zoomable + }; +} + +export function setCanvasPannable(pannable) { + return { + type: CANVAS_PANNABLE, + payload: pannable + }; +} + +export function setCanvasZoomingAndPanning(zoomingAndPanning) { + return { + type: CANVAS_ZOOMABLE_PANNABLE, + payload: zoomingAndPanning + }; +} + +export function setCanvasNotification(notificationOptions) { + return { + type: CANVAS_NOTIFICATION, + payload: notificationOptions + }; +} diff --git a/src/assets/scenario-builder/actions/CriteriaActions.js b/src/assets/scenario-builder/actions/CriteriaActions.js new file mode 100644 index 0000000..2cbea23 --- /dev/null +++ b/src/assets/scenario-builder/actions/CriteriaActions.js @@ -0,0 +1,35 @@ +import axios from 'axios'; +import * as config from '../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; +import { CRITERIA_CHANGED } from './types'; + +export function updateCriteria(criteria) { + return { + type: CRITERIA_CHANGED, + payload: criteria + }; +} + +export function fetchCriteria() { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(config.URL_SCENARIO_CRITERIA) + .then(response => { + dispatch(updateCriteria(response.data.blueprint)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + console.log(error); + dispatch(setScenarioLoading(false)); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Criteria fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/GenericsActions.js b/src/assets/scenario-builder/actions/GenericsActions.js new file mode 100644 index 0000000..3f49fef --- /dev/null +++ b/src/assets/scenario-builder/actions/GenericsActions.js @@ -0,0 +1,36 @@ +import axios from 'axios'; +import * as config from './../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; + +import { GENERICS_CHANGED } from './types'; + +export function updateGenerics(generics) { + return { + type: GENERICS_CHANGED, + payload: generics + }; +} + +export function fetchGenerics() { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(`${config.URL_GENERICS_INDEX}`) + .then(response => { + dispatch(updateGenerics(response.data)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + console.log(error); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Generics fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/GoalsActions.js b/src/assets/scenario-builder/actions/GoalsActions.js new file mode 100644 index 0000000..7f68603 --- /dev/null +++ b/src/assets/scenario-builder/actions/GoalsActions.js @@ -0,0 +1,35 @@ +import axios from 'axios'; +import * as config from './../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; +import { GOALS_CHANGED } from './types'; + +export function updateGoals(goals) { + return { + type: GOALS_CHANGED, + payload: goals + }; +} + +export function fetchGoals() { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(config.URL_GOALS_INDEX) + .then(response => { + dispatch(updateGoals(response.data.goals)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + console.log(error); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Goals fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/MailsActions.js b/src/assets/scenario-builder/actions/MailsActions.js new file mode 100644 index 0000000..9f934df --- /dev/null +++ b/src/assets/scenario-builder/actions/MailsActions.js @@ -0,0 +1,36 @@ +import axios from 'axios'; +import * as config from './../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; + +import { MAILS_CHANGED } from './types'; + +export function updateMails(mails) { + return { + type: MAILS_CHANGED, + payload: mails + }; +} + +export function fetchMails() { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(`${config.URL_MAILS_INDEX}`) + .then(response => { + dispatch(updateMails(response.data.mail_templates)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + console.log(error); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Mails fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/PushNotificationActions.js b/src/assets/scenario-builder/actions/PushNotificationActions.js new file mode 100644 index 0000000..97621b3 --- /dev/null +++ b/src/assets/scenario-builder/actions/PushNotificationActions.js @@ -0,0 +1,43 @@ +import axios from 'axios'; +import * as config from './../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; + +import {PUSH_NOTIFICATIONS_CHANGED} from './types'; + +export function updateNotifications(templates, applications) { + return { + type: PUSH_NOTIFICATIONS_CHANGED, + payload: { + templates: templates, + applications: applications, + } + }; +} + +export function fetchPushNotifications() { + return dispatch => { + + dispatch(setScenarioLoading(true)); + + let requestTemplates = axios.get(`${config.URL_PUSH_NOTIFICATION_TEMPLATES}`); + let requestApplications = axios.get(`${config.URL_PUSH_NOTIFICATION_APPLICATIONS}`); + + return axios.all([requestTemplates, requestApplications]) + .then(responses => { + dispatch(updateNotifications(responses[0].data.templates, responses[1].data.applications)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + console.log(error); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Notifications fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/ScenarioActions.js b/src/assets/scenario-builder/actions/ScenarioActions.js new file mode 100644 index 0000000..d1d71ee --- /dev/null +++ b/src/assets/scenario-builder/actions/ScenarioActions.js @@ -0,0 +1,62 @@ +import axios from 'axios'; +import { + SET_SCENARIO_ID, + SET_SCENARIO_NAME, + SET_SCENARIO_LOADING, + SET_SCENARIO_PAYLOAD +} from './types'; +import * as config from '../config'; +import { setCanvasNotification } from './CanvasActions'; + +export function setScenarioId(id) { + return { + type: SET_SCENARIO_ID, + payload: id + }; +} + +export function setScenarioName(name) { + return { + type: SET_SCENARIO_NAME, + payload: name + }; +} + +export function setScenarioPayload(payload) { + return { + type: SET_SCENARIO_PAYLOAD, + payload + }; +} + +export function setScenarioLoading(loading) { + return { + type: SET_SCENARIO_LOADING, + payload: loading + }; +} + +export function fetchScenario(scenarioId) { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(config.URL_SCENARIO_DETAIL + scenarioId) + .then(response => { + dispatch(setScenarioPayload(response.data)); + dispatch(setScenarioName(response.data.name)); + dispatch(setScenarioId(response.data.id)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Scenario fetching failed.' + }) + ); + console.log(error); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/SegmentsActions.js b/src/assets/scenario-builder/actions/SegmentsActions.js new file mode 100644 index 0000000..ccc0ffe --- /dev/null +++ b/src/assets/scenario-builder/actions/SegmentsActions.js @@ -0,0 +1,36 @@ +import axios from 'axios'; +import * as config from './../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; + +import { SEGMENTS_CHANGED } from './types'; + +export function updateSegments(segments) { + return { + type: SEGMENTS_CHANGED, + payload: segments + }; +} + +export function fetchSegments() { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(`${config.URL_SEGMENTS_INDEX}`) + .then(response => { + dispatch(updateSegments(response.data.result)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + console.log(error); + dispatch(setScenarioLoading(false)); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Segments fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/StatisticActions.js b/src/assets/scenario-builder/actions/StatisticActions.js new file mode 100644 index 0000000..bac2165 --- /dev/null +++ b/src/assets/scenario-builder/actions/StatisticActions.js @@ -0,0 +1,19 @@ +import {STATISTICS_CHANGED} from "./types"; +import axios from "axios"; +import * as config from "../config"; + +export function fetchStatistics(scenarioId) { + return dispatch => { + return axios + .get(config.URL_SCENARIO_STATISTIC + scenarioId) + .then(response => { + dispatch({ + type: STATISTICS_CHANGED, + payload: response.data + }); + }) + .catch(error => { + console.log(error); + }); + }; +} \ No newline at end of file diff --git a/src/assets/scenario-builder/actions/TriggersActions.js b/src/assets/scenario-builder/actions/TriggersActions.js new file mode 100644 index 0000000..9a08df0 --- /dev/null +++ b/src/assets/scenario-builder/actions/TriggersActions.js @@ -0,0 +1,36 @@ +import axios from 'axios'; +import * as config from './../config'; +import { setScenarioLoading } from './ScenarioActions'; +import { setCanvasNotification } from './CanvasActions'; + +import { TRIGGERS_CHANGED } from './types'; + +export function updateTriggers(triggers) { + return { + type: TRIGGERS_CHANGED, + payload: triggers + }; +} + +export function fetchTriggers() { + return dispatch => { + dispatch(setScenarioLoading(true)); + return axios + .get(`${config.URL_TRIGGERS_INDEX}`) + .then(response => { + dispatch(updateTriggers(response.data.events)); + dispatch(setScenarioLoading(false)); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + console.log(error); + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Triggers fetching failed.' + }) + ); + }); + }; +} diff --git a/src/assets/scenario-builder/actions/index.js b/src/assets/scenario-builder/actions/index.js new file mode 100644 index 0000000..75d6278 --- /dev/null +++ b/src/assets/scenario-builder/actions/index.js @@ -0,0 +1,12 @@ +export * from './SegmentsActions'; +export * from './TriggersActions'; +export * from './CanvasActions'; +export * from './ScenarioActions'; +export * from './MailsActions'; +export * from './GenericsActions'; +export * from './GoalsActions'; +export * from './BannersActions'; +export * from './CriteriaActions'; +export * from './BeforeTriggersActions'; +export * from './PushNotificationActions'; +export * from './StatisticActions'; \ No newline at end of file diff --git a/src/assets/scenario-builder/actions/types.js b/src/assets/scenario-builder/actions/types.js new file mode 100644 index 0000000..c689ba1 --- /dev/null +++ b/src/assets/scenario-builder/actions/types.js @@ -0,0 +1,24 @@ +export const SEGMENTS_CHANGED = 'segments_changed'; + +export const CRITERIA_CHANGED = 'criteria_changed'; + +export const TRIGGERS_CHANGED = 'triggers_changed'; +export const BEFORE_TRIGGERS_CHANGED = 'before_triggers_changed'; + +export const MAILS_CHANGED = 'mails_changed'; +export const GOALS_CHANGED = 'goals_changed'; +export const BANNERS_CHANGED = 'banners_changed'; +export const GENERICS_CHANGED = 'generics_changed'; +export const PUSH_NOTIFICATIONS_CHANGED = 'push_notifications_changed'; + +export const CANVAS_ZOOMABLE = 'canvas_zoomable'; +export const CANVAS_PANNABLE = 'canvas_pannable'; +export const CANVAS_ZOOMABLE_PANNABLE = 'canvas_zoomable_pannable'; +export const CANVAS_NOTIFICATION = 'canvas_notification'; + +export const SET_SCENARIO_ID = 'set_scenario_id'; +export const SET_SCENARIO_NAME = 'set_scenario_name'; +export const SET_SCENARIO_PAYLOAD = 'set_scenario_payload'; +export const SET_SCENARIO_LOADING = 'set_scenario_loading'; + +export const STATISTICS_CHANGED = 'statistics_changed'; \ No newline at end of file diff --git a/src/assets/scenario-builder/components/Application.js b/src/assets/scenario-builder/components/Application.js new file mode 100755 index 0000000..55ec3fa --- /dev/null +++ b/src/assets/scenario-builder/components/Application.js @@ -0,0 +1,131 @@ +import { DiagramEngine, DiagramModel } from '@projectstorm/react-diagrams'; + +// import the custom models +import { + SimplePortFactory, + Banner, + Email, + Generic, + Segment, + Trigger, + BeforeTrigger, + Wait, + Goal, + Condition, + PushNotification, + ABTest +} from './elements'; + +import './sass/main.scss'; +import { LinkFactory } from './elements/Link'; +import { RenderService } from './../services/RenderService'; + +export class Application { + activeModel; + diagramEngine; + + constructor(payload) { + this.diagramEngine = new DiagramEngine(); + this.diagramEngine.installDefaultFactories(); + this.activeModel = new DiagramModel(); + this.renderService = new RenderService(this.activeModel); + this.payload = payload; + this.corruptedPayload = false; + + if (payload) { + this.renderPayload(); + } else { + this.registerCustomModels(); + } + } + + renderPayload() { + this.registerCustomModels(); + try { + this.renderService.renderPayload(this.payload); + } catch(ex) { + // In case of rendering error, dump loaded model, log and flag as corrupted scenario + console.log(ex.message); + this.corruptedPayload = true; + this.activeModel = new DiagramModel(); + } + + this.diagramEngine.setDiagramModel(this.activeModel); + this.diagramEngine.repaintCanvas(); + } + + registerCustomModels() { + this.diagramEngine.registerLinkFactory(new LinkFactory()); + this.diagramEngine.registerPortFactory( + new SimplePortFactory('email', config => new Email.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new Email.NodeFactory()); + + this.diagramEngine.registerPortFactory( + new SimplePortFactory('generic', config => new Generic.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new Generic.NodeFactory()); + + this.diagramEngine.registerPortFactory( + new SimplePortFactory('segment', config => new Segment.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new Segment.NodeFactory()); + + this.diagramEngine.registerPortFactory( + new SimplePortFactory('trigger', config => new Trigger.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new Trigger.NodeFactory()); + + this.diagramEngine.registerPortFactory( + new SimplePortFactory('before_trigger', config => new BeforeTrigger.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new BeforeTrigger.NodeFactory()); + + this.diagramEngine.registerPortFactory( + new SimplePortFactory('wait', config => new Wait.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new Wait.NodeFactory()); + + // Goal + this.diagramEngine.registerPortFactory( + new SimplePortFactory('goal', config => new Goal.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new Goal.NodeFactory()); + + // Banner + this.diagramEngine.registerPortFactory( + new SimplePortFactory('banner', config => new Banner.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new Banner.NodeFactory()); + + // Condition + this.diagramEngine.registerPortFactory( + new SimplePortFactory('condition', config => new Condition.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new Condition.NodeFactory()); + + // Notification + this.diagramEngine.registerPortFactory( + new SimplePortFactory('push_notification', config => new PushNotification.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new PushNotification.NodeFactory()); + + // AB Test + this.diagramEngine.registerPortFactory( + new SimplePortFactory('ab_test', config => new ABTest.PortModel()) + ); + this.diagramEngine.registerNodeFactory(new ABTest.NodeFactory()); + } + + getActiveDiagram() { + return this.activeModel; + } + + getDiagramEngine() { + return this.diagramEngine; + } + + isCorruptedPayload() { + return this.corruptedPayload; + } +} diff --git a/src/assets/scenario-builder/components/Notification.js b/src/assets/scenario-builder/components/Notification.js new file mode 100644 index 0000000..c1d3cea --- /dev/null +++ b/src/assets/scenario-builder/components/Notification.js @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Snackbar from '@material-ui/core/Snackbar'; +import SnackbarContent from '@material-ui/core/SnackbarContent'; +import CheckCircleIcon from '@material-ui/icons/CheckCircle'; +import ErrorIcon from '@material-ui/icons/Error'; +import InfoIcon from '@material-ui/icons/Info'; +import WarningIcon from '@material-ui/icons/Warning'; + +const variantIcon = { + success: CheckCircleIcon, + warning: WarningIcon, + error: ErrorIcon, + info: InfoIcon +}; + +class Notification extends React.Component { + render() { + const Icon = variantIcon[this.props.variant]; + + return ( + + + + {this.props.text} + + } + /> + + ); + } +} + +Notification.propTypes = { + variant: PropTypes.oneOf(['success', 'warning', 'info', 'error']).isRequired, + text: PropTypes.string.isRequired, + handleClose: PropTypes.func, + open: PropTypes.bool.isRequired +}; + +export default Notification; diff --git a/src/assets/scenario-builder/components/StatisticBadge.js b/src/assets/scenario-builder/components/StatisticBadge.js new file mode 100644 index 0000000..529c639 --- /dev/null +++ b/src/assets/scenario-builder/components/StatisticBadge.js @@ -0,0 +1,78 @@ +import React from "react"; +import {useSelector} from 'react-redux'; +import Chip from "@material-ui/core/Chip"; +import {CircularProgress} from "@material-ui/core"; + +function StatisticBadge(props) { + + const formatLabelNumbers = number => { + if (number < 1000) { + return number; + } + if (number < 1000000) { + return Number(number / 1000).toFixed((number > 100000 ? 0 : 1)) + 'K'; + } + + return Number(number / 1000000).toFixed(1) + 'M'; + }; + + const defaultTimePeriod = '24h'; + const statistics = useSelector(state => state.statistics.statistics); + const data = statistics[props.elementId] ?? null; + + let label = null; + if (statistics.length === 0) { + label =
; + } else { + if (data) { + if (data.hasOwnProperty('finished')) { + label = data.finished[defaultTimePeriod]; + } + + if (props.position === 'right' && data.hasOwnProperty('matched')) { + label = data.matched[defaultTimePeriod]; + } + + if (props.position === 'right' && data.hasOwnProperty('completed')) { + label = data.completed[defaultTimePeriod]; + } + + if (props.position === 'bottom' && data.hasOwnProperty('notMatched')) { + label = data.notMatched[defaultTimePeriod]; + } + + if (props.position === 'bottom' && data.hasOwnProperty('timeout')) { + label = data.timeout[defaultTimePeriod]; + } + + if (props.hasOwnProperty('index')) { + if (!data[props.index]) { + label = 0; + } else { + label = data[props.index][defaultTimePeriod]; + } + } + } + + if (label !== null) { + label=formatLabelNumbers(label); + } else { + return null; + } + } + + return ( +
+ +
+ ); +} + + +export default StatisticBadge; \ No newline at end of file diff --git a/src/assets/scenario-builder/components/StatisticTooltip.js b/src/assets/scenario-builder/components/StatisticTooltip.js new file mode 100644 index 0000000..9787d25 --- /dev/null +++ b/src/assets/scenario-builder/components/StatisticTooltip.js @@ -0,0 +1,141 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Popover from '@material-ui/core/Popover'; +import { connect } from 'react-redux'; + +class StatisticsTooltip extends React.Component { + static propTypes = { + id: PropTypes.string.isRequired, + anchorElement: PropTypes.instanceOf(Element) + }; + + render() { + const { anchorElement } = this.props; + const data = this.props.statistics[this.props.id] ?? null; + const variants = this.props.variants ?? []; + + if (data === null) { + return null; + } + + return ( + +
+ {data ? +
+ Statistics +
+ + {data.hasOwnProperty('waiting') ? +
+ Waiting: {data.waiting} +
: null + } + + {data.hasOwnProperty('recheck') ? +
+ Waiting to re-check: {data.recheck} +
: null + } + + Last 24 hours
+ + + {data.hasOwnProperty('finished') ? + + + + : null} + {data.hasOwnProperty('matched') ? + + + + : null} + {data.hasOwnProperty('notMatched') ? + + + + : null} + {data.hasOwnProperty('completed') ? + + + + : null} + {data.hasOwnProperty('timeout') ? + + + + : null} + {variants.flatMap((variant) => ( + + + + + ))} + +
Finished:{data.finished["24h"]}
Matched:{data.matched["24h"]}
Not matched:{data.notMatched["24h"]}
Completed:{data.completed["24h"]}
Timed out:{data.timeout["24h"]}
{variant.name}:{data[variant.code] ? data[variant.code]["24h"] : 0}
+ + Last 30 days
+ + + + {data.hasOwnProperty('finished') ? + + + + : null} + {data.hasOwnProperty('matched') ? + + + + : null} + {data.hasOwnProperty('notMatched') ? + + + + : null} + {data.hasOwnProperty('completed') ? + + + + : null} + {data.hasOwnProperty('timeout') ? + + + + : null} + {variants.flatMap((variant) => ( + + + + + ))} + +
Finished:{data.finished["30d"]}
Matched:{data.matched["30d"]}
Not matched:{data.notMatched["30d"]}
Completed:{data.completed["30d"]}
Timed out:{data.timeout["30d"]}
{variant.name}:{data[variant.code] ? data[variant.code]["30d"] : 0}
+
+ : ''} +
+
+ ); + } +} + +function mapStateToProps(state) { + return { + statistics: state.statistics.statistics + }; +} + +export default connect(mapStateToProps)(StatisticsTooltip); diff --git a/src/assets/scenario-builder/components/elements/ABTest/NodeFactory.js b/src/assets/scenario-builder/components/elements/ABTest/NodeFactory.js new file mode 100755 index 0000000..f0d9dd0 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/ABTest/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('ab_test'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/ABTest/NodeModel.js b/src/assets/scenario-builder/components/elements/ABTest/NodeModel.js new file mode 100755 index 0000000..a1f65b3 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/ABTest/NodeModel.js @@ -0,0 +1,46 @@ +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import * as _ from 'lodash'; +import { PortModel } from './PortModel'; +import uuidv4 from 'uuid/v4'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('ab_test', element.id); + + this.name = element.name; + this.scenarioName = element.scenarioName; + + if (element.variants) { + this.variants = element.variants; + } else { + this.variants = [{ + code: uuidv4().slice(0, 6), + name: 'Variant A', + distribution: 50, + }, { + code: uuidv4().slice(0, 6), + name: 'Variant B', + distribution: 50, + }]; + } + + this.addPort(new PortModel('left')); + + this.variants.forEach((item, index) => + this.addPort(new PortModel('right.' + index)) + ); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.variants = ob.variants; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + variants: this.variants, + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/ABTest/NodeWidget.js b/src/assets/scenario-builder/components/elements/ABTest/NodeWidget.js new file mode 100755 index 0000000..8315ec3 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/ABTest/NodeWidget.js @@ -0,0 +1,247 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import SwapVertIcon from '@material-ui/icons/SwapVert'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import { PortWidget } from './../../widgets/PortWidget'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import DialogContentText from "@material-ui/core/DialogContentText"; +import VariantBuilder from "./VariantBuilder"; +import {Typography} from "@material-ui/core"; +import {PortModel} from './PortModel'; +import StatisticBadge from "../../StatisticBadge"; +import StatisticsTooltip from "../../StatisticTooltip"; + +class NodeWidget extends React.Component { + + constructor(props) { + super(props); + + // Use it to access VariantBuilder state + this.builderRef = React.createRef(); + + this.state = { + nodeFormName: this.props.node.name, + enabledSave: true, + dialogOpened: false, + anchorElementForTooltip: null, + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + enableSave = enable => { + // prevents re-rendering, setState only if value differs + if (this.state.enabledSave !== enable) { + this.setState( {enabledSave: enable }); + } + }; + + syncNodeModel = () => { + + // Add ports if is not in variants + this.props.node.variants.forEach((variant, index) => { + if (!this.props.node.getPort('right.' + index)) { + this.props.node.addPort(new PortModel('right.' + index)); + } + }); + + // Remove ports if is not in variants + for (const portName in this.props.node.getPorts()) { + if (portName.startsWith('right.')) { + let index = portName.split('.')[1]; + if (!this.props.node.variants[index]) { + let port = this.props.node.getPort(portName); + + for (const [link] of Object.entries(port.getLinks())) { + this.props.diagramEngine.diagramModel.removeLink(link); + } + this.props.node.removePort(port); + } + } + } + }; + + render() { + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ {this.props.node.name + ? this.props.node.name + : 'AB Test'} +
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+ {this.props.node.variants.flatMap((variant, index) => ( +
+ +
+ + + {variant.name} ({variant.distribution}%) + +
+
+ ))} +
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + > + AB Test + + + + + A/B testing is comparing two versions of either a webpage, email campaign or an aspect in a scenario to evaluate which performs best. + With the different variants shown to your customers, you can determine which version is the most effective. + + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + + + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + const { dispatch } = state; + return { dispatch }; +} + +export default connect(mapStateToProps)(NodeWidget); \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/ABTest/PortModel.js b/src/assets/scenario-builder/components/elements/ABTest/PortModel.js new file mode 100755 index 0000000..83f6a94 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/ABTest/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightBottomPort } from './../Ports'; + +export class PortModel extends LeftRightBottomPort { + constructor(position = 'right') { + super(position, 'ab_test'); + } +} diff --git a/src/assets/scenario-builder/components/elements/ABTest/VariantBuilder.js b/src/assets/scenario-builder/components/elements/ABTest/VariantBuilder.js new file mode 100644 index 0000000..a0a082a --- /dev/null +++ b/src/assets/scenario-builder/components/elements/ABTest/VariantBuilder.js @@ -0,0 +1,294 @@ +import React, {forwardRef, useImperativeHandle, useReducer} from "react"; +import Grid from "@material-ui/core/Grid"; +import { + Dialog, DialogActions, DialogContent, DialogTitle, + FormControl, + IconButton, + Input, + InputAdornment, + InputLabel, + TextField +} from "@material-ui/core"; +import AddCircle from '@material-ui/icons/AddCircleOutline'; +import DeleteIcon from "@material-ui/icons/Delete"; +import Button from "@material-ui/core/Button"; +import AddIcon from "@material-ui/icons/AddCircleOutline"; +import EditIcon from '@material-ui/icons/Edit'; +import ShowIcon from '@material-ui/icons/Visibility'; +import uuidv4 from 'uuid/v4'; +import DialogContentText from "@material-ui/core/DialogContentText"; +import * as config from '../../../config'; + +function reducer(state, action) { + + let isError = false; + + switch (action.type) { + case 'ADD_VARIANT': + return { + ...state, + variants: [ + ...state.variants, { + "code": uuidv4().slice(0, 6), + "name": "Variant " + String.fromCharCode(65 + state.variants.length), + "distribution": 0 + } + ] + }; + + case 'DELETE_VARIANT': + let variants = state.variants.filter((element, index) => index !== action.payload.index); + if (variants.reduce((sum, item) => sum + parseInt(item.distribution), 0) !== 100) { + isError = true; + } + + return { + ...state, + variants: variants, + isError: isError + }; + + case 'UPDATE_VARIANT_DISTRIBUTION': + let filteredVariants = state.variants.filter((element, index) => index !== action.payload.index); + isError = (filteredVariants.reduce((sum, item) => sum + parseInt(item.distribution), 0) + parseInt(action.payload.value)) !== 100; + + return { + ...state, + variants: state.variants.map((element, index) => { + if (index === action.payload.index) { + return { + ...element, + distribution: action.payload.value + } + } + return element; + }), + isError: isError, + }; + + case 'UPDATE_VARIANT_NAME': + return { + ...state, + variants: state.variants.map((element, index) => { + if (index === action.payload.index) { + return { + ...element, + name: action.payload.value + } + } + return element; + }) + }; + + case 'UPDATE_SEGMENT_NAME': + return { + ...state, + variants: state.variants.map((element, index) => { + if (index === action.payload.index) { + return { + ...element, + segment: { + name: action.payload.name + } + } + } + return element; + }) + }; + + default: + throw new Error("unsupported action type " + action.type); + } +} + +function VariantBuilder(props, ref) { + const [state, dispatch] = useReducer(reducer, { + ...props + }); + + if (state.isError !== undefined) { + props.onEnableSave(!state.isError); + } + + // expose state to outer node + useImperativeHandle(ref, () => ({ + state: state + })); + + return ( +
+ {state.variants.flatMap((variant, index) => ( + + + + + + + dispatch({type: 'UPDATE_VARIANT_NAME', payload: {index: index, value: element.target.value}})} + /> + + + + + Distribution + dispatch({type: 'UPDATE_VARIANT_DISTRIBUTION', payload: {index: index, value: element.target.value}})} + endAdornment={ + % + } + /> + + + + + {index > 1 && + {dispatch({type: 'DELETE_VARIANT', payload: {index: index}})}} + size="small" + aria-label="delete" + > + + + } + + + + + + + + + + ))} + + + + + + +
+); +} + +function SegmentForm(props) { + + const defaultSegmentName = "Scenario variant: " + props.scenarioName + " - " + props.nodeName + " - " + props.variantName + " (" + props.variantCode + ")"; + + const [open, setOpen] = React.useState(false); + const [name, setName] = React.useState(props.segment.name ?? defaultSegmentName); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleCancel = () => { + setName(props.segment.name ?? defaultSegmentName); + setOpen(false); + }; + + const handleSave = () => { + props.dispatch({ + type: 'UPDATE_SEGMENT_NAME', + payload: { + index: props.index, + name: name + } + }); + + setOpen(false); + }; + + return ( + + + + + + {props.segment.name ? 'Edit segment' : 'Create segment'} + + + To create associated segment to variant, please enter segment name. + + { setName(e.target.value)}} + fullWidth + /> + + {props.segment.code ? +
+ + +
: ''} +
+ + + + + +
+
+ ); +} + +// // forwardRef is here used to access local state from parent node +export default forwardRef(VariantBuilder) \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/ABTest/index.js b/src/assets/scenario-builder/components/elements/ABTest/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/ABTest/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/ABTest/sass/style.scss b/src/assets/scenario-builder/components/elements/ABTest/sass/style.scss new file mode 100644 index 0000000..f41aa46 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/ABTest/sass/style.scss @@ -0,0 +1,43 @@ +$nodeColor: $grey; + +.srd-node--selected { + .diamond-node .abtest-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.abtest-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + height: auto; + } + + &__title { + color: $nodeColor; + } + + &__ports { + width: 50%; + height: 100%; + flex-direction: column; + } + + &__right { + margin-top: 10px; + height: 20px; + } + + &__description { + display: flex; + position: absolute; + left: 40px; + top: -2px; + font-size: 0.8rem; + color: $black; + } + + .port { + box-shadow: 0 0 10px $nodeColor; + } +} diff --git a/src/assets/scenario-builder/components/elements/Banner/NodeFactory.js b/src/assets/scenario-builder/components/elements/Banner/NodeFactory.js new file mode 100755 index 0000000..203ebdd --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Banner/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('banner'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Banner/NodeModel.js b/src/assets/scenario-builder/components/elements/Banner/NodeModel.js new file mode 100755 index 0000000..85f6fa6 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Banner/NodeModel.js @@ -0,0 +1,36 @@ +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import * as _ from 'lodash'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('banner', element.id); + + this.name = element.name; + this.selectedBanner = element.selectedBanner; + + this.expiresInTime = element.expiresInTime; + this.expiresInUnit = element.expiresInUnit; + + this.addPort(new PortModel('left')); + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.selectedBanner = ob.selectedBanner; + + this.expiresInTime = ob.expiresInTime || '1'; + this.expiresInUnit = ob.expiresInUnit || 'days'; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + selectedBanner: this.selectedBanner, + expiresInTime: this.expiresInTime, + expiresInUnit: this.expiresInUnit, + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/Banner/NodeWidget.js b/src/assets/scenario-builder/components/elements/Banner/NodeWidget.js new file mode 100755 index 0000000..b460dcb --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Banner/NodeWidget.js @@ -0,0 +1,265 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import ActionIcon from '@material-ui/icons/Adjust'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import FormControl from '@material-ui/core/FormControl'; +import InputLabel from '@material-ui/core/InputLabel'; +import Select from '@material-ui/core/Select'; +import MenuItem from '@material-ui/core/MenuItem'; + +import { PortWidget } from '../../widgets/PortWidget'; +import StatisticsTooltip from '../../StatisticTooltip'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import StatisticBadge from "../../StatisticBadge"; +import {Autocomplete} from "@material-ui/lab"; + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + this.state = { + nodeFormName: this.props.node.name, + selectedBanner: this.props.node.selectedBanner, + dialogOpened: false, + anchorElementForTooltip: null, + expiresInTime: this.props.node.expiresInTime, + expiresInUnit: this.props.node.expiresInUnit, + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + getSelectedBanner = () => { + const selected = this.props.banners.find( + banner => banner.id === this.state.selectedBanner + ); + + return selected ? selected : null; + }; + + getSelectedBannerValue = () => { + const selected = this.props.banners.find( + banner => banner.id === this.props.node.selectedBanner + ); + + return selected ? ` - ${selected.name}` : ''; + }; + + render() { + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ +
+ +
+
+ +
+
+ + +
+
+
+
+
+ {this.props.node.name + ? this.props.node.name + : `Banner ${this.getSelectedBannerValue()}`} +
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + fullWidth + > + Banner node + + + Shows a one-time banner to user. + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + option.name} + disableClearable={true} + onChange={(event, selectedOption) => { + if (selectedOption !== null) { + this.setState({ + selectedBanner: selectedOption.id + }); + } + }} + renderInput={params => ( + + )} + /> + + + + + + { + this.setState({ + expiresInTime: event.target.value + }); + }} + /> + + + + Time unit + + + + + + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + return { + banners: state.banners.availableBanners + }; +} + +export default connect(mapStateToProps)(NodeWidget); diff --git a/src/assets/scenario-builder/components/elements/Banner/PortModel.js b/src/assets/scenario-builder/components/elements/Banner/PortModel.js new file mode 100755 index 0000000..e9526a8 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Banner/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightPort } from '../Ports'; + +export class PortModel extends LeftRightPort { + constructor(position = 'left') { + super(position, 'banner'); + } +} diff --git a/src/assets/scenario-builder/components/elements/Banner/index.js b/src/assets/scenario-builder/components/elements/Banner/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Banner/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/Banner/sass/style.scss b/src/assets/scenario-builder/components/elements/Banner/sass/style.scss new file mode 100644 index 0000000..b6ccd50 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Banner/sass/style.scss @@ -0,0 +1,30 @@ +$nodeColor: $violet; + +.srd-node--selected { + .square-node.banner-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.banner-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} diff --git a/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeFactory.js b/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeFactory.js new file mode 100755 index 0000000..2ec72b1 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('before_trigger'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeModel.js b/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeModel.js new file mode 100755 index 0000000..009499f --- /dev/null +++ b/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeModel.js @@ -0,0 +1,33 @@ +import * as _ from 'lodash'; +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('before_trigger', element.id); + + this.name = element.name; + this.selectedTrigger = element.selectedTrigger; + this.time = element.time !== undefined ? element.time : 10; + this.timeUnit = element.timeUnit !== undefined ? element.timeUnit : 'hours'; + + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.selectedTrigger = ob.selectedTrigger; + this.time = ob.time; + this.timeUnit = ob.timeUnit; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + selectedTrigger: this.selectedTrigger, + time: this.time, + timeUnit: this.timeUnit + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeWidget.js b/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeWidget.js new file mode 100755 index 0000000..7e6f74d --- /dev/null +++ b/src/assets/scenario-builder/components/elements/BeforeTrigger/NodeWidget.js @@ -0,0 +1,271 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import NotificationsActiveIcon from '@material-ui/icons/NotificationsActive'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import { PortWidget } from '../../widgets/PortWidget'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import StatisticsTooltip from '../../StatisticTooltip'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import FormControl from "@material-ui/core/FormControl"; +import InputLabel from "@material-ui/core/InputLabel"; +import Select from "@material-ui/core/Select"; +import MenuItem from "@material-ui/core/MenuItem"; +import StatisticBadge from "../../StatisticBadge"; + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + + this.state = { + nodeFormName: this.props.node.name, + selectedTrigger: this.props.node.selectedTrigger, + nodeFormBeforeTime: this.props.node.time, + timeUnit: this.props.node.timeUnit, + dialogOpened: false, + anchorElementForTooltip: null + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + nodeFormBeforeTime: this.props.node.time, + timeUnit: this.props.node.timeUnit, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + getTriggersInSelectableFormat = () => { + return this.props.beforeTriggers.map(trigger => { + return { + value: trigger.code, + label: trigger.name + }; + }); + }; + + getSelectedTriggerValue = () => { + const selected = this.getTriggersInSelectableFormat().find( + trigger => trigger.value === this.props.node.selectedTrigger + ); + + return selected ? `${this.props.node.time} ${this.props.node.timeUnit} before - ${selected.label} event` : 'Before Event'; + }; + + render() { + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ +
+ +
+
+ + +
+
+
+ +
+
+ {this.props.node.name + ? this.props.node.name + : this.getSelectedTriggerValue()} +
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + > + Before event node + + + + Events are emitted in advanced of trigger according to selected time period. + + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + option.value === this.state.selectedTrigger + )} + options={this.getTriggersInSelectableFormat()} + getOptionLabel={(option) => option.label} + style={{ marginBottom: 16 }} + onChange={(event, selectedOption) => { + if (selectedOption !== null) { + this.setState({ + selectedTrigger: selectedOption.value + }); + } + }} + renderInput={params => ( + + )} + /> + + + + + + { + this.setState({ + nodeFormBeforeTime: event.target.value + }); + }} + /> + + + + Time unit + + + + + + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + const { beforeTriggers } = state; + + return { + beforeTriggers: beforeTriggers.availableBeforeTriggers + }; +} + +export default connect(mapStateToProps)(NodeWidget); diff --git a/src/assets/scenario-builder/components/elements/BeforeTrigger/PortModel.js b/src/assets/scenario-builder/components/elements/BeforeTrigger/PortModel.js new file mode 100755 index 0000000..23d7f82 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/BeforeTrigger/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightPort } from '../Ports'; + +export class PortModel extends LeftRightPort { + constructor(position = 'left') { + super(position, 'trigger_before'); + } +} diff --git a/src/assets/scenario-builder/components/elements/BeforeTrigger/index.js b/src/assets/scenario-builder/components/elements/BeforeTrigger/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/BeforeTrigger/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/BeforeTrigger/sass/style.scss b/src/assets/scenario-builder/components/elements/BeforeTrigger/sass/style.scss new file mode 100644 index 0000000..575ecfb --- /dev/null +++ b/src/assets/scenario-builder/components/elements/BeforeTrigger/sass/style.scss @@ -0,0 +1,30 @@ +$nodeColor: $teal; + +.srd-node--selected { + .square-node.before-trigger-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.before-trigger-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} diff --git a/src/assets/scenario-builder/components/elements/Condition/CriteriaBuilder.js b/src/assets/scenario-builder/components/elements/Condition/CriteriaBuilder.js new file mode 100755 index 0000000..8530e32 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Condition/CriteriaBuilder.js @@ -0,0 +1,223 @@ +import React, { useImperativeHandle, useReducer, useContext, forwardRef } from 'react'; +import { useSelector } from 'react-redux'; +import Button from '@material-ui/core/Button'; +import Grid from '@material-ui/core/Grid'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import DeleteIcon from '@material-ui/icons/Delete'; +import AddIcon from '@material-ui/icons/AddCircleOutline'; +import { Card, CardContent, FormControl, InputLabel, Select, MenuItem, IconButton } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import StringLabeledArrayParam from '../params/StringLabeledArrayParam'; +import BooleanParam from '../params/BooleanParam'; +import NumberParam from '../params/NumberParam'; +import TimeframeParam from '../params/TimeframeParam'; +import { emptyNode, reducer, actionSetEvent, actionSetKeyForNode, actionAddCriterion, actionDeleteNode } from './criteriaReducer'; + +const BuilderDispatch = React.createContext(null); + +//////////////////// +// CriterionParam +//////////////////// + +// Props - node, blueprint +function CriterionParam(props) { + const dispatch = useContext(BuilderDispatch); + + let param = props.node.params.filter(param => param.key === props.blueprint.key)[0]; + // input name identifying both criterion (using artifically generated ID) and its param (using param's key) + let name = [props.node.id, param.key]; + + switch (props.blueprint.type) { + case 'string_labeled_array': + return (); + case 'boolean': + return (); + case 'number': + return (); + case 'timeframe': + return (); + default: + throw new Error("unsupported node type " + props.blueprint.type); + } +} + +// Props - node, blueprint +function CriterionParams(props) { + return ( + <> + {props.blueprint.map(paramBlueprint => ( + // key is required by React here (not used in CriterionParam) + + ))} + + ) +} + +//////////////////// +// CriteriaForm +//////////////////// + +const useCriteriaFormStyles = makeStyles({ + cardContent: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-end', + backgroundColor: '#F2F2F2', + }, + formControl: { + minWidth: '180px', + }, +}); + + +// Props - node, criteriaBlueprint +function CriteriaForm(props) { + const classes = useCriteriaFormStyles(); + const dispatch = useContext(BuilderDispatch); + + return ( + + + + Criterion + + + dispatch(actionDeleteNode(props.node.id))} + size="small" + className={classes.icon} + aria-label="delete"> + + + + + { props.node.key && + + cr.key === props.node.key)[0].params}> + + + } + + ); +} + +//////////////////// +// CriteriaTable +//////////////////// + +const useCriteriaTableStyles = makeStyles({ + andContainer: { + display: 'flex', + justifyContent: 'center', + flexGrow: 0, + maxWidth: '100%', + flexBasis: '100%' + } +}); + +// Props - criteriaBlueprint, nodes +function CriteriaTable(props) { + const classes = useCriteriaTableStyles(); + const dispatch = useContext(BuilderDispatch); + + return ( + <> + {props.nodes.map((node, index) => ( + + { index >= 1 && +
AND
+ } + + + + +
+ ))} + + + + + + ) +} + +//////////////////// +// CriteriaBuilder +//////////////////// + +const useCriteriaBuilderStyles = makeStyles({ + selectedButton: { + backgroundColor: "#E4E4E4" + }, + deselectedButton: { + color: "#A6A6A6" + } +}); + +function CriteriaBuilder(props, ref) { + const classes = useCriteriaBuilderStyles(); + const criteria = useSelector(state => state.criteria.criteria); + + const [state, dispatch] = useReducer(reducer, { + version: 1, + event: criteria[0].event, + nodes: [emptyNode()] // by default, one empty node + , ...props.conditions}); + + // expose state to outer node + useImperativeHandle(ref, () => ({ + state: state + })); + + return ( + + + + + {criteria.map(criteriaBlueprint => ( + + ))} + + + + {criteria.filter(cb => cb.event === state.event).map(criteriaBlueprint => ( + + ) + )} + + + ) +} + +// forwardRef is here used to access local state from parent node +export default forwardRef(CriteriaBuilder) diff --git a/src/assets/scenario-builder/components/elements/Condition/NodeFactory.js b/src/assets/scenario-builder/components/elements/Condition/NodeFactory.js new file mode 100755 index 0000000..8d7d7aa --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Condition/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('condition'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Condition/NodeModel.js b/src/assets/scenario-builder/components/elements/Condition/NodeModel.js new file mode 100755 index 0000000..2361c79 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Condition/NodeModel.js @@ -0,0 +1,29 @@ +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import * as _ from 'lodash'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('condition', element.id); + + this.name = element.name; + this.conditions = element.conditions; + + this.addPort(new PortModel('left')); + this.addPort(new PortModel('bottom')); + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.conditions = ob.conditions; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + conditions: this.conditions, + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/Condition/NodeWidget.js b/src/assets/scenario-builder/components/elements/Condition/NodeWidget.js new file mode 100755 index 0000000..0108e26 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Condition/NodeWidget.js @@ -0,0 +1,210 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import ConditionIcon from '@material-ui/icons/CallSplit'; +import OkIcon from '@material-ui/icons/Check'; +import Grid from '@material-ui/core/Grid'; +import NopeIcon from '@material-ui/icons/Close'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import CriteriaBuilder from './CriteriaBuilder'; +import { PortWidget } from '../../widgets/PortWidget'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import StatisticBadge from "../../StatisticBadge"; +import StatisticsTooltip from "../../StatisticTooltip"; + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + + // Use it to access CriteriaBuilder state + this.builderRef = React.createRef(); + + this.state = { + nodeFormName: this.props.node.name, + dialogOpened: false, + anchorElementForTooltip: null + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + + render() { + let displayStatisticBadge = false; + if (this.props.statistics.length === 0 || this.props.statistics[this.props.node.id]) { + displayStatisticBadge = true; + } + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ {this.props.node.name + ? this.props.node.name + : 'Condition'} +
+
+ +
+
+ +
+ +
+
+ +
+ +
+ + {displayStatisticBadge ? + : + + } +
+ +
+ + {displayStatisticBadge ? + : + + } +
+
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + > + + Event Condition + + + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + const { dispatch } = state; + return { + dispatch, + statistics: state.statistics.statistics + }; +} + +export default connect(mapStateToProps)(NodeWidget); \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/Condition/PortModel.js b/src/assets/scenario-builder/components/elements/Condition/PortModel.js new file mode 100755 index 0000000..67656db --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Condition/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightBottomPort } from './../Ports'; + +export class PortModel extends LeftRightBottomPort { + constructor(position = 'top') { + super(position, 'condition'); + } +} diff --git a/src/assets/scenario-builder/components/elements/Condition/criteriaReducer.js b/src/assets/scenario-builder/components/elements/Condition/criteriaReducer.js new file mode 100644 index 0000000..42e6a52 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Condition/criteriaReducer.js @@ -0,0 +1,122 @@ +import uuidv4 from 'uuid/v4'; + +/////////////////////////// +// local reducer and state for criteria builder +/////////////////////////// + +export function emptyNode() { + return { + id: uuidv4(), + key: '', + params: [], + }; +} + +export function actionSetKeyForNode(nodeId, criterionKey, criterionParams) { + return { + type: 'SET_KEY_FOR_NODE', + payload: { + key: criterionKey, + nodeId: nodeId, + // Criterion params are associated with key, + // but since we do not have access to blueprint here, request criterionParams as additional parameter + params: criterionParams + } + }; +} + +export function actionDeleteNode(nodeId) { + return { + type: 'DELETE_NODE', + payload: { + nodeId: nodeId, + } + }; +} + +export function actionAddCriterion() { + return { + type: 'ADD_CRITERION' + }; +} + +export function actionSetEvent(event) { + return { + type: 'SET_EVENT', + payload: event + }; +} + +export function reducer(state, action) { + switch (action.type) { + // params actions + case 'UPDATE_PARAM_VALUES': { + let [nodeId, paramKey] = action.payload.name; + return { + ...state, nodes: state.nodes.map(node => { + if (node.id === nodeId) { + return { + ...node, params: node.params.map(param => { + if (param.key === paramKey) { + return {...param, values: Object.assign(param.values, action.payload.values)}; + } + return param; + }) + }; + } + return node; + }) + }; + } + case 'SET_PARAM_VALUES': { + let [nodeId, paramKey] = action.payload.name; + return { + ...state, nodes: state.nodes.map(node => { + if (node.id === nodeId) { + return { + ...node, params: node.params.map(param => { + if (param.key === paramKey) { + return {...param, values: action.payload.values}; + } + return param; + }) + }; + } + return node; + }) + }; + } + + // internal criteriaReducer actions + case 'SET_EVENT': + // this also resets nodes state + return { + ...state, nodes: [emptyNode()], event: action.payload + }; + case 'ADD_CRITERION': + return { + ...state, nodes: [...state.nodes, emptyNode()] + }; + case 'DELETE_NODE': + return { + ...state, nodes: state.nodes.filter(n => n.id !== action.payload.nodeId) + }; + case 'SET_KEY_FOR_NODE': { + let newNodes = state.nodes.map(node => { + if (action.payload.nodeId === node.id) return { + id: node.id, + key: action.payload.key, + // TODO: load params from blueprint without needing them in a payload (since they are associated with a criteria key) + params: action.payload.params, + }; + return node; + }); + return { + ...state, nodes: newNodes + }; + } + + default: + throw new Error("unsupported action type " + action.type); + } +} diff --git a/src/assets/scenario-builder/components/elements/Condition/index.js b/src/assets/scenario-builder/components/elements/Condition/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Condition/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/Condition/sass/style.scss b/src/assets/scenario-builder/components/elements/Condition/sass/style.scss new file mode 100644 index 0000000..0f9b40a --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Condition/sass/style.scss @@ -0,0 +1,31 @@ +$nodeColor: $orange; + +.srd-node--selected { + .diamond-node.condition-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.condition-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right, + &__bottom { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} diff --git a/src/assets/scenario-builder/components/elements/Email/NodeFactory.js b/src/assets/scenario-builder/components/elements/Email/NodeFactory.js new file mode 100755 index 0000000..e0c9d71 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Email/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('email'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Email/NodeModel.js b/src/assets/scenario-builder/components/elements/Email/NodeModel.js new file mode 100755 index 0000000..501c9a0 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Email/NodeModel.js @@ -0,0 +1,27 @@ +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import * as _ from 'lodash'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('email', element.id); + + this.name = element.name; + this.selectedMail = element.selectedMail; + this.addPort(new PortModel('left')); + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.selectedMail = ob.selectedMail; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + selectedMail: this.selectedMail, + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/Email/NodeWidget.js b/src/assets/scenario-builder/components/elements/Email/NodeWidget.js new file mode 100755 index 0000000..dbb0fb1 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Email/NodeWidget.js @@ -0,0 +1,263 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import ActionIcon from '@material-ui/icons/Mail'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import {styled} from '@material-ui/core/styles'; +import { PortWidget } from '../../widgets/PortWidget'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import StatisticBadge from "../../StatisticBadge"; +import StatisticsTooltip from "../../StatisticTooltip"; +import {Autocomplete} from "@material-ui/lab"; +import {withStyles} from "@material-ui/core"; +import {createFilterOptions} from "@material-ui/lab/Autocomplete"; + +const PreviewEmailButton = styled(Button)({ + marginRight: 'auto' +}); + +const styles = theme => ({ + autocomplete: { + margin: theme.spacing(1) + }, + subtitle: { + paddingLeft: '6px', + color: theme.palette.grey[600] + }, +}); + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + this.state = { + nodeFormName: this.props.node.name, + selectedMail: this.props.node.selectedMail, + dialogOpened: false, + anchorElementForTooltip: null + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + selectedMail: this.props.node.selectedMail, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + getSelectedMail = () => { + const selected = this.props.mails.find( + mail => mail.code === this.state.selectedMail + ); + + return selected ? selected : null; + }; + + getSelectedMailValue = () => { + const selected = this.props.mails.find( + mail => mail.code === this.props.node.selectedMail + ); + + return selected ? ` - ${selected.name}` : ''; + }; + + filterOptions = () => createFilterOptions({ + matchFrom: 'any', + trim: true, + ignoreAccents: true, + ignoreCase: true, + stringify: option => { + return option.name + " " + option.code; + }, + }); + + render() { + const {classes} = this.props; + + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ +
+ +
+
+ +
+
+ + +
+
+
+
+
+ {this.props.node.name + ? this.props.node.name + : `Mail ${this.getSelectedMailValue()}`} +
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + fullWidth + > + Email node + + + Sends an email to user. + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + option.name} + disableClearable={true} + filterOptions={this.filterOptions()} + groupBy={(option) => option.mail_type.code} + onChange={(event, selectedOption) => { + if (selectedOption !== null) { + this.setState({ + selectedMail: selectedOption.code + }) + } + }} + renderInput={params => ( + + )} + renderOption={(option, { selected }) => ( +
+ {option.name} + ({option.code}) +
+ )} + /> +
+
+
+ + + {this.props.mails.filter(mail => mail.link && mail.code === this.state.selectedMail).map(item => + + Preview + + )} + + + + + +
+
+ ); + } +} + +function mapStateToProps(state) { + return { + mails: state.mails.availableMails + }; +} + +export default connect(mapStateToProps)( + withStyles(styles)(NodeWidget) +); diff --git a/src/assets/scenario-builder/components/elements/Email/PortModel.js b/src/assets/scenario-builder/components/elements/Email/PortModel.js new file mode 100755 index 0000000..39b38ec --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Email/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightPort } from '../Ports'; + +export class PortModel extends LeftRightPort { + constructor(position = 'left') { + super(position, 'email'); + } +} diff --git a/src/assets/scenario-builder/components/elements/Email/index.js b/src/assets/scenario-builder/components/elements/Email/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Email/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/Email/sass/style.scss b/src/assets/scenario-builder/components/elements/Email/sass/style.scss new file mode 100644 index 0000000..f18f6d6 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Email/sass/style.scss @@ -0,0 +1,30 @@ +$nodeColor: $lightViolet; + +.srd-node--selected { + .square-node.email-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.email-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/Generic/NodeFactory.js b/src/assets/scenario-builder/components/elements/Generic/NodeFactory.js new file mode 100644 index 0000000..a59609b --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Generic/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('generic'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ) + } + + getNewInstance() { + return new NodeModel(); + } +} \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/Generic/NodeModel.js b/src/assets/scenario-builder/components/elements/Generic/NodeModel.js new file mode 100644 index 0000000..a53b809 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Generic/NodeModel.js @@ -0,0 +1,30 @@ +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import * as _ from 'lodash'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('generic', element.id); + + this.name = element.name; + this.selectedGeneric = element.selectedGeneric; + this.options = element.options; + this.addPort(new PortModel('left')); + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.selectedGeneric = ob.selectedGeneric; + this.options = ob.options; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + selectedGeneric: this.selectedGeneric, + options: this.options, + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/Generic/NodeWidget.js b/src/assets/scenario-builder/components/elements/Generic/NodeWidget.js new file mode 100644 index 0000000..d25937a --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Generic/NodeWidget.js @@ -0,0 +1,303 @@ +import * as React from 'react'; +import * as _ from 'lodash'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import ActionIcon from '@material-ui/icons/Extension'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +import { PortWidget } from "../../widgets/PortWidget"; +import StatisticsTooltip from '../../StatisticTooltip'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import { setCanvasZoomingAndPanning } from "../../../actions"; +import { withStyles } from '@material-ui/core/styles'; +import { createFilterOptions } from '@material-ui/lab/Autocomplete'; +import OptionsForm from "./OptionsForm"; +import StatisticBadge from "../../StatisticBadge"; + +const styles = theme => ({ + autocomplete: { + margin: theme.spacing(1) + }, + subtitle: { + paddingLeft: '6px', + color: theme.palette.grey[600] + }, +}); + +const filterOptions = createFilterOptions({ + matchFrom: 'any', + trim: true, + ignoreAccents: true, + ignoreCase: true, + stringify: option => { + return option.label + " " + option.value; + }, +}); + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + + // Use it to access CriteriaBuilder state + this.optionsFormRef = React.createRef(); + + this.state = { + nodeFormName: this.props.node.name, + selectedGeneric: this.props.node.selectedGeneric, + dialogOpened: false, + anchorElementForTooltip: null + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + getSelectedGeneric = () => { + const match = this.props.generics.find(generic => { + return generic.code === this.state.selectedGeneric; + }); + + return match ? match : null; + }; + + getSelectedGenericOptionBlueprints = () => { + const generic = this.getSelectedGeneric(); + + let blueprints = []; + if (generic !== null && generic.options !== null) { + _.forOwn(generic.options, function(value, key) { + blueprints.push({ + key: key, + blueprint: value + }); + }); + } + + return blueprints; + }; + + // maybe refactor to more effective way if is a problem + transformOptionsForSelect = () => { + const generics = []; + + Object.keys(this.props.generics).forEach(key => { + generics.push({ + value: this.props.generics[key].code, + label: this.props.generics[key].label + }); + }); + + return generics; + }; + + getSelectedGenericDefaultLabel = () => { + const generic = this.getSelectedGeneric(); + return generic ? ` - ${generic.label}` : ''; + }; + + render() { + const { classes } = this.props; + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ +
+ +
+
+ +
+
+ + +
+
+
+
+
+ {this.props.node.name + ? this.props.node.name + : `Generic ${this.getSelectedGenericDefaultLabel()}`} +
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + fullWidth + > + Generic action node + + + Runs defined generic action. + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + option.label} + getOptionSelected={(option, value) => option.key === value.key} + disableClearable={true} + filterOptions={filterOptions} + onChange={(event, selectedOption) => { + if (selectedOption !== null) { + this.setState({ + selectedGeneric: selectedOption.value + }); + } + }} + renderInput={params => ( + + )} + renderOption={(option, { selected }) => ( +
+ {option.label} + ({option.value}) +
+ )} + /> +
+
+ + {this.state.selectedGeneric && this.getSelectedGenericOptionBlueprints().length > 0 && + + +

Options

+ +
+
+ } +
+ + + + + + +
+
+ ); + } +} + +NodeWidget.propTypes = { + classes: PropTypes.object.isRequired, +}; + +function mapStateToProps(state) { + return { + generics: state.generics.generics, + }; +} + +export default connect(mapStateToProps)( + withStyles(styles)(NodeWidget) +); diff --git a/src/assets/scenario-builder/components/elements/Generic/OptionsForm.js b/src/assets/scenario-builder/components/elements/Generic/OptionsForm.js new file mode 100644 index 0000000..7f781f1 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Generic/OptionsForm.js @@ -0,0 +1,97 @@ +import React, {forwardRef, useContext, useImperativeHandle, useReducer} from "react"; +import {createOption, reducer, actionDeleteOption, actionCreateOption} from "./optionsReducer"; +import Card from "@material-ui/core/Card"; +import {CardContent} from "@material-ui/core"; +import {makeStyles} from "@material-ui/core/styles"; +import BooleanParam from "../params/BooleanParam"; +import NumberParam from "../params/NumberParam"; +import StringLabeledArrayParam from "../params/StringLabeledArrayParam"; + +const OptionsFormDispatch = React.createContext(null); + +function Option(props) { + const dispatch = useContext(OptionsFormDispatch); + + switch (props.blueprint.type) { + case 'boolean': + return ( + + ); + case 'number': + return ( + + ); + case 'string_labeled_array': + return ( + + ); + default: + throw new Error("unsupported option type " + props.blueprint.type); + } +} + +const useOptionsFormStyles = makeStyles((theme) => ({ + option: { + flex: '0 0 100%', + borderBottom: '1px solid #dfdfdf', + marginBottom: theme.spacing(2), + paddingBottom: theme.spacing(1), + "&:last-child": { + borderBottom: 'none', + marginBottom: 0, + paddingBottom: 0 + } + } +})); + +function OptionsForm(props, ref) { + const classes = useOptionsFormStyles(); + const [state, dispatch] = useReducer(reducer, { + version: 1, + // event: criteria[0].event, + options: props.options ?? [] // by default, one empty node + }); + + // expose state to outer node + useImperativeHandle(ref, () => ({ + state: state + })); + + // remove all options which doesnt have blueprint for current generic + state.options.forEach(option => { + if (props.blueprints.find(blueprint => blueprint.key === option.key) === undefined) { + dispatch(actionDeleteOption(option.key)); + } + }); + + // create not existing options from blueprints + props.blueprints.forEach(blueprint => { + if (state.options.find(option => blueprint.key === option.key) === undefined) { + dispatch(actionCreateOption(blueprint.key)); + } + }); + + return ( + + + + {props.blueprints.map((blueprint) => { + let option = state.options.find(option => option.key === blueprint.key) ?? createOption(blueprint.key, null); + return ( +
+
+ ); + })} +
+
+
+ ); +} + +// forwardRef is here used to access local state from parent node +export default forwardRef(OptionsForm); diff --git a/src/assets/scenario-builder/components/elements/Generic/PortModel.js b/src/assets/scenario-builder/components/elements/Generic/PortModel.js new file mode 100644 index 0000000..1420ff8 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Generic/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightPort } from '../Ports'; + +export class PortModel extends LeftRightPort { + constructor(position = 'left') { + super(position, 'generic'); + } +} diff --git a/src/assets/scenario-builder/components/elements/Generic/index.js b/src/assets/scenario-builder/components/elements/Generic/index.js new file mode 100644 index 0000000..0b3fa31 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Generic/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/Generic/optionsReducer.js b/src/assets/scenario-builder/components/elements/Generic/optionsReducer.js new file mode 100644 index 0000000..5ce614a --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Generic/optionsReducer.js @@ -0,0 +1,71 @@ +export function createOption(key, values) { + return { + key: key, + values: { + selection: values + } + }; +} + +export function actionCreateOption(optionKey, values) { + return { + type: 'CREATE_OPTION', + payload: { + values: values, + optionKey: optionKey, + } + }; +} + +export function actionDeleteOption(optionKey) { + return { + type: 'DELETE_OPTION', + payload: { + optionKey: optionKey, + } + }; +} + +export function reducer(state, action) { + switch (action.type) { + case 'CREATE_OPTION': + return { + ...state, options: [...state.options, { + key: action.payload.optionKey, + values: { + selection: action.payload.values + } + }] + }; + case 'DELETE_OPTION': + return { + ...state, options: state.options.filter(n => n.key !== action.payload.optionKey) + }; + case 'SET_PARAM_VALUES': + return { + ...state, options: state.options.map(option => { + if (option.key === action.payload.name) { + return { + ...option, + values: action.payload.values + }; + } + return option; + }) + }; + case 'UPDATE_PARAM_VALUES': + return { + ...state, options: state.options.map(option => { + if (option.key === action.payload.name) { + return { + ...option, + values: Object.assign(option.values, action.payload.values) + }; + } + return option; + }) + }; + default: + throw new Error("unsupported action type " + action.type); + } +} diff --git a/src/assets/scenario-builder/components/elements/Generic/sass/style.scss b/src/assets/scenario-builder/components/elements/Generic/sass/style.scss new file mode 100644 index 0000000..0512c49 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Generic/sass/style.scss @@ -0,0 +1,30 @@ +$nodeColor: $lightViolet; + +.srd-node--selected { + .square-node.generic-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.generic-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/Goal/NodeFactory.js b/src/assets/scenario-builder/components/elements/Goal/NodeFactory.js new file mode 100755 index 0000000..c2a8a32 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Goal/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('goal'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Goal/NodeModel.js b/src/assets/scenario-builder/components/elements/Goal/NodeModel.js new file mode 100755 index 0000000..64fc6c2 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Goal/NodeModel.js @@ -0,0 +1,44 @@ +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import * as _ from 'lodash'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('goal', element.id); + + this.name = element.name; + this.selectedGoals = element.selectedGoals; + this.timeoutTime = element.timeoutTime; + this.timeoutUnit = element.timeoutUnit; + + this.recheckPeriodTime = element.recheckPeriodTime; + this.recheckPeriodUnit = element.recheckPeriodUnit; + + this.addPort(new PortModel('left')); + this.addPort(new PortModel('bottom')); + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.selectedGoals = ob.selectedGoals; + + this.timeoutTime = ob.timeoutTime !== null && ob.timeoutTime !== undefined ? ob.timeoutTime : ''; + this.timeoutUnit = ob.timeoutUnit !== null && ob.timeoutUnit !== undefined ? ob.timeoutUnit : 'days'; + + this.recheckPeriodTime = ob.recheckPeriodTime !== null && ob.recheckPeriodTime !== undefined ? ob.recheckPeriodTime : '1'; + this.recheckPeriodUnit = ob.recheckPeriodUnit !== null && ob.recheckPeriodUnit !== undefined ? ob.recheckPeriodUnit : 'hours'; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + selectedGoals: this.selectedGoals, + timeoutTime: this.timeoutTime, + timeoutUnit: this.timeoutUnit, + recheckPeriodTime: this.recheckPeriodTime, + recheckPeriodUnit: this.recheckPeriodUnit, + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/Goal/NodeWidget.js b/src/assets/scenario-builder/components/elements/Goal/NodeWidget.js new file mode 100755 index 0000000..e52cf0d --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Goal/NodeWidget.js @@ -0,0 +1,334 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import OkIcon from '@material-ui/icons/Check'; +import TimeoutIcon from '@material-ui/icons/AccessTime'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import FormControl from '@material-ui/core/FormControl'; +import InputLabel from '@material-ui/core/InputLabel'; +import Select from '@material-ui/core/Select'; +import MenuItem from '@material-ui/core/MenuItem'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import GoalIcon from '@material-ui/icons/CheckBox'; + +import StatisticsTooltip from '../../StatisticTooltip'; +import { PortWidget } from './../../widgets/PortWidget'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import StatisticBadge from "../../StatisticBadge"; +import {Autocomplete} from "@material-ui/lab"; + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + this.state = { + nodeFormName: this.props.node.name, + selectedGoals: this.props.node.selectedGoals, + timeoutTime: this.props.node.timeoutTime, + timeoutUnit: this.props.node.timeoutUnit, + recheckPeriodTime: this.props.node.recheckPeriodTime, + recheckPeriodUnit: this.props.node.recheckPeriodUnit, + dialogOpened: false, + anchorElementForTooltip: null, + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + selectedGoals: this.props.node.selectedGoals, + timeoutTime: this.props.node.timeoutTime, + timeoutUnit: this.props.node.timeoutUnit, + recheckPeriodTime: this.props.node.recheckPeriodTime, + recheckPeriodUnit: this.props.node.recheckPeriodUnit, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + transformOptionsForSelect = () => { + const goals = this.props.goals.map(goal => ({ + value: goal.code, + label: goal.name, + })); + return goals; + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + getSelectedGoals = () => { + if (this.state.selectedGoals === undefined) { + return []; + } + + return this.props.goals.filter((item) => { + return this.state.selectedGoals.includes(item.code) + }, this.state.selectedGoals); + }; + + render() { + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ {this.props.node.name ? this.props.node.name : 'Goal'} +
+
+ +
+
+ +
+ +
+
+ +
+ +
+ + {this.props.statistics[this.props.node.id] ? + : + + } +
+ +
+ + {this.props.statistics[this.props.node.id] ? + : + + } +
+
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + > + Goal node + + + + Goal node evaluates whether user has completed selected onboarding goals. + Timeout value can be optionally specified, defining a point in time when evalution of completed goals is stopped. + Execution flow can be directed two ways from the node - a positive direction, when all goals are completed, or a negative one, when timeout threshold is reached. + + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + option.name} + disableClearable={true} + onChange={(event, values) => { + if (values !== null) { + this.setState({ + selectedGoals: values.map(item => item.code) + }); + } + }} + renderInput={params => ( + + )} + /> + + + + + + { + this.setState({ + recheckPeriodTime: event.target.value + }); + }} + /> + + + + Time unit + + + + + + + + { + this.setState({ + timeoutTime: event.target.value + }); + }} + /> + + + + Time unit + + + + + + + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + const { goals, dispatch } = state; + + return { + goals: goals.availableGoals, + dispatch, + statistics: state.statistics.statistics + }; +} + +export default connect(mapStateToProps)(NodeWidget); diff --git a/src/assets/scenario-builder/components/elements/Goal/PortModel.js b/src/assets/scenario-builder/components/elements/Goal/PortModel.js new file mode 100755 index 0000000..5749f53 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Goal/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightBottomPort } from '../Ports'; + +export class PortModel extends LeftRightBottomPort { + constructor(position = 'top') { + super(position, 'goal'); + } +} diff --git a/src/assets/scenario-builder/components/elements/Goal/index.js b/src/assets/scenario-builder/components/elements/Goal/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Goal/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/Goal/sass/style.scss b/src/assets/scenario-builder/components/elements/Goal/sass/style.scss new file mode 100644 index 0000000..693c40f --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Goal/sass/style.scss @@ -0,0 +1,31 @@ +$nodeColor: $grey; + +.srd-node--selected { + .diamond-node.goal-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.goal-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right, + &__bottom { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} diff --git a/src/assets/scenario-builder/components/elements/Link/LinkFactory.js b/src/assets/scenario-builder/components/elements/Link/LinkFactory.js new file mode 100644 index 0000000..6c1fccf --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Link/LinkFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { DefaultLinkFactory } from '@projectstorm/react-diagrams'; + +import { LinkModel } from './LinkModel'; + +export class LinkFactory extends DefaultLinkFactory { + constructor() { + super(); + this.type = 'custom'; + } + + getNewInstance(initialConfig) { + return new LinkModel(); + } + + generateLinkSegment(model, widget, selected, path) { + return ( + + ); + } +} diff --git a/src/assets/scenario-builder/components/elements/Link/LinkModel.js b/src/assets/scenario-builder/components/elements/Link/LinkModel.js new file mode 100644 index 0000000..9dacdb4 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Link/LinkModel.js @@ -0,0 +1,11 @@ +import { DefaultLinkModel } from '@projectstorm/react-diagrams'; + +export class LinkModel extends DefaultLinkModel { + constructor() { + super('custom'); + + this.width = 2; + this.curvyness = 50; + this.color = 'rgba(0,0,0,0.3)'; + } +} diff --git a/src/assets/scenario-builder/components/elements/Link/index.js b/src/assets/scenario-builder/components/elements/Link/index.js new file mode 100644 index 0000000..7dd05b3 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Link/index.js @@ -0,0 +1,4 @@ +import { LinkModel } from './LinkModel'; +import { LinkFactory } from './LinkFactory'; + +export { LinkModel, LinkFactory }; diff --git a/src/assets/scenario-builder/components/elements/Link/sass/style.scss b/src/assets/scenario-builder/components/elements/Link/sass/style.scss new file mode 100644 index 0000000..73c5d85 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Link/sass/style.scss @@ -0,0 +1,38 @@ +.srd-default-link { + @keyframes dash { + from { + stroke-dashoffset: 24; + } + to { + stroke-dashoffset: 0; + } + } + + path { + fill: none; + pointer-events: all; + } + + &--path-selected { + stroke: $lightBlue !important; + stroke-dasharray: 10, 2; + animation: dash 1s linear infinite; + } + + &__label { + pointer-events: none; + + > div { + display: inline-block; + position: absolute; + } + } + + &__point { + fill: rgba(white, 0.5); + } + + &--point-selected { + fill: rgb(0, 192, 255); + } +} diff --git a/src/assets/scenario-builder/components/elements/Ports/LeftRightBottomPort.js b/src/assets/scenario-builder/components/elements/Ports/LeftRightBottomPort.js new file mode 100644 index 0000000..059170b --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Ports/LeftRightBottomPort.js @@ -0,0 +1,43 @@ +import * as _ from 'lodash'; +import { PortModel as BasePortModel } from '@projectstorm/react-diagrams'; + +import { LinkModel } from './../Link'; + +export class LeftRightBottomPort extends BasePortModel { + position; + + constructor(pos = 'left', type) { + super(pos, type); + + this.position = pos; + this.in = this.position === 'left'; + } + + link(port) { + let link = this.createLinkModel(); + + link.setSourcePort(this); + link.setTargetPort(port); + + return link; + } + + serialize() { + return _.merge(super.serialize(), { + position: this.position + }); + } + + canLinkToPort(port) { + return this.in !== port.in; + } + + deSerialize(data, engine) { + super.deSerialize(data, engine); + this.position = data.position; + } + + createLinkModel() { + return new LinkModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Ports/LeftRightPort.js b/src/assets/scenario-builder/components/elements/Ports/LeftRightPort.js new file mode 100644 index 0000000..8a706f9 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Ports/LeftRightPort.js @@ -0,0 +1,44 @@ +import * as _ from 'lodash'; +import { PortModel as BasePortModel } from '@projectstorm/react-diagrams'; + +import { LinkModel } from './../Link'; + +export class LeftRightPort extends BasePortModel { + in; + position; + + constructor(pos = 'left', type) { + super(pos, type); + + this.position = pos; + this.in = this.position === 'left'; + } + + link(port) { + let link = this.createLinkModel(); + + link.setSourcePort(this); + link.setTargetPort(port); + + return link; + } + + canLinkToPort(port) { + return this.in !== port.in; + } + + serialize() { + return _.merge(super.serialize(), { + position: this.position + }); + } + + deSerialize(data, engine) { + super.deSerialize(data, engine); + this.position = data.position; + } + + createLinkModel() { + return new LinkModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Ports/index.js b/src/assets/scenario-builder/components/elements/Ports/index.js new file mode 100644 index 0000000..3a5eedb --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Ports/index.js @@ -0,0 +1,7 @@ +import { LeftRightPort } from "./LeftRightPort"; +import { LeftRightBottomPort } from "./LeftRightBottomPort"; + +export { + LeftRightPort, + LeftRightBottomPort +}; diff --git a/src/assets/scenario-builder/components/elements/PushNotification/NodeFactory.js b/src/assets/scenario-builder/components/elements/PushNotification/NodeFactory.js new file mode 100755 index 0000000..8d0529a --- /dev/null +++ b/src/assets/scenario-builder/components/elements/PushNotification/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('push_notification'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/PushNotification/NodeModel.js b/src/assets/scenario-builder/components/elements/PushNotification/NodeModel.js new file mode 100755 index 0000000..d7ada01 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/PushNotification/NodeModel.js @@ -0,0 +1,30 @@ +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import * as _ from 'lodash'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('push_notification', element.id); + + this.name = element.name; + this.selectedTemplate = element.selectedTemplate; + this.selectedApplication = element.selectedApplication; + this.addPort(new PortModel('left')); + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.selectedTemplate = ob.selectedTemplate; + this.selectedApplication = ob.selectedApplication; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + selectedTemplate: this.selectedTemplate, + selectedApplication: this.selectedApplication, + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/PushNotification/NodeWidget.js b/src/assets/scenario-builder/components/elements/PushNotification/NodeWidget.js new file mode 100755 index 0000000..2b4904b --- /dev/null +++ b/src/assets/scenario-builder/components/elements/PushNotification/NodeWidget.js @@ -0,0 +1,255 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import ActionIcon from '@material-ui/icons/PhonelinkRing'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import { PortWidget } from '../../widgets/PortWidget'; +import StatisticsTooltip from '../../StatisticTooltip'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import Autocomplete from "@material-ui/lab/Autocomplete"; +import StatisticBadge from "../../StatisticBadge"; + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + this.state = { + nodeFormName: this.props.node.name, + selectedTemplate: this.props.node.selectedTemplate, + selectedApplication: this.props.node.selectedApplication, + dialogOpened: false, + anchorElementForTooltip: null + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + getTemplatesInSelectableFormat = () => { + return this.props.templates.map(item => { + return { + value: item.code, + label: item.name, + }; + }); + }; + + getApplicationsInSelectableFormat = () => { + return this.props.applications.map(item => { + return { + value: item.code, + label: item.name, + }; + }); + }; + + getSelectedTemplateValue = () => { + const selected = this.props.templates.find( + item => item.code === this.props.node.selectedTemplate + ); + + return selected ? ` - ${selected.name}` : ''; + }; + + render() { + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ +
+ +
+
+ +
+
+ + +
+
+
+
+
+ {this.props.node.name + ? this.props.node.name + : `Notification ${this.getSelectedTemplateValue()}`} +
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + fullWidth + > + Notification node + + + Sends a push notification to user. + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + option.value === this.state.selectedTemplate + )} + options={this.getTemplatesInSelectableFormat()} + getOptionLabel={(option) => option.label} + style={{ marginBottom: 16 }} + onChange={(event, selectedOption) => { + if (selectedOption !== null) { + this.setState({ + selectedTemplate: selectedOption.value + }); + } + }} + renderInput={params => ( + + )} + /> + + + + + + option.value === this.state.selectedApplication + )} + options={this.getApplicationsInSelectableFormat()} + getOptionLabel={(option) => option.label} + style={{ marginBottom: 16 }} + onChange={(event, selectedOption) => { + if (selectedOption !== null) { + this.setState({ + selectedApplication: selectedOption.value + }); + } + }} + renderInput={params => ( + + )} + /> + + + + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + return { + templates: state.pushNotifications.availableTemplates, + applications: state.pushNotifications.availableApplications, + }; +} + +export default connect(mapStateToProps)(NodeWidget); diff --git a/src/assets/scenario-builder/components/elements/PushNotification/PortModel.js b/src/assets/scenario-builder/components/elements/PushNotification/PortModel.js new file mode 100755 index 0000000..3d9003c --- /dev/null +++ b/src/assets/scenario-builder/components/elements/PushNotification/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightPort } from '../Ports'; + +export class PortModel extends LeftRightPort { + constructor(position = 'left') { + super(position, 'push_notification'); + } +} diff --git a/src/assets/scenario-builder/components/elements/PushNotification/index.js b/src/assets/scenario-builder/components/elements/PushNotification/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/PushNotification/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/PushNotification/sass/style.scss b/src/assets/scenario-builder/components/elements/PushNotification/sass/style.scss new file mode 100644 index 0000000..92c1f50 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/PushNotification/sass/style.scss @@ -0,0 +1,30 @@ +$nodeColor: $lightPurple; + +.srd-node--selected { + .square-node.push-notification-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.push-notification-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/Segment/NodeFactory.js b/src/assets/scenario-builder/components/elements/Segment/NodeFactory.js new file mode 100755 index 0000000..2d06b35 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Segment/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('segment'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Segment/NodeModel.js b/src/assets/scenario-builder/components/elements/Segment/NodeModel.js new file mode 100755 index 0000000..61d5d9c --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Segment/NodeModel.js @@ -0,0 +1,29 @@ +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import * as _ from 'lodash'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('segment', element.id); + + this.name = element.name; + this.selectedSegment = element.selectedSegment; + + this.addPort(new PortModel('left')); + this.addPort(new PortModel('bottom')); + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.selectedSegment = ob.selectedSegment; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + selectedSegment: this.selectedSegment + }); + } +} \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/Segment/NodeWidget.js b/src/assets/scenario-builder/components/elements/Segment/NodeWidget.js new file mode 100755 index 0000000..a69fa2f --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Segment/NodeWidget.js @@ -0,0 +1,269 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import SegmentIcon from '@material-ui/icons/SubdirectoryArrowRight'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Icon from '@material-ui/core/Icon'; +import { PortWidget } from '../../widgets/PortWidget'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import SegmentSelector from './SegmentSelector'; +import * as config from '../../../config'; +import { styled } from '@material-ui/core/styles'; +import StatisticBadge from "../../StatisticBadge"; +import StatisticsTooltip from "../../StatisticTooltip"; +import OkIcon from "@material-ui/icons/Check"; +import NopeIcon from "@material-ui/icons/Close"; + +const NewSegmentButton = styled(Button)({ + marginRight: 'auto' +}); + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + this.state = { + nodeFormName: this.props.node.name, + selectedSegment: this.props.node.selectedSegment, + dialogOpened: false, + anchorElementForTooltip: null, + creatingNewSegment: false, + selectedSegmentSourceTable: null + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + selectedSegment: this.props.node.selectedSegment, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + actionSetTable = table => { + if (this.state.selectedSegmentSourceTable !== table) { + this.setState({selectedSegment: null}); + this.setState({selectedSegmentSourceTable: table}); + } + }; + + segmentSelectedChange = segment => { + let value = null; + if (segment && segment.hasOwnProperty('code')) { + value = segment.code; + } + + this.setState({selectedSegment: value}); + }; + + getSelectedSegmentValue = () => { + const selected = this.props.segments.flatMap( + item => item.segments).find( + segment => segment.code === this.props.node.selectedSegment + ); + + return selected ? ` - ${selected.name}` : ''; + }; + + handleNewSegmentClick = () => { + window.open(config.URL_SEGMENT_NEW); + }; + + render() { + let displayStatisticBadge = false; + if (this.props.statistics.length === 0 || this.props.statistics[this.props.node.id]) { + displayStatisticBadge = true; + } + + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ {this.props.node.name + ? this.props.node.name + : `Segment ${this.getSelectedSegmentValue()}`} +
+
+ +
+
+ +
+ +
+
+ +
+ +
+ + {displayStatisticBadge ? + : + + } +
+ +
+ + {displayStatisticBadge ? + : + + } +
+
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + > + Segment node + + + + Segments evaluate user's presence in a group of users defined + by system-provided conditions. Execution flow can be directed + based on presence/absence of user within the selected segment. + You can either pick one of the existing segments or create a + new one. + + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + + + + + + + + + add_circle + New segment + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + const { segments, dispatch } = state; + + return { + segments: segments.avalaibleSegments, + statistics: state.statistics.statistics, + dispatch + }; +} + +export default connect(mapStateToProps)(NodeWidget); diff --git a/src/assets/scenario-builder/components/elements/Segment/PortModel.js b/src/assets/scenario-builder/components/elements/Segment/PortModel.js new file mode 100755 index 0000000..5f01520 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Segment/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightBottomPort } from './../Ports'; + +export class PortModel extends LeftRightBottomPort { + constructor(position = 'top') { + super(position, 'segment'); + } +} diff --git a/src/assets/scenario-builder/components/elements/Segment/SegmentFormSelect.js b/src/assets/scenario-builder/components/elements/Segment/SegmentFormSelect.js new file mode 100644 index 0000000..fc3bc0d --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Segment/SegmentFormSelect.js @@ -0,0 +1,75 @@ +import React from 'react'; +import {useSelector} from "react-redux"; +import Autocomplete from "@material-ui/lab/Autocomplete"; +import TextField from "@material-ui/core/TextField"; +import { makeStyles } from '@material-ui/core/styles'; +import { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const useStyles = makeStyles((theme) => ({ + autocomplete: { + margin: theme.spacing(1) + }, + subtitle: { + paddingLeft: '6px', + color: theme.palette.grey[600] + }, + })); + +function optionLabel(option) { + return typeof(option) === 'string' ? option : option.name; +} + +function optionSelected(option, value) { + if (value && value.hasOwnProperty('code')) { + return option.code === value.code; + } + return false; +} + +function value(selectedSegment, items) { + const item = items.filter(item => item.code === selectedSegment)[0]; + return item ? item : null; +} + +// Defines what values are searched within option +const filterOptions = createFilterOptions({ + matchFrom: 'any', + trim: true, + ignoreAccents: true, + ignoreCase: true, + stringify: option => { + return option.name + " " + option.code; + }, + }); + +export default function SegmentFormSelect(props) { + const classes = useStyles(); + const items = useSelector(state => state.segments.avalaibleSegments.filter( + item => item.table === props.selectedSegmentSourceTable + ))[0].segments.sort( + (a,b) => a.group.sorting - b.group.sorting === 0 ? a.group.id - b.group.id : a.group.sorting - b.group.sorting + ); + + return ( + option.group.name} + filterOptions={filterOptions} + onChange={(event, value) => {props.onSegmentSelectedChange(value)}} + renderInput={params => ( + + )} + renderOption={(option, { selected }) => ( +
+ {optionLabel(option)} + ({option.code}) +
+ )} + /> + ); +} \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/Segment/SegmentSelector.js b/src/assets/scenario-builder/components/elements/Segment/SegmentSelector.js new file mode 100644 index 0000000..482ecfc --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Segment/SegmentSelector.js @@ -0,0 +1,58 @@ +import React from 'react'; +import Grid from '@material-ui/core/Grid'; +import ButtonGroup from "@material-ui/core/ButtonGroup"; +import {makeStyles} from "@material-ui/core/styles"; +import Button from "@material-ui/core/Button"; +import {useSelector} from "react-redux"; +import SegmentFormSelect from "./SegmentFormSelect"; + +const useCriteriaBuilderStyles = makeStyles({ + selectedButton: { + backgroundColor: "#E4E4E4" + }, + deselectedButton: { + color: "#A6A6A6" + } +}); + +function getSourceTable(selectedSegmentSourceTable, selectedSegment, items) { + if (selectedSegmentSourceTable) { + return selectedSegmentSourceTable; + } + + if (selectedSegment) { + const segment = items.filter(item => item.segments.filter(item => item.code === selectedSegment).length > 0); + + return (segment[0] && segment[0].hasOwnProperty('table')) ? segment[0].table : 'users'; + } + + return 'users'; +} + +export default function SegmentSelector(props) { + const classes = useCriteriaBuilderStyles(); + const items = useSelector(state => state.segments.avalaibleSegments); + const sourceTable = getSourceTable(props.selectedSegmentSourceTable, props.selectedSegment, items); + + return ( + + + + {items.map(item => ( + + ))} + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/Segment/index.js b/src/assets/scenario-builder/components/elements/Segment/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Segment/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/Segment/sass/style.scss b/src/assets/scenario-builder/components/elements/Segment/sass/style.scss new file mode 100644 index 0000000..b43e6ea --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Segment/sass/style.scss @@ -0,0 +1,33 @@ +$nodeColor: $lightRed; + +.srd-node--selected { + .diamond-node.segment-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.segment-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right, + &__bottom { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} + +@import url(https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons); \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/SimplePortFactory.js b/src/assets/scenario-builder/components/elements/SimplePortFactory.js new file mode 100644 index 0000000..441718d --- /dev/null +++ b/src/assets/scenario-builder/components/elements/SimplePortFactory.js @@ -0,0 +1,14 @@ +import { AbstractPortFactory } from '@projectstorm/react-diagrams'; + +export class SimplePortFactory extends AbstractPortFactory { + cb; + + constructor(type, cb) { + super(type); + this.cb = cb; + } + + getNewInstance(initialConfig) { + return this.cb(initialConfig); + } +} diff --git a/src/assets/scenario-builder/components/elements/Trigger/NodeFactory.js b/src/assets/scenario-builder/components/elements/Trigger/NodeFactory.js new file mode 100755 index 0000000..9a74c1b --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Trigger/NodeFactory.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('trigger'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Trigger/NodeModel.js b/src/assets/scenario-builder/components/elements/Trigger/NodeModel.js new file mode 100755 index 0000000..5755fca --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Trigger/NodeModel.js @@ -0,0 +1,26 @@ +import * as _ from 'lodash'; +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('trigger', element.id); + + this.name = element.name; + this.selectedTrigger = element.selectedTrigger; + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.selectedTrigger = ob.selectedTrigger; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + selectedTrigger: this.selectedTrigger + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/Trigger/NodeWidget.js b/src/assets/scenario-builder/components/elements/Trigger/NodeWidget.js new file mode 100755 index 0000000..6755fdf --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Trigger/NodeWidget.js @@ -0,0 +1,223 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import TriggerIcon from '@material-ui/icons/Notifications'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import { PortWidget } from './../../widgets/PortWidget'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import StatisticBadge from "../../StatisticBadge"; +import StatisticsTooltip from "../../StatisticTooltip"; + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + + this.state = { + nodeFormName: this.props.node.name, + selectedTrigger: this.props.node.selectedTrigger, + dialogOpened: false, + anchorElementForTooltip: null + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormName: this.props.node.name, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + getTriggersInSelectableFormat = () => { + return this.props.triggers.map(trigger => { + return { + value: trigger.code, + label: trigger.name + }; + }); + }; + + getSelectedTriggerValue = () => { + const selected = this.getTriggersInSelectableFormat().find( + trigger => trigger.value === this.props.node.selectedTrigger + ); + + return selected ? ` - ${selected.label}` : ''; + }; + + render() { + return ( +
+
{this.openDialog();}} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+ +
+ +
+
+ + +
+
+
+ +
+
+ {this.props.node.name + ? this.props.node.name + : `Event ${this.getSelectedTriggerValue()}`} +
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + > + Event node + + + + Events are emitted on any change related to user. We recommend to + combine "before" events with "Wait" operations to achieve + execution at any desired time. + + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + option.value === this.state.selectedTrigger + )} + options={this.getTriggersInSelectableFormat()} + getOptionLabel={(option) => option.label} + style={{ marginTop: 16 }} + onChange={(event, selectedOption) => { + if (selectedOption !== null) { + this.setState({ + selectedTrigger: selectedOption.value + }); + } + }} + renderInput={params => ( + + )} + /> + + + + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + const { triggers } = state; + + return { + triggers: triggers.avalaibleTriggers + }; +} + +export default connect(mapStateToProps)(NodeWidget); diff --git a/src/assets/scenario-builder/components/elements/Trigger/PortModel.js b/src/assets/scenario-builder/components/elements/Trigger/PortModel.js new file mode 100755 index 0000000..da87906 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Trigger/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightPort } from './../Ports'; + +export class PortModel extends LeftRightPort { + constructor(position = 'left') { + super(position, 'trigger'); + } +} diff --git a/src/assets/scenario-builder/components/elements/Trigger/index.js b/src/assets/scenario-builder/components/elements/Trigger/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Trigger/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/Trigger/sass/style.scss b/src/assets/scenario-builder/components/elements/Trigger/sass/style.scss new file mode 100644 index 0000000..473eca8 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Trigger/sass/style.scss @@ -0,0 +1,30 @@ +$nodeColor: $lightGreen; + +.srd-node--selected { + .square-node.trigger-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.trigger-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} diff --git a/src/assets/scenario-builder/components/elements/Wait/NodeFactory.js b/src/assets/scenario-builder/components/elements/Wait/NodeFactory.js new file mode 100755 index 0000000..8d0f041 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Wait/NodeFactory.js @@ -0,0 +1,26 @@ +import { AbstractNodeFactory } from '@projectstorm/react-diagrams'; + +import NodeWidget from './NodeWidget'; +import { NodeModel } from './NodeModel'; +import * as React from 'react'; + +export class NodeFactory extends AbstractNodeFactory { + constructor() { + super('wait'); + } + + generateReactWidget(diagramEngine, node) { + return ( + + ); + } + + getNewInstance() { + return new NodeModel(); + } +} diff --git a/src/assets/scenario-builder/components/elements/Wait/NodeModel.js b/src/assets/scenario-builder/components/elements/Wait/NodeModel.js new file mode 100755 index 0000000..eb16c2b --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Wait/NodeModel.js @@ -0,0 +1,31 @@ +import * as _ from 'lodash'; +import { NodeModel as BaseNodeModel } from '@projectstorm/react-diagrams'; +import { PortModel } from './PortModel'; + +export class NodeModel extends BaseNodeModel { + constructor(element) { + super('wait', element.id); + + this.name = element.name; + this.waitingTime = element.waitingTime !== undefined ? element.waitingTime : 10; + this.waitingUnit = element.waitingUnit !== undefined ? element.waitingUnit : 'minutes'; + + this.addPort(new PortModel('left')); + this.addPort(new PortModel('right')); + } + + deSerialize(ob, engine) { + super.deSerialize(ob, engine); + this.name = ob.name; + this.waitingTime = ob.waitingTime; + this.waitingUnit = ob.waitingUnit; + } + + serialize() { + return _.merge(super.serialize(), { + name: this.name, + waitingTime: this.waitingTime, + waitingUnit: this.waitingUnit + }); + } +} diff --git a/src/assets/scenario-builder/components/elements/Wait/NodeWidget.js b/src/assets/scenario-builder/components/elements/Wait/NodeWidget.js new file mode 100755 index 0000000..41fda18 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Wait/NodeWidget.js @@ -0,0 +1,229 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import WaitIcon from '@material-ui/icons/AccessAlarmsOutlined'; +import Grid from '@material-ui/core/Grid'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import MenuItem from '@material-ui/core/MenuItem'; +import InputLabel from '@material-ui/core/InputLabel'; +import Select from '@material-ui/core/Select'; +import FormControl from '@material-ui/core/FormControl'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import { PortWidget } from '../../widgets/PortWidget'; +import { setCanvasZoomingAndPanning } from '../../../actions'; +import StatisticBadge from "../../StatisticBadge"; +import StatisticsTooltip from "../../StatisticTooltip"; + +class NodeWidget extends React.Component { + constructor(props) { + super(props); + this.state = { + nodeFormWaitingTime: this.props.node.waitingTime, + nodeFormName: this.props.node.name, + timeUnit: this.props.node.waitingUnit, + dialogOpened: false, + anchorElementForTooltip: null + }; + } + + bem(selector) { + return ( + this.props.classBaseName + + selector + + ' ' + + this.props.className + + selector + + ' ' + ); + } + + getClassName() { + return this.props.classBaseName + ' ' + this.props.className; + } + + openDialog = () => { + this.setState({ + dialogOpened: true, + nodeFormWaitingTime: this.props.node.waitingTime, + nodeFormName: this.props.node.name, + timeUnit: this.props.node.waitingUnit, + anchorElementForTooltip: null + }); + this.props.dispatch(setCanvasZoomingAndPanning(false)); + }; + + closeDialog = () => { + this.setState({ dialogOpened: false }); + this.props.dispatch(setCanvasZoomingAndPanning(true)); + }; + + handleNodeMouseEnter = event => { + if (!this.state.dialogOpened) { + this.setState({ anchorElementForTooltip: event.currentTarget }); + } + }; + + handleNodeMouseLeave = () => { + this.setState({ anchorElementForTooltip: null }); + }; + + render() { + return ( +
{ + this.openDialog(); + }} + onMouseEnter={this.handleNodeMouseEnter} + onMouseLeave={this.handleNodeMouseLeave} + > +
+
+ +
+
+
+ +
+
+ + +
+
+
+
+
+ {this.props.node.name + ? this.props.node.name + : `Wait - ${this.props.node.waitingTime} ${ + this.props.node.waitingUnit + }`} +
+
+ + + + { + if (event.keyCode === 46 || event.keyCode === 8) { + event.preventDefault(); + event.stopPropagation(); + return false; + } + }} + > + Wait node + + + Postpones the execution of next node in flow by selected amount of + time. + + + + + { + this.setState({ + nodeFormName: event.target.value + }); + }} + /> + + + + + + { + this.setState({ + nodeFormWaitingTime: event.target.value + }); + }} + /> + + + + Time unit + + + + + + + + + + + + +
+ ); + } +} + +function mapStateToProps(state) { + const { segments } = state; + + return { + segments: segments.avalaibleSegments + }; +} + +export default connect(mapStateToProps)(NodeWidget); diff --git a/src/assets/scenario-builder/components/elements/Wait/PortModel.js b/src/assets/scenario-builder/components/elements/Wait/PortModel.js new file mode 100755 index 0000000..871b6cb --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Wait/PortModel.js @@ -0,0 +1,7 @@ +import { LeftRightPort } from './../Ports'; + +export class PortModel extends LeftRightPort { + constructor(position = 'left') { + super(position, 'wait'); + } +} diff --git a/src/assets/scenario-builder/components/elements/Wait/index.js b/src/assets/scenario-builder/components/elements/Wait/index.js new file mode 100755 index 0000000..af068d7 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Wait/index.js @@ -0,0 +1,9 @@ +import { NodeModel } from "./NodeModel"; +import { NodeFactory } from "./NodeFactory"; +import { PortModel } from "./PortModel"; + +export { + NodeModel, + NodeFactory, + PortModel +}; diff --git a/src/assets/scenario-builder/components/elements/Wait/sass/style.scss b/src/assets/scenario-builder/components/elements/Wait/sass/style.scss new file mode 100644 index 0000000..c8d6ca3 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/Wait/sass/style.scss @@ -0,0 +1,30 @@ +$nodeColor: $lightOrange; + +.srd-node--selected { + .round-node.wait-node .node-container { + box-shadow: 0 0 10px $nodeColor; + } +} + +.wait-node { + .node-container { + border: solid 2px $nodeColor; + color: $nodeColor; + } + + &__title { + color: $nodeColor; + } + + &__left, + &__right { + .port { + box-shadow: 0 0 10px $nodeColor; + + &:hover, + &.selected { + background: $nodeColor; + } + } + } +} diff --git a/src/assets/scenario-builder/components/elements/defaultElementsStyle.scss b/src/assets/scenario-builder/components/elements/defaultElementsStyle.scss new file mode 100644 index 0000000..eaf18d1 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/defaultElementsStyle.scss @@ -0,0 +1,118 @@ +.square-node, +.round-node, +.diamond-node { + background-color: transparent; + display: flex; + flex-direction: column; + align-items: center; + width: 150px; + + .node-container { + position: relative; + display: flex; + align-items: center; + width: 50px; + height: 50px; + border-radius: 5px; + overflow: visible; + background-color: white; + transition: all 0.15s; + } + + &__title { + display: flex; + text-align: center; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', + 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; + font-weight: 500; + + > * { + align-self: center; + } + .fa { + padding: 5px; + opacity: 0.2; + cursor: pointer; + + &:hover { + opacity: 1; + } + } + } + + &__name { + flex-grow: 1; + padding: 5px 0; + } + + &__ports { + display: flex; + background-image: none; + width: 100%; + } + + &__left, + &__right, + &__bottom { + flex-grow: 1; + display: flex; + flex-direction: column; + + .port { + border-radius: 13px; + width: 13px; + height: 13px; + position: relative; + background: white; + &:hover, + &.selected { + background: #a291fb; + } + } + } + + &__left { + align-items: flex-start; + .port { + left: -6.5px; + } + } + + &__right { + align-items: flex-end; + .port { + right: -6.5px; + } + } + + &__bottom { + align-items: center; + .port { + bottom: -6.5px; + } + } + + &__icon { + position: absolute; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + } +} + +.round-node { + .node-container { + border-radius: 50px; + } +} + +.diamond-node { + &__bottom { + position: absolute; + bottom: 0; + width: 100%; + } +} diff --git a/src/assets/scenario-builder/components/elements/index.js b/src/assets/scenario-builder/components/elements/index.js new file mode 100644 index 0000000..c05a700 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/index.js @@ -0,0 +1,15 @@ +import { SimplePortFactory } from './SimplePortFactory'; + +import * as Email from './Email'; +import * as Banner from './Banner'; +import * as Generic from './Generic'; +import * as Segment from './Segment'; +import * as Trigger from './Trigger'; +import * as BeforeTrigger from './BeforeTrigger'; +import * as Wait from './Wait'; +import * as Goal from './Goal'; +import * as Condition from './Condition'; +import * as PushNotification from './PushNotification'; +import * as ABTest from './ABTest'; + +export { SimplePortFactory, Email, Banner, Generic, Segment, Trigger, BeforeTrigger, Wait, Goal, Condition, PushNotification, ABTest}; diff --git a/src/assets/scenario-builder/components/elements/index.scss b/src/assets/scenario-builder/components/elements/index.scss new file mode 100644 index 0000000..e10492a --- /dev/null +++ b/src/assets/scenario-builder/components/elements/index.scss @@ -0,0 +1,43 @@ +/*--- Colors ---*/ +$red: #db2828; +$orange: #f2711c; +$yellow: #fbbd08; +$olive: #b5cc18; +$green: #21ba45; +$teal: #00b5ad; +$blue: #2185d0; +$violet: #6435c9; +$purple: #a333c8; +$pink: #e03997; +$brown: #a5673f; +$grey: #767676; +$black: #1b1c1d; + +/*--- Light Colors ---*/ +$lightRed: #ff695e; +$lightOrange: #ff851b; +$lightYellow: #ffe21f; +$lightOlive: #d9e778; +$lightGreen: #2ecc40; +$lightTeal: #6dffff; +$lightBlue: #54c8ff; +$lightViolet: #a291fb; +$lightPurple: #dc73ff; +$lightPink: #ff8edf; +$lightBrown: #d67c1c; +$lightGrey: #dcddde; +$lightBlack: #54545418; + +@import './defaultElementsStyle'; +@import './Email/sass/style'; +@import './Generic/sass/style'; +@import './Segment/sass/style'; +@import './Trigger/sass/style'; +@import './BeforeTrigger/sass/style'; +@import './Wait/sass/style'; +@import './Link/sass/style'; +@import './Goal/sass/style'; +@import './Banner/sass/style'; +@import './Condition/sass/style'; +@import './PushNotification/sass/style'; +@import './ABTest/sass/style'; diff --git a/src/assets/scenario-builder/components/elements/params/BooleanParam.js b/src/assets/scenario-builder/components/elements/params/BooleanParam.js new file mode 100644 index 0000000..e086a92 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/params/BooleanParam.js @@ -0,0 +1,49 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Switch from '@material-ui/core/Switch'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import { actionSetParamValues } from './actions'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(theme => ({ + formControl: { + marginBottom: theme.spacing(1), + } +})); + +export default function BooleanParam(props) { + const classes = useStyles(); + + // if not selected yet, set selection to True + if (props.values.selection === undefined) { + props.dispatch(actionSetParamValues(props.name, { + selection: true + })); + } + + const handleChange = (event) => { + props.dispatch(actionSetParamValues(props.name, { + selection: event.target.checked + })); + }; + + return ( + } + checked={props.values.selection !== undefined && props.values.selection} + label={props.blueprint.label} + className={classes.formControl} + /> + ); + } + +BooleanParam.propTypes = { + // name identifying input (same function as in HTML ), used in dispatch + name: PropTypes.any.isRequired, + // values, example: {selection: true} + values: PropTypes.object.isRequired, + // blueprint, example: {label: 'Is recurrent', type: 'boolean'} + blueprint: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired, +}; \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/params/NumberParam.js b/src/assets/scenario-builder/components/elements/params/NumberParam.js new file mode 100644 index 0000000..fcab531 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/params/NumberParam.js @@ -0,0 +1,95 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import TextField from '@material-ui/core/TextField'; +import InputLabel from '@material-ui/core/InputLabel'; +import FormLabel from '@material-ui/core/FormLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; +import { makeStyles } from '@material-ui/core/styles'; +import { actionSetParamValues, actionUpdateParamValues } from './actions'; + +const useStyles = makeStyles((theme) => ({ + formControl: { + marginRight: theme.spacing(1), + marginBottom: theme.spacing(1), + minWidth: 100, + }, + formLabel: { + display: 'block', + marginBottom: theme.spacing(1), + }, + numberInput: { + marginRight: theme.spacing(1), + marginBottom: theme.spacing(1), + } +})); + +export default function NumberParam(props) { + const classes = useStyles(); + + // if only one operator, select it by default + if (props.values.operator === undefined && props.blueprint.operators && props.blueprint.operators.length === 1) { + props.dispatch(actionSetParamValues(props.name, { + operator: props.blueprint.operators[0] + })); + } + + const handleOperatorChange = (event) => { + props.dispatch(actionUpdateParamValues(props.name, { + operator: event.target.value + })); + }; + + const handleInputChange = (event) => { + props.dispatch(actionUpdateParamValues(props.name, { + selection: event.target.value + })); + }; + + return ( + + {!props.hideLabel && + + {props.blueprint.label} + + } + + {props.blueprint.operators && props.blueprint.operators.length && + + Operator + + + } + + HTML tag + inputProps={props.blueprint.numberInputAttributes ?? {}} + /> + + ); +} + +NumberParam.propTypes = { + // name identifying input (same function as in HTML ), used in dispatch + name: PropTypes.any.isRequired, + // values, example: {selection: 3, operator: '>'} + values: PropTypes.object.isRequired, + // blueprint, example: {label: 'Subscription type length', type: 'number', 'operators': ['=', '<', '>'], unit: 'Day(s)', numberInputAttributes: {min: 0}} + blueprint: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired, + hideOperator: PropTypes.bool, + hideLabel: PropTypes.bool, +}; \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/params/StringLabeledArrayParam.js b/src/assets/scenario-builder/components/elements/params/StringLabeledArrayParam.js new file mode 100644 index 0000000..ef7f3a9 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/params/StringLabeledArrayParam.js @@ -0,0 +1,148 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import { createFilterOptions } from '@material-ui/lab/Autocomplete'; +import { TextField } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import { actionSetParamValues } from './actions'; + +const elementStyles = makeStyles(theme => ({ + // Puts visually OR/AND between tags + chipRoot: props => ({ + "&:not(:first-child)": { + "&::before": { + content: "'" + props.operator + "'", + textTransform: 'uppercase', + position: 'absolute', + left: '-20px', + }, + marginLeft: '20px' + }, + position: 'relative' + }), + subtitle: { + paddingLeft: '6px', + color: theme.palette.grey[600] + }, + autocomplete: { + marginBottom: theme.spacing(1), + } +})); + +function selectedOptions(selectedValues, options) { + const s = new Set(selectedValues); + let selected = options.filter(option => { + let has = s.has(option.value); + if (has) { + // for free-solo mode + s.delete(option.value); + } + return has; + }); + // If free solo mode is enabled, there might be additional selected values (outside of options), add them as well + return selected.concat([...s]); +} + +function optionLabel(option) { + if (typeof(option) === 'string') { + // free-solo value + return option; + } else { + // predefined option value + return option.label; + } +} + +function optionSubtitle(option) { + if (typeof(option) === 'string') { + return ''; + } else { + return option.subtitle !== undefined ? option.subtitle : ''; + } +} + +function optionGroup(option) { + if (typeof(option) === 'string') { + // free-solo value + return ''; + } else if (option.hasOwnProperty('group')) { + return option.group; + } else { + return ''; + } +} + +// https://material-ui.com/components/autocomplete/#createfilteroptions-config-filteroptions +// Defines what values are searched within option +const filterOptions = createFilterOptions({ + matchFrom: 'any', + trim: true, + ignoreAccents: true, + ignoreCase: true, + stringify: option => { + return optionLabel(option) + " " + optionSubtitle(option); + }, +}); + +export default function StringLabeledArrayParam(props) { + const classes = elementStyles({operator: props.blueprint.operator}); + const handleChange = (event, values) => { + props.dispatch(actionSetParamValues(props.name, { + operator: props.blueprint.operator, // TODO add ability to change operator + selection: values.map(item => { + if (typeof(item) === 'string') { + // free-solo value + return item; + } else { + // predefined option value + return item.value; + } + }) + })); + }; + + return ( + ( + + )} + renderOption={(option, { selected }) => ( + + {optionLabel(option)} + {optionSubtitle(option)} + + )} + /> + ); +} + +StringLabeledArrayParam.propTypes = { + // name identifying input (same function as in HTML ), used in dispatch + name: PropTypes.any.isRequired, + // values, example: {selection: ['city_1'], operator: 'or'} + values: PropTypes.object.isRequired, + // blueprint, example: {label: 'Cities', type: 'string_labeled_array', options: [{value: 'city_1', label: 'City 1', subtitle: '(best city)' group: 'Group 1'}], operator: 'or', freeSolo: true} + blueprint: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired, +}; \ No newline at end of file diff --git a/src/assets/scenario-builder/components/elements/params/TimeframeParam.js b/src/assets/scenario-builder/components/elements/params/TimeframeParam.js new file mode 100644 index 0000000..b6d8d57 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/params/TimeframeParam.js @@ -0,0 +1,122 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import TextField from '@material-ui/core/TextField'; +import InputLabel from '@material-ui/core/InputLabel'; +import FormLabel from '@material-ui/core/FormLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; +import { makeStyles } from '@material-ui/core/styles'; +import { actionSetParamValues, actionUpdateParamValues } from './actions'; + +const useStyles = makeStyles((theme) => ({ + formControl: { + marginRight: theme.spacing(1), + marginBottom: theme.spacing(1), + minWidth: 150, + }, + formLabel: { + display: 'block', + marginBottom: theme.spacing(1), + }, + amountInput: { + marginRight: theme.spacing(1), + marginBottom: theme.spacing(1), + } +})); + +export default function TimeframeParam(props) { + const classes = useStyles(); + + // if only one operator, select it by default + if (props.values.operator === undefined && props.blueprint.operators && props.blueprint.operators.length === 1) { + props.dispatch(actionSetParamValues(props.name, { + operator: props.blueprint.operators[0] + })); + } + + // if only one unit, select it by default + if (props.values.unit === undefined && props.blueprint.units && props.blueprint.units.length === 1) { + props.dispatch(actionUpdateParamValues(props.name, { + unit: props.blueprint.units[0] + })); + } + + const handleOperatorChange = (event) => { + props.dispatch(actionUpdateParamValues(props.name, { + operator: event.target.value + })); + }; + + const handleUnitChange = (event) => { + props.dispatch(actionUpdateParamValues(props.name, { + unit: event.target.value + })); + }; + + const handleAmountInputChange = (event) => { + props.dispatch(actionUpdateParamValues(props.name, { + selection: event.target.value + })); + }; + + return ( + + {!props.hideLabel && + + {props.blueprint.label} + + } + + {props.blueprint.operators && props.blueprint.operators.length && + + Operator + + + } + + HTML tag + inputProps={props.blueprint.amountInputAttributes ?? {}} + /> + + {props.blueprint.units && props.blueprint.units.length && + + {props.blueprint.unitsLabel} + + + } + + + ); +}; + +TimeframeParam.propTypes = { + // name identifying input (same function as in HTML ), used in dispatch + name: PropTypes.any.isRequired, + // values, example: {selection: true} + values: PropTypes.object.isRequired, + // blueprint, example: {label: 'Timeframe', type: 'timeframe', 'operators': ['=', '<', '>'], 'units': ['days', 'months', 'years'], 'amountLabel': 'Amount', 'unitsLabel': 'Time unit', 'amountInputAttributes': {min: 0}} + blueprint: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired, +}; diff --git a/src/assets/scenario-builder/components/elements/params/actions.js b/src/assets/scenario-builder/components/elements/params/actions.js new file mode 100644 index 0000000..7919352 --- /dev/null +++ b/src/assets/scenario-builder/components/elements/params/actions.js @@ -0,0 +1,19 @@ +export function actionSetParamValues(name, values) { + return { + type: 'SET_PARAM_VALUES', + payload: { + name: name, + values: values, + } + }; +} + +export function actionUpdateParamValues(name, values) { + return { + type: 'UPDATE_PARAM_VALUES', + payload: { + name: name, + values: values, + } + }; +} \ No newline at end of file diff --git a/src/assets/scenario-builder/components/sass/main.scss b/src/assets/scenario-builder/components/sass/main.scss new file mode 100644 index 0000000..a637d0a --- /dev/null +++ b/src/assets/scenario-builder/components/sass/main.scss @@ -0,0 +1,159 @@ +@import '~@projectstorm/react-diagrams/dist/style.min.css'; +@import '../elements/index'; + +.srd-demo-workspace { + background: black; + display: flex; + flex-direction: column; + height: 100%; + border-radius: 5px; + overflow: hidden; + + &__toolbar { + padding: 5px; + display: flex; + flex-shrink: 0; + + button { + background: rgb(60, 60, 60); + font-size: 14px; + padding: 5px 10px; + border: none; + color: white; + outline: none; + cursor: pointer; + margin: 2px; + border-radius: 3px; + + &:hover { + background: rgb(0, 192, 255); + } + } + } + + &__content { + flex-grow: 1; + height: 100%; + } +} + +.docs-preview-wrapper { + background: rgb(60, 60, 60); + border-radius: 10px; + overflow: hidden; + padding: 10px; + margin-top: 20px; + margin-bottom: 20px; +} + +.srd-demo-canvas { + height: 100vh; + min-height: 300px; + + background-color: rgba(0, 0, 0, 0.05) !important; + $color: rgba(0, 0, 0, 0.06); + background-size: 50px 50px; + + .pointui { + fill: rgba(white, 0.5); + } +} + +.diagram-layer { + position: relative; + top: 64px; +} + +.body { + flex-grow: 1; + display: flex; + flex-direction: column; + min-height: 100%; + + .tray-item { + cursor: move; + cursor: grab; + cursor: -moz-grab; + cursor: -webkit-grab; + } + + .tray-item:active { + cursor: grabbing; + cursor: -moz-grabbing; + cursor: -webkit-grabbing; + } +} + +.scenario-name { + &:hover { + cursor: pointer; + } + width: 100%; + display: inline-block; +} +.changing-name-input { + font-size: 20px; + width: 100%; + border: 0; + padding: 2px 5px; +} + +.circular-loading { + margin-right: 15px; + position: relative; + top: 7px; +} + +.toast-success { + background-color: #5d9d52 !important; +} + +.toast-error { + background-color: #c33e37 !important; +} + +.toast-info { + background-color: #3477cb !important; +} + +.toast-warning { + background-color: #f3a33a !important; +} + +.toast__icon { + font-size: 20px; +} + +.toast__icon-variant { + opacity: 0.9; + margin-right: 10px; +} + +.toast__message { + display: flex; + align-items: center; +} + +.node-tooltip-loader { + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} + +.statistic-badge-container-bottom { + width: 100%; + display: flex; + justify-content: center; +} + +.statistic-badge-right { + position: absolute; + top: 15px; + left: 60px; +} + +.statistic-badge-bottom { + position: absolute; + top: 25px; +} diff --git a/src/assets/scenario-builder/components/widgets/BodyWidget.js b/src/assets/scenario-builder/components/widgets/BodyWidget.js new file mode 100755 index 0000000..7eddfb1 --- /dev/null +++ b/src/assets/scenario-builder/components/widgets/BodyWidget.js @@ -0,0 +1,572 @@ +import * as React from 'react'; +import axios from 'axios'; +import { connect } from 'react-redux'; +import compose from 'recompose/compose'; +import { DiagramWidget, NodeModel } from '@projectstorm/react-diagrams'; +import Button from '@material-ui/core/Button'; +import Drawer from '@material-ui/core/Drawer'; +import AppBar from '@material-ui/core/AppBar'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Toolbar from '@material-ui/core/Toolbar'; +import List from '@material-ui/core/List'; +import ListSubheader from '@material-ui/core/ListSubheader'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; +import { withStyles } from '@material-ui/core/styles'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import EmailIcon from '@material-ui/icons/Mail'; +import ExtensionIcon from '@material-ui/icons/Extension'; +import BannerIcon from '@material-ui/icons/Adjust'; +import TriggerIcon from '@material-ui/icons/Notifications'; +import WaitIcon from '@material-ui/icons/AccessAlarmsOutlined'; +import SegmentIcon from '@material-ui/icons/SubdirectoryArrowRight'; +import ConditionIcon from '@material-ui/icons/CallSplit'; +import GoalIcon from '@material-ui/icons/CheckBox'; +import NotificationsActiveIcon from '@material-ui/icons/NotificationsActive'; +import PushNotificationIcon from '@material-ui/icons/PhonelinkRing'; +import ABTestIcon from '@material-ui/icons/SwapVert'; + +import * as config from './../../config'; +import { TrayItemWidget } from './TrayItemWidget'; +import { ExportService } from '../../services/ExportService'; +import Notification from '../Notification'; +import { + Email, + Generic, + Segment, + Trigger, + BeforeTrigger, + Wait, + Goal, + Banner, + Condition, + PushNotification, + ABTest +} from './../elements'; +import { + setScenarioId, + setScenarioName, + setCanvasNotification, + setScenarioLoading +} from '../../actions'; +import {ZoomIn, ZoomOut, ZoomOutMap} from "@material-ui/icons"; +import {Divider} from "@material-ui/core"; + +const drawerWidth = 240; + +const styles = theme => ({ + root: { + display: 'flex' + }, + appBar: { + zIndex: theme.zIndex.drawer + 1 + }, + drawer: { + width: drawerWidth, + flexShrink: 0 + }, + drawerPaper: { + width: drawerWidth + }, + content: { + flexGrow: 1, + padding: 0 + }, + toolbar: theme.mixins.toolbar +}); + +const ctrlKey = 17, + cmdKey = 91, + vKey = 86, + cKey = 67; + +class BodyWidget extends React.Component { + constructor(props) { + super(props); + this.state = { + editingName: false, + editedName: '' + }; + + this.ctrlDown = false; + this.nodesToCopy = []; + + // Required to bind 'this' inside callback methods + this.keydownHandler = this.keydownHandler.bind(this); + this.keyupHandler = this.keyupHandler.bind(this); + this.copyNode = this.copyNode.bind(this); + } + + copyNode(nodeId) { + let offset = { x: 75, y: 75 }; + let model = this.props.app.getDiagramEngine().getDiagramModel(); + let nodes = model.getNodes(); + + if (nodes[nodeId] !== undefined) { + let newNode = nodes[nodeId].clone({}); + newNode.setPosition(newNode.x + offset.x, newNode.y + offset.y); + newNode.selected = false; + model.addNode(newNode); + this.forceUpdate(); + } else { + console.warn("Unable to copy node with ID " + nodeId); + } + } + + keydownHandler(e) { + if (e.keyCode === ctrlKey || e.keyCode === cmdKey) { + this.ctrlDown = true; + } + + // CTRL/CMD + C + if (this.ctrlDown && (e.keyCode === cKey)) { + let model = this.props.app.getDiagramEngine().getDiagramModel(); + this.nodesToCopy = []; + for (const node of model.getSelectedItems()) { + // currently do not allow to copy links + if (node.selected && node instanceof NodeModel) { + this.nodesToCopy.push(node.id); + } + } + } + + // CTRL/CMD + V + if (this.ctrlDown && (e.keyCode === vKey)) { + for (const nodeId of this.nodesToCopy) { + this.copyNode(nodeId); + } + this.nodesToCopy = []; + } + } + + keyupHandler(e) { + if (e.keyCode === ctrlKey || e.keyCode === cmdKey) { + this.ctrlDown = false; + } + } + + componentDidMount() { + document.addEventListener('keydown', this.keydownHandler); + document.addEventListener('keyup', this.keyupHandler); + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.keydownHandler); + document.removeEventListener('keyup', this.keyupHandler); + } + + componentDidUpdate(prevProps) { + // Typical usage (don't forget to compare props): + if (this.props.app.isCorruptedPayload() === true && prevProps.app.isCorruptedPayload() === false) { + this.props.dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Unable to load corrupted scenario.' + }) + ); + } + } + + saveChanges = () => { + const { dispatch } = this.props; + + // Check for corruption to prevent override + if (this.props.app.isCorruptedPayload()) { + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: 'Cannot modify corrupted scenario.' + }) + ); + return; + }; + + const exportService = new ExportService( + this.props.app.getDiagramEngine().getDiagramModel() + ); + + const payload = { + name: this.props.scenario.name, + ...exportService.exportPayload() + }; + + const scenarioId = this.props.scenario.id; + if (scenarioId) { + payload.id = scenarioId; + } + + dispatch(setScenarioLoading(true)); + + axios + .post(`${config.URL_SCENARIO_CREATE}`, payload) + .then(response => { + dispatch(setScenarioId(response.data.id)); + dispatch(setScenarioLoading(false)); + dispatch( + setCanvasNotification({ + open: true, + variant: 'success', + text: 'Scenario saving succeeded.' + }) + ); + }) + .catch(error => { + dispatch(setScenarioLoading(false)); + + let errorMessage = 'Scenario saving failed.'; + if (error.response.data && error.response.data.message) { + errorMessage = error.response.data.message; + } + + dispatch( + setCanvasNotification({ + open: true, + variant: 'error', + text: errorMessage + }) + ); + console.log(error); + }); + }; + + startEditingName = () => { + this.setState({ + editedName: this.props.scenario.name, + editingName: true + }); + }; + + cancelEditingName = () => { + this.setState({ + editedName: '', + editingName: false + }); + }; + + submitEditingName = () => { + if (this.state.editedName.trim().length === 0) { + this.cancelEditingName(); + return; + } + + this.props.dispatch(setScenarioName(this.state.editedName)); + this.setState({ + editedName: '', + editingName: false + }); + }; + + handleCloseAndSaveDuringChangingName = event => { + if (event.keyCode === 27) { + this.cancelEditingName(); + } else if (event.keyCode === 13) { + this.submitEditingName(); + } + }; + + handleNameTyping = event => { + this.setState({ + editedName: event.target.value + }); + }; + + closeNotification = () => { + this.props.dispatch(setCanvasNotification({ open: false })); + }; + + zoomOut = () => { + let zoomLevel = this.props.app.diagramEngine.getDiagramModel().getZoomLevel(); + this.props.app.diagramEngine.getDiagramModel().setZoomLevel(zoomLevel - 5); + this.props.app.diagramEngine.repaintCanvas(); + }; + + zoomIn = () => { + let zoomLevel = this.props.app.diagramEngine.getDiagramModel().getZoomLevel(); + this.props.app.diagramEngine.getDiagramModel().setZoomLevel(zoomLevel + 5); + this.props.app.diagramEngine.repaintCanvas(); + }; + + zoomToFit = () => { + this.props.app.diagramEngine.zoomToFit(); + }; + + render() { + const { classes, canvas } = this.props; + + const diagramProps = { + className: 'srd-demo-canvas', + diagramEngine: this.props.app.getDiagramEngine(), + maxNumberPointsPerLink: 0, + allowLooseLinks: false, + allowCanvasTranslation: canvas.pannable, + allowCanvasZoom: canvas.zoomable + }; // as DiagramProps; + + return ( +
+
+ + + + + + + {this.state.editingName ? ( + + ) : ( + + {this.props.scenario.name} + + )} + + + + + + {!!this.props.scenario.loading && ( + + )} + + + + + + + + + + + +
+ Triggers + } + > + } + /> + + } + /> + + + Actions} + > + } + /> + + } + /> + + {config.BANNER_ENABLED && + } + /> + } + + {config.PUSH_NOTIFICATION_ENABLED && + } + /> + } + + + + Operations + } + > + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + + + +
+
{ + const stormDiagramNode = event.dataTransfer.getData( + 'storm-diagram-node' + ); + if (!stormDiagramNode) return; + var data = JSON.parse(stormDiagramNode); + + var node = null; + if (data.type === 'email') { + node = new Email.NodeModel({}); + } else if (data.type === 'generic') { + node = new Generic.NodeModel({}); + } else if (data.type === 'banner') { + node = new Banner.NodeModel({ + expiresInUnit: 'days', + expiresInTime: 1, + }); + } else if (data.type === 'push_notification') { + node = new PushNotification.NodeModel({}); + } else if (data.type === 'segment') { + node = new Segment.NodeModel({}); + } else if (data.type === 'condition') { + node = new Condition.NodeModel({}); + } else if (data.type === 'trigger') { + node = new Trigger.NodeModel({}); + } else if (data.type === 'before_trigger') { + node = new BeforeTrigger.NodeModel({}); + } else if (data.type === 'wait') { + node = new Wait.NodeModel({}); + } else if (data.type === 'goal') { + node = new Goal.NodeModel({ + recheckPeriodUnit: 'hours', + recheckPeriodTime: 1, + }); + } else if (data.type === 'ab_test') { + node = new ABTest.NodeModel({ + name: "AB Test", + scenarioName: this.props.scenario.name + }); + } + var points = this.props.app + .getDiagramEngine() + .getRelativeMousePoint(event); + node.x = points.x; + node.y = points.y; + this.props.app + .getDiagramEngine() + .getDiagramModel() + .addNode(node); + this.forceUpdate(); + }} + onDragOver={event => { + event.preventDefault(); + }} + > + +
+
+
+
+ ); + } +} + +function mapStateToProps(state) { + return { + canvas: state.canvas, + scenario: state.scenario + }; +} + +export default compose( + withStyles(styles, { name: 'BodyWidget' }), + connect( + mapStateToProps, + null + ) +)(BodyWidget); diff --git a/src/assets/scenario-builder/components/widgets/PortWidget.js b/src/assets/scenario-builder/components/widgets/PortWidget.js new file mode 100644 index 0000000..d5a54b3 --- /dev/null +++ b/src/assets/scenario-builder/components/widgets/PortWidget.js @@ -0,0 +1,38 @@ +import * as React from 'react'; + +import { PortWidget as BasePortWidget } from '@projectstorm/react-diagrams'; + +export class PortWidget extends BasePortWidget { + constructor(props) { + super(props); + this.state = { + selected: false + }; + } + + getClassName() { + return ( + 'port ' + + super.getClassName() + + (this.state.selected ? this.bem('--selected') : '') + ); + } + + render() { + return ( +
{ + this.setState({ selected: true }); + }} + onMouseLeave={() => { + this.setState({ selected: false }); + }} + data-name={this.props.name} + data-nodeid={this.props.node.getID()} + > + {this.props.children} +
+ ); + } +} diff --git a/src/assets/scenario-builder/components/widgets/TrayItemWidget.js b/src/assets/scenario-builder/components/widgets/TrayItemWidget.js new file mode 100755 index 0000000..5a9aaaa --- /dev/null +++ b/src/assets/scenario-builder/components/widgets/TrayItemWidget.js @@ -0,0 +1,32 @@ +import * as React from 'react'; + +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; + +export class TrayItemWidget extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + + render() { + return ( + { + event.dataTransfer.setData( + 'storm-diagram-node', + JSON.stringify(this.props.model) + ); + }} + className='tray-item' + > + {this.props.icon} + + + ); + } +} diff --git a/src/assets/scenario-builder/config.js b/src/assets/scenario-builder/config.js new file mode 100644 index 0000000..9de0267 --- /dev/null +++ b/src/assets/scenario-builder/config.js @@ -0,0 +1,28 @@ +// window.Scenario = { +// config: { +// AUTH_TOKEN: '', +// CRM_HOST: 'https://predplatne.dennikn.sk', +// SCENARIO_ID: null, +// BANNER_ENABLED: null, +// PUSH_NOTIFICATION_ENABLED: null, +// } +// }; + +export const { AUTH_TOKEN, CRM_HOST, SCENARIO_ID, BANNER_ENABLED, PUSH_NOTIFICATION_ENABLED } = window.Scenario.config; + +export const URL_SCENARIO_DETAIL = `${CRM_HOST}/api/v1/scenarios/info?id=`; +export const URL_SCENARIO_CREATE = `${CRM_HOST}/api/v1/scenarios/create`; +export const URL_SCENARIO_CRITERIA = `${CRM_HOST}/api/v1/scenarios/criteria`; +export const URL_SEGMENTS_INDEX = `${CRM_HOST}/api/v1/segments/list`; +export const URL_TRIGGERS_INDEX = `${CRM_HOST}/api/v1/events/list`; +export const URL_BEFORE_TRIGGERS_INDEX = `${CRM_HOST}/api/v1/event-generators/list`; +export const URL_MAILS_INDEX = `${CRM_HOST}/api/v1/mail-template/list`; +export const URL_GENERICS_INDEX = `${CRM_HOST}/api/v1/scenarios/generics`; +export const URL_GOALS_INDEX = `${CRM_HOST}/api/v1/onboarding-goals/list`; +export const URL_BANNERS_INDEX = `${CRM_HOST}/api/v1/remp/list-banners`; +export const URL_PUSH_NOTIFICATION_TEMPLATES = `${CRM_HOST}/api/v1/onesignal-templates/list`; +export const URL_PUSH_NOTIFICATION_APPLICATIONS = `${CRM_HOST}/api/v1/onesignal-applications/list`; +export const URL_SCENARIO_STATISTIC = `${CRM_HOST}/api/v1/scenarios/stats?id=`; + +export const URL_SEGMENT_NEW = `${CRM_HOST}/segment/stored-segments/new`; +export const URL_SEGMENT_SHOW = `${CRM_HOST}/segment/stored-segments/show/`; \ No newline at end of file diff --git a/src/assets/scenario-builder/index.js b/src/assets/scenario-builder/index.js new file mode 100644 index 0000000..f131976 --- /dev/null +++ b/src/assets/scenario-builder/index.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { render } from 'react-dom'; +import { Provider } from 'react-redux'; +import { createStore, applyMiddleware, compose } from 'redux'; +import thunkMiddleware from 'redux-thunk'; +import axios from 'axios'; + +import rootReducer from './reducers'; +import App from './App'; +import * as config from './config'; + +window.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__ = true; +axios.defaults.headers.common['Authorization'] = config.AUTH_TOKEN; + +const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; + +const store = createStore( + rootReducer, + {}, + composeEnhancers(applyMiddleware(thunkMiddleware)) +); + +render( + + + , + document.getElementById('root') +); diff --git a/src/assets/scenario-builder/public/index.html b/src/assets/scenario-builder/public/index.html new file mode 100644 index 0000000..35b319a --- /dev/null +++ b/src/assets/scenario-builder/public/index.html @@ -0,0 +1,34 @@ + + + + + + + remp-scenario + + + +
+ + + diff --git a/src/assets/scenario-builder/reducers/BannersReducer.js b/src/assets/scenario-builder/reducers/BannersReducer.js new file mode 100644 index 0000000..96e1a96 --- /dev/null +++ b/src/assets/scenario-builder/reducers/BannersReducer.js @@ -0,0 +1,15 @@ +import { BANNERS_CHANGED } from '../actions/types'; + +const INITIAL_STATE = { + availableBanners: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case BANNERS_CHANGED: + return { ...state, availableBanners: action.payload }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/BeforeTriggersReducer.js b/src/assets/scenario-builder/reducers/BeforeTriggersReducer.js new file mode 100644 index 0000000..e582103 --- /dev/null +++ b/src/assets/scenario-builder/reducers/BeforeTriggersReducer.js @@ -0,0 +1,15 @@ +import {BEFORE_TRIGGERS_CHANGED} from '../actions/types'; + +const INITIAL_STATE = { + availableBeforeTriggers: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case BEFORE_TRIGGERS_CHANGED: + return { ...state, availableBeforeTriggers: action.payload }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/CanvasReducer.js b/src/assets/scenario-builder/reducers/CanvasReducer.js new file mode 100644 index 0000000..f5bd929 --- /dev/null +++ b/src/assets/scenario-builder/reducers/CanvasReducer.js @@ -0,0 +1,38 @@ +import { + CANVAS_PANNABLE, + CANVAS_ZOOMABLE, + CANVAS_ZOOMABLE_PANNABLE, + CANVAS_NOTIFICATION +} from './../actions/types'; + +const INITIAL_STATE = { + pannable: true, + zoomable: true, + notification: { + open: false, + variant: 'success', + text: '' + } +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case CANVAS_PANNABLE: + return { ...state, pannable: action.payload }; + + case CANVAS_ZOOMABLE: + return { ...state, zoomable: action.payload }; + + case CANVAS_ZOOMABLE_PANNABLE: + return { ...state, zoomable: action.payload, pannable: action.payload }; + + case CANVAS_NOTIFICATION: + return { + ...state, + notification: { ...state.notification, ...action.payload } + }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/CriteriaReducer.js b/src/assets/scenario-builder/reducers/CriteriaReducer.js new file mode 100644 index 0000000..d12ca7e --- /dev/null +++ b/src/assets/scenario-builder/reducers/CriteriaReducer.js @@ -0,0 +1,18 @@ + +import { + CRITERIA_CHANGED +} from './../actions/types'; + +const INITIAL_STATE = { + criteria: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case CRITERIA_CHANGED: + return { ...state, criteria: action.payload }; + + default: + return state; + } +}; \ No newline at end of file diff --git a/src/assets/scenario-builder/reducers/GenericsReducer.js b/src/assets/scenario-builder/reducers/GenericsReducer.js new file mode 100644 index 0000000..fd48c7a --- /dev/null +++ b/src/assets/scenario-builder/reducers/GenericsReducer.js @@ -0,0 +1,15 @@ +import { GENERICS_CHANGED } from './../actions/types'; + +const INITIAL_STATE = { + generics: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case GENERICS_CHANGED: + return { ...state, generics: action.payload }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/GoalsReducer.js b/src/assets/scenario-builder/reducers/GoalsReducer.js new file mode 100644 index 0000000..d096d28 --- /dev/null +++ b/src/assets/scenario-builder/reducers/GoalsReducer.js @@ -0,0 +1,15 @@ +import { GOALS_CHANGED } from './../actions/types'; + +const INITIAL_STATE = { + availableGoals: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case GOALS_CHANGED: + return { ...state, availableGoals: action.payload }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/MailsReducer.js b/src/assets/scenario-builder/reducers/MailsReducer.js new file mode 100644 index 0000000..049abfe --- /dev/null +++ b/src/assets/scenario-builder/reducers/MailsReducer.js @@ -0,0 +1,15 @@ +import { MAILS_CHANGED } from './../actions/types'; + +const INITIAL_STATE = { + availableMails: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case MAILS_CHANGED: + return { ...state, availableMails: action.payload }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/PushNotificationsReducer.js b/src/assets/scenario-builder/reducers/PushNotificationsReducer.js new file mode 100644 index 0000000..134ecec --- /dev/null +++ b/src/assets/scenario-builder/reducers/PushNotificationsReducer.js @@ -0,0 +1,20 @@ +import { PUSH_NOTIFICATIONS_CHANGED } from './../actions/types'; + +const INITIAL_STATE = { + availableTemplates: [], + availableApplications: [], +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case PUSH_NOTIFICATIONS_CHANGED: + return { + ...state, + availableTemplates: action.payload.templates, + availableApplications: action.payload.applications, + }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/ScenarioReducer.js b/src/assets/scenario-builder/reducers/ScenarioReducer.js new file mode 100644 index 0000000..fc9651d --- /dev/null +++ b/src/assets/scenario-builder/reducers/ScenarioReducer.js @@ -0,0 +1,38 @@ +import { + SET_SCENARIO_ID, + SET_SCENARIO_NAME, + SET_SCENARIO_LOADING, + SET_SCENARIO_PAYLOAD +} from './../actions/types'; + +const INITIAL_STATE = { + id: null, + name: '', + loading: 0, + payload: null +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case SET_SCENARIO_ID: + return { ...state, id: action.payload }; + + case SET_SCENARIO_NAME: + return { ...state, name: action.payload }; + + case SET_SCENARIO_LOADING: + let loading = state.loading; + if (action.payload) { + ++loading; + } else { + --loading; + } + return { ...state, loading }; + + case SET_SCENARIO_PAYLOAD: + return { ...state, payload: action.payload }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/SegmentsReducer.js b/src/assets/scenario-builder/reducers/SegmentsReducer.js new file mode 100644 index 0000000..7320de9 --- /dev/null +++ b/src/assets/scenario-builder/reducers/SegmentsReducer.js @@ -0,0 +1,18 @@ + +import { + SEGMENTS_CHANGED +} from './../actions/types'; + +const INITIAL_STATE = { + avalaibleSegments: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case SEGMENTS_CHANGED: + return { ...state, avalaibleSegments: action.payload }; + + default: + return state; + } +}; \ No newline at end of file diff --git a/src/assets/scenario-builder/reducers/StatisticsReducer.js b/src/assets/scenario-builder/reducers/StatisticsReducer.js new file mode 100644 index 0000000..780f2ea --- /dev/null +++ b/src/assets/scenario-builder/reducers/StatisticsReducer.js @@ -0,0 +1,19 @@ +import {STATISTICS_CHANGED} from "../actions/types"; + +const INITIAL_STATE = { + statistics: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case STATISTICS_CHANGED: + return { + ...state, + statistics: action.payload.statistics, + }; + + default: + return state; + + } +}; \ No newline at end of file diff --git a/src/assets/scenario-builder/reducers/TriggersReducer.js b/src/assets/scenario-builder/reducers/TriggersReducer.js new file mode 100644 index 0000000..37b2ad3 --- /dev/null +++ b/src/assets/scenario-builder/reducers/TriggersReducer.js @@ -0,0 +1,15 @@ +import { TRIGGERS_CHANGED } from './../actions/types'; + +const INITIAL_STATE = { + avalaibleTriggers: [] +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case TRIGGERS_CHANGED: + return { ...state, avalaibleTriggers: action.payload }; + + default: + return state; + } +}; diff --git a/src/assets/scenario-builder/reducers/index.js b/src/assets/scenario-builder/reducers/index.js new file mode 100644 index 0000000..78436e6 --- /dev/null +++ b/src/assets/scenario-builder/reducers/index.js @@ -0,0 +1,28 @@ +import { combineReducers } from 'redux'; +import SegmentsReducer from './SegmentsReducer'; +import CriteriaReducer from './CriteriaReducer'; +import TriggersReducer from './TriggersReducer'; +import BeforeTriggersReducer from './BeforeTriggersReducer'; +import CanvasReducer from './CanvasReducer'; +import ScenarioReducer from './ScenarioReducer'; +import MailsReducer from './MailsReducer'; +import GenericsReducer from './GenericsReducer'; +import GoalsReducer from './GoalsReducer'; +import BannersReducer from './BannersReducer'; +import PushNotificationsReducer from './PushNotificationsReducer'; +import StatisticsReducer from "./StatisticsReducer"; + +export default combineReducers({ + segments: SegmentsReducer, + triggers: TriggersReducer, + beforeTriggers: BeforeTriggersReducer, + canvas: CanvasReducer, + criteria: CriteriaReducer, + scenario: ScenarioReducer, + mails: MailsReducer, + generics: GenericsReducer, + goals: GoalsReducer, + banners: BannersReducer, + pushNotifications: PushNotificationsReducer, + statistics: StatisticsReducer, +}); diff --git a/src/assets/scenario-builder/services/ExportService.js b/src/assets/scenario-builder/services/ExportService.js new file mode 100644 index 0000000..0c2c284 --- /dev/null +++ b/src/assets/scenario-builder/services/ExportService.js @@ -0,0 +1,244 @@ +function unitTimeToMinutes(time, unit) { + switch (unit) { + case 'minutes': + return parseInt(time); + case 'hours': + return time * 60; + case 'days': + return time * 60 * 24; + default: + return parseInt(time); + } +} + +export class ExportService { + constructor(model) { + this.model = model; + } + + exportPayload() { + const payload = {}; + const serializedModel = this.model.serializeDiagram(); + const triggers = ['trigger', 'before_trigger']; + + payload.triggers = {}; + payload.elements = {}; + + payload.visual = {}; + + serializedModel.nodes + .filter(node => triggers.includes(node.type)) + .map(node => (payload.triggers[node.id] = this.formatNode(node))); + + Object.entries(this.model.getNodes()).forEach(node => { + payload.visual[node[0]] = { + x: node[1].x, + y: node[1].y + }; + }); + + Object.entries(this.model.getNodes()).forEach(node => { + + if (!triggers.includes(node[1].type)) { + payload.elements[node[0]] = this.formatNode(node[1].serialize()); + } + }); + + return payload; + } + + getAllChildrenNodes(node, portName = 'right') { + const port = node.ports.find(port => port.name === portName); + + let childrenNodes = port.links.map(link => { + let nextNode = null; + + if (this.model.links[link].targetPort.parent.id !== node.id) { + nextNode = this.model.links[link].targetPort.parent; + } else { + nextNode = this.model.links[link].sourcePort.parent; + } + + return { ...nextNode.serialize(), portName }; + }); + + return childrenNodes; + } + + getPositiveAndNegativeDescendants(node) { + const descendantsPositive = this.getAllChildrenNodes(node, 'right').map( + descendantNode => this.formatDescendant(descendantNode, node) + ); + const descendantsNegative = this.getAllChildrenNodes(node, 'bottom').map( + descendantNode => this.formatDescendant(descendantNode, node) + ); + return [...descendantsPositive, ...descendantsNegative]; + } + + formatNode(node) { + if (node.type === 'email') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'email', + email: { + code: node.selectedMail, + descendants: this.getAllChildrenNodes(node).map(descendantNode => + this.formatDescendant(descendantNode, node) + ) + } + }; + } else if (node.type === 'banner') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'banner', + banner: { + id: node.selectedBanner, + expiresInMinutes: unitTimeToMinutes(node.expiresInTime, node.expiresInUnit), + descendants: this.getAllChildrenNodes(node).map(descendantNode => + this.formatDescendant(descendantNode, node) + ) + } + }; + } else if (node.type === 'generic') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'generic', + generic: { + code: node.selectedGeneric, + options: node.options, + descendants: this.getAllChildrenNodes(node).map(descendantNode => + this.formatDescendant(descendantNode, node) + ) + } + }; + } else if (node.type === 'condition') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'condition', + condition: { + conditions: node.conditions, + descendants: this.getPositiveAndNegativeDescendants(node), + } + }; + } else if (node.type === 'segment') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'segment', + segment: { + code: node.selectedSegment ?? null, + descendants: this.getPositiveAndNegativeDescendants(node), + } + }; + } else if (node.type === 'trigger') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'event', + event: { + code: node.selectedTrigger ?? null + }, + elements: this.getAllChildrenNodes(node).map( + descendantNode => descendantNode.id + ) + }; + } else if (node.type === 'before_trigger') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'before_event', + event: { + code: node.selectedTrigger ?? null + }, + elements: this.getAllChildrenNodes(node).map( + descendantNode => descendantNode.id + ), + options: { + minutes: unitTimeToMinutes(node.time, node.timeUnit) + } + }; + } else if (node.type === 'wait') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'wait', + wait: { + minutes: unitTimeToMinutes(node.waitingTime, node.waitingUnit), + descendants: this.getAllChildrenNodes(node).map(descendantNode => + this.formatDescendant(descendantNode, node) + ) + } + }; + } else if (node.type === 'goal') { + let goalProperties = { + codes: node.selectedGoals ? node.selectedGoals : [], + descendants: this.getPositiveAndNegativeDescendants(node), + recheckPeriodMinutes: unitTimeToMinutes(node.recheckPeriodTime, node.recheckPeriodUnit) + }; + + if (node.timeoutTime && node.timeoutUnit) { + goalProperties.timeoutMinutes = unitTimeToMinutes(node.timeoutTime, node.timeoutUnit); + } + + return { + id: node.id, + name: node.name ? node.name : '', + type: 'goal', + goal: goalProperties, + }; + } else if (node.type === 'push_notification') { + return { + id: node.id, + name: node.name ? node.name : '', + type: 'push_notification', + push_notification: { + template: node.selectedTemplate, + application: node.selectedApplication, + descendants: this.getAllChildrenNodes(node).map(descendantNode => + this.formatDescendant(descendantNode, node) + ) + } + } + } else if (node.type === 'ab_test') { + let descendants = node.ports + .filter(port => port.name.startsWith('right.')) + .flatMap(port => this.getAllChildrenNodes(node, port.name) + .map(descendantNode => this.formatDescendant(descendantNode, node) + ) + ); + + return { + id: node.id, + name: node.name ? node.name : '', + type: 'ab_test', + ab_test: { + variants: node.variants, + descendants: descendants + } + } + } + } + + formatDescendant = (node, parentNode, index = 0) => { + let descendant = { + uuid: node.id + }; + + if (parentNode.type === 'segment') { + descendant.direction = node.portName === 'right' ? 'positive' : 'negative'; + } else if (parentNode.type === 'goal') { + descendant.direction = node.portName === 'right' ? 'positive' : 'negative'; + } else if (parentNode.type === 'condition') { + descendant.direction = node.portName === 'right' ? 'positive' : 'negative'; + } else if (parentNode.type === 'ab_test') { + descendant.direction = 'positive'; + descendant.position = node.portName.split('.')[1]; + } + + return descendant; + }; +} diff --git a/src/assets/scenario-builder/services/RenderService.js b/src/assets/scenario-builder/services/RenderService.js new file mode 100644 index 0000000..d62803b --- /dev/null +++ b/src/assets/scenario-builder/services/RenderService.js @@ -0,0 +1,201 @@ +import flatMap from 'lodash/flatMap'; + +// import the custom models +import { + Banner, + Email, + Segment, + Trigger, + Wait, + Goal, + Condition, + BeforeTrigger, + Generic, + PushNotification, + ABTest +} from './../components/elements'; +import { BANNER_ENABLED } from './../config'; + +function minutesToTimeUnit(minutes) { + if (minutes === 0) { + return { + unit: 'minutes', + time: minutes + }; + } + + if (minutes % 1440 === 0) { + return { + unit: 'days', + time: minutes / 1440 + }; + } else if (minutes % 60 === 0) { + return { + unit: 'hours', + time: minutes / 60 + }; + } + return { + unit: 'minutes', + time: minutes + }; +} + +export class RenderService { + constructor(activeModel, payload = {}) { + this.activeModel = activeModel; + this.payload = payload; + } + + renderPayload(payload) { + this.payload = payload; + + const visual = this.payload.visual; + + //render Nodes + flatMap(payload.elements, element => { + let node = null; + + if (element.type === 'segment') { + + element.selectedSegment = element.segment.code; + node = new Segment.NodeModel(element); + + } else if (element.type === 'condition') { + + node = new Condition.NodeModel({ + id: element.id, + name: element.name, + conditions: element.condition.conditions + }); + + } else if (element.type === 'email') { + + element.selectedMail = element.email.code; + node = new Email.NodeModel(element); + + } else if (element.type === 'generic') { + + element.selectedGeneric = element.generic.code; + element.options = element.generic.options; + node = new Generic.NodeModel(element); + + } else if (element.type === 'wait') { + + const timeUnit = minutesToTimeUnit(element.wait.minutes); + element.waitingUnit = timeUnit.unit; + element.waitingTime = timeUnit.time; + + node = new Wait.NodeModel(element); + + } else if (element.type === 'banner') { + + if (!BANNER_ENABLED) { + throw Error("BANNER_ENABLED configuration is false, but loaded scenario contains banner element."); + } + + const timeUnit = minutesToTimeUnit(element.banner.expiresInMinutes); + element.expiresInUnit = timeUnit.unit; + element.expiresInTime = timeUnit.time; + + element.selectedBanner = element.banner.id; + + node = new Banner.NodeModel(element); + + } else if (element.type === 'goal') { + + if (element.goal.hasOwnProperty("timeoutMinutes")) { + const timeUnit = minutesToTimeUnit(element.goal.timeoutMinutes); + element.timeoutUnit = timeUnit.unit; + element.timeoutTime = timeUnit.time; + } + + const recheckPeriodTimeUnit = minutesToTimeUnit(element.goal.recheckPeriodMinutes); + element.recheckPeriodUnit = recheckPeriodTimeUnit.unit; + element.recheckPeriodTime = recheckPeriodTimeUnit.time; + + element.selectedGoals = element.goal.codes; + + node = new Goal.NodeModel(element); + + } else if (element.type === 'push_notification') { + + element.selectedTemplate = element.push_notification.template; + element.selectedApplication = element.push_notification.application; + + node = new PushNotification.NodeModel(element); + + } else if (element.type === 'ab_test') { + + node = new ABTest.NodeModel({ + 'id': element.id, + 'name': element.name, + 'variants': element.ab_test.variants, + 'scenarioName': this.payload.name + }); + } + + this.activeModel.addNode(node); + node.setPosition(visual[element.id].x, visual[element.id].y); + }); + + // link nodes + flatMap(payload.elements, element => { + let sourceNode = this.activeModel.getNode(element.id); + + element[element.type].descendants.forEach(item => { + this.linkNodes(sourceNode, this.activeModel.getNode(item.uuid), item.direction, item.position); + }); + }); + + // renderTriggers + flatMap(payload.triggers, trigger => { + + let node = null; + if (trigger.type === 'event') { + + trigger.selectedTrigger = trigger.event.code; + node = new Trigger.NodeModel(trigger); + + } else if (trigger.type === 'before_event') { + + const timeUnit = minutesToTimeUnit(trigger.options.minutes); + trigger.timeUnit = timeUnit.unit; + trigger.time = timeUnit.time; + trigger.selectedTrigger = trigger.event.code; + + node = new BeforeTrigger.NodeModel(trigger); + } + + this.activeModel.addNode(node); + node.setPosition(visual[trigger.id].x, visual[trigger.id].y); + + // link triggers with nodes + trigger.elements.forEach(element => { + this.linkNodes(node, this.activeModel.getNode(element)); + }); + }); + } + + linkNodes(sourceNode, targetNode, direction, position = 0) { + if (direction){ + if (direction === 'positive') { + let link; + if (sourceNode.type === 'ab_test') { + link = sourceNode.getPort('right.' + position).link(targetNode.getPort('left')); + } else { + link = sourceNode.getPort('right').link(targetNode.getPort('left')); + } + this.activeModel.addLink(link); + return; + } else if (direction === 'negative') { + const link = sourceNode.getPort('bottom').link(targetNode.getPort('left')); + this.activeModel.addLink(link); + return; + } + } + + const link = sourceNode.getPort('right').link(targetNode.getPort('left')); + this.activeModel.addLink(link); + } +} diff --git a/src/assets/scenario-builder/services/index.js b/src/assets/scenario-builder/services/index.js new file mode 100644 index 0000000..422e486 --- /dev/null +++ b/src/assets/scenario-builder/services/index.js @@ -0,0 +1,7 @@ +import RenderService from "./RenderService"; +import ExportService from ".ExportService"; + +export { + RenderService, + ExportService +}; diff --git a/src/assets/scenariobuilder/css/2.31f6971f.chunk.css b/src/assets/scenariobuilder/css/2.31f6971f.chunk.css deleted file mode 100644 index ace14ed..0000000 --- a/src/assets/scenariobuilder/css/2.31f6971f.chunk.css +++ /dev/null @@ -1 +0,0 @@ -.srd-diagram{position:relative;flex-grow:1;display:flex;cursor:move;overflow:hidden}.srd-diagram__selector{position:absolute;background-color:rgba(0,192,255,.2);border:2px solid #00c0ff}.srd-link-layer{overflow:visible!important}.srd-link-layer,.srd-node-layer{position:absolute;height:100%;width:100%;transform-origin:0 0;top:0;bottom:0;left:0;right:0}.srd-node-layer{pointer-events:none}.srd-node{position:absolute;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;cursor:move;pointer-events:all}.srd-node--selected>*{border-color:#00c0ff!important}.srd-port{width:15px;height:15px;background:hsla(0,0%,100%,.1)}.srd-port.selected,.srd-port:hover{background:#c0ff00}.srd-default-node{background-color:#1e1e1e;border-radius:5px;font-family:sans-serif;color:#fff;border:2px solid #000;overflow:visible;font-size:11px}.srd-default-node__title{background:rgba(0,0,0,.3);display:flex;white-space:nowrap}.srd-default-node__title>*{align-self:center}.srd-default-node__title .fa{padding:5px;opacity:.2;cursor:pointer}.srd-default-node__title .fa:hover{opacity:1}.srd-default-node__name{flex-grow:1;padding:5px}.srd-default-node__ports{display:flex;background-image:linear-gradient(rgba(0,0,0,.1),rgba(0,0,0,.2))}.srd-default-node__in,.srd-default-node__out{flex-grow:1;display:flex;flex-direction:column}.srd-default-port{display:flex;margin-top:1px}.srd-default-port>*{align-self:center}.srd-default-port__name{padding:0 5px}.srd-default-port--out{justify-content:flex-end}.srd-default-port--out .srd-default-port__name{justify-content:flex-end;text-align:right}.srd-default-label{background:rgba(70,70,70,.8);border:1px solid #333;border-radius:4px;color:#fff;display:inline-block;font-size:smaller;padding:5px}@keyframes dash{0%{stroke-dashoffset:24}to{stroke-dashoffset:0}}.srd-default-link path{fill:none;pointer-events:all}.srd-default-link--path-selected{stroke:#00c0ff!important;stroke-dasharray:10,2;animation:dash 1s linear infinite}.srd-default-link__label{pointer-events:none}.srd-default-link__label>div{display:inline-block;position:absolute}.srd-default-link__point{fill:hsla(0,0%,100%,.5)}.srd-default-link--point-selected{fill:#00c0ff} \ No newline at end of file diff --git a/src/assets/scenariobuilder/css/main.ed16f2f1.chunk.css b/src/assets/scenariobuilder/css/main.ed16f2f1.chunk.css deleted file mode 100644 index 66c6c6c..0000000 --- a/src/assets/scenariobuilder/css/main.ed16f2f1.chunk.css +++ /dev/null @@ -1 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons);.diamond-node,.round-node,.square-node{background-color:initial;display:flex;flex-direction:column;align-items:center;width:150px}.diamond-node .node-container,.round-node .node-container,.square-node .node-container{position:relative;display:flex;align-items:center;width:50px;height:50px;border-radius:5px;overflow:visible;background-color:#fff;transition:all .15s}.diamond-node__title,.round-node__title,.square-node__title{display:flex;text-align:center;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;font-size:14px;font-weight:500}.diamond-node__title>*,.round-node__title>*,.square-node__title>*{align-self:center}.diamond-node__title .fa,.round-node__title .fa,.square-node__title .fa{padding:5px;opacity:.2;cursor:pointer}.diamond-node__title .fa:hover,.round-node__title .fa:hover,.square-node__title .fa:hover{opacity:1}.diamond-node__name,.round-node__name,.square-node__name{flex-grow:1;padding:5px 0}.diamond-node__ports,.round-node__ports,.square-node__ports{display:flex;background-image:none;width:100%}.diamond-node__bottom,.diamond-node__left,.diamond-node__right,.round-node__bottom,.round-node__left,.round-node__right,.square-node__bottom,.square-node__left,.square-node__right{flex-grow:1;display:flex;flex-direction:column}.diamond-node__bottom .port,.diamond-node__left .port,.diamond-node__right .port,.round-node__bottom .port,.round-node__left .port,.round-node__right .port,.square-node__bottom .port,.square-node__left .port,.square-node__right .port{border-radius:13px;width:13px;height:13px;position:relative;background:#fff}.diamond-node__bottom .port.selected,.diamond-node__bottom .port:hover,.diamond-node__left .port.selected,.diamond-node__left .port:hover,.diamond-node__right .port.selected,.diamond-node__right .port:hover,.round-node__bottom .port.selected,.round-node__bottom .port:hover,.round-node__left .port.selected,.round-node__left .port:hover,.round-node__right .port.selected,.round-node__right .port:hover,.square-node__bottom .port.selected,.square-node__bottom .port:hover,.square-node__left .port.selected,.square-node__left .port:hover,.square-node__right .port.selected,.square-node__right .port:hover{background:#a291fb}.diamond-node__left,.round-node__left,.square-node__left{align-items:flex-start}.diamond-node__left .port,.round-node__left .port,.square-node__left .port{left:-6.5px}.diamond-node__right,.round-node__right,.square-node__right{align-items:flex-end}.diamond-node__right .port,.round-node__right .port,.square-node__right .port{right:-6.5px}.diamond-node__bottom,.round-node__bottom,.square-node__bottom{align-items:center}.diamond-node__bottom .port,.round-node__bottom .port,.square-node__bottom .port{bottom:-6.5px}.diamond-node__icon,.round-node__icon,.square-node__icon{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center}.round-node .node-container{border-radius:50px}.diamond-node__bottom{position:absolute;bottom:0;width:100%}.srd-node--selected .square-node.email-node .node-container{box-shadow:0 0 10px #a291fb}.email-node .node-container{border:2px solid #a291fb;color:#a291fb}.email-node__title{color:#a291fb}.email-node__left .port,.email-node__right .port{box-shadow:0 0 10px #a291fb}.email-node__left .port.selected,.email-node__left .port:hover,.email-node__right .port.selected,.email-node__right .port:hover{background:#a291fb}.srd-node--selected .square-node.generic-node .node-container{box-shadow:0 0 10px #a291fb}.generic-node .node-container{border:2px solid #a291fb;color:#a291fb}.generic-node__title{color:#a291fb}.generic-node__left .port,.generic-node__right .port{box-shadow:0 0 10px #a291fb}.generic-node__left .port.selected,.generic-node__left .port:hover,.generic-node__right .port.selected,.generic-node__right .port:hover{background:#a291fb}.srd-node--selected .diamond-node.segment-node .node-container{box-shadow:0 0 10px #ff695e}.segment-node .node-container{border:2px solid #ff695e;color:#ff695e}.segment-node__title{color:#ff695e}.segment-node__bottom .port,.segment-node__left .port,.segment-node__right .port{box-shadow:0 0 10px #ff695e}.segment-node__bottom .port.selected,.segment-node__bottom .port:hover,.segment-node__left .port.selected,.segment-node__left .port:hover,.segment-node__right .port.selected,.segment-node__right .port:hover{background:#ff695e}.srd-node--selected .square-node.trigger-node .node-container{box-shadow:0 0 10px #2ecc40}.trigger-node .node-container{border:2px solid #2ecc40;color:#2ecc40}.trigger-node__title{color:#2ecc40}.trigger-node__left .port,.trigger-node__right .port{box-shadow:0 0 10px #2ecc40}.trigger-node__left .port.selected,.trigger-node__left .port:hover,.trigger-node__right .port.selected,.trigger-node__right .port:hover{background:#2ecc40}.srd-node--selected .square-node.before-trigger-node .node-container{box-shadow:0 0 10px #00b5ad}.before-trigger-node .node-container{border:2px solid #00b5ad;color:#00b5ad}.before-trigger-node__title{color:#00b5ad}.before-trigger-node__left .port,.before-trigger-node__right .port{box-shadow:0 0 10px #00b5ad}.before-trigger-node__left .port.selected,.before-trigger-node__left .port:hover,.before-trigger-node__right .port.selected,.before-trigger-node__right .port:hover{background:#00b5ad}.srd-node--selected .round-node.wait-node .node-container{box-shadow:0 0 10px #ff851b}.wait-node .node-container{border:2px solid #ff851b;color:#ff851b}.wait-node__title{color:#ff851b}.wait-node__left .port,.wait-node__right .port{box-shadow:0 0 10px #ff851b}.wait-node__left .port.selected,.wait-node__left .port:hover,.wait-node__right .port.selected,.wait-node__right .port:hover{background:#ff851b}@keyframes dash{0%{stroke-dashoffset:24}to{stroke-dashoffset:0}}.srd-default-link path{fill:none;pointer-events:all}.srd-default-link--path-selected{stroke:#54c8ff!important;stroke-dasharray:10,2;animation:dash 1s linear infinite}.srd-default-link__label{pointer-events:none}.srd-default-link__label>div{display:inline-block;position:absolute}.srd-default-link__point{fill:hsla(0,0%,100%,.5)}.srd-default-link--point-selected{fill:#00c0ff}.srd-node--selected .diamond-node.goal-node .node-container{box-shadow:0 0 10px #767676}.goal-node .node-container{border:2px solid #767676;color:#767676}.goal-node__title{color:#767676}.goal-node__bottom .port,.goal-node__left .port,.goal-node__right .port{box-shadow:0 0 10px #767676}.goal-node__bottom .port.selected,.goal-node__bottom .port:hover,.goal-node__left .port.selected,.goal-node__left .port:hover,.goal-node__right .port.selected,.goal-node__right .port:hover{background:#767676}.srd-node--selected .square-node.banner-node .node-container{box-shadow:0 0 10px #6435c9}.banner-node .node-container{border:2px solid #6435c9;color:#6435c9}.banner-node__title{color:#6435c9}.banner-node__left .port,.banner-node__right .port{box-shadow:0 0 10px #6435c9}.banner-node__left .port.selected,.banner-node__left .port:hover,.banner-node__right .port.selected,.banner-node__right .port:hover{background:#6435c9}.srd-node--selected .diamond-node.condition-node .node-container{box-shadow:0 0 10px #f2711c}.condition-node .node-container{border:2px solid #f2711c;color:#f2711c}.condition-node__title{color:#f2711c}.condition-node__bottom .port,.condition-node__left .port,.condition-node__right .port{box-shadow:0 0 10px #f2711c}.condition-node__bottom .port.selected,.condition-node__bottom .port:hover,.condition-node__left .port.selected,.condition-node__left .port:hover,.condition-node__right .port.selected,.condition-node__right .port:hover{background:#f2711c}.srd-node--selected .square-node.push-notification-node .node-container{box-shadow:0 0 10px #dc73ff}.push-notification-node .node-container{border:2px solid #dc73ff;color:#dc73ff}.push-notification-node__title{color:#dc73ff}.push-notification-node__left .port,.push-notification-node__right .port{box-shadow:0 0 10px #dc73ff}.push-notification-node__left .port.selected,.push-notification-node__left .port:hover,.push-notification-node__right .port.selected,.push-notification-node__right .port:hover{background:#dc73ff}.srd-node--selected .diamond-node .abtest-node .node-container{box-shadow:0 0 10px #767676}.abtest-node .node-container{border:2px solid #767676;color:#767676;height:auto}.abtest-node__title{color:#767676}.abtest-node__ports{width:50%;height:100%;flex-direction:column}.abtest-node__right{margin-top:10px;height:20px}.abtest-node__description{display:flex;position:absolute;left:40px;top:-2px;font-size:.8rem;color:#1b1c1d}.abtest-node .port{box-shadow:0 0 10px #767676}.srd-demo-workspace{background:#000;display:flex;flex-direction:column;height:100%;border-radius:5px;overflow:hidden}.srd-demo-workspace__toolbar{padding:5px;display:flex;flex-shrink:0}.srd-demo-workspace__toolbar button{background:#3c3c3c;font-size:14px;padding:5px 10px;border:none;color:#fff;outline:none;cursor:pointer;margin:2px;border-radius:3px}.srd-demo-workspace__toolbar button:hover{background:#00c0ff}.srd-demo-workspace__content{flex-grow:1;height:100%}.docs-preview-wrapper{background:#3c3c3c;border-radius:10px;overflow:hidden;padding:10px;margin-top:20px;margin-bottom:20px}.srd-demo-canvas{height:100vh;min-height:300px;background-color:rgba(0,0,0,.05)!important;background-size:50px 50px}.srd-demo-canvas .pointui{fill:hsla(0,0%,100%,.5)}.diagram-layer{position:relative;top:64px}.body{flex-grow:1;display:flex;flex-direction:column;min-height:100%}.body .tray-item{cursor:move;cursor:grab;cursor:-webkit-grab}.body .tray-item:active{cursor:grabbing;cursor:-webkit-grabbing}.scenario-name{width:100%;display:inline-block}.scenario-name:hover{cursor:pointer}.changing-name-input{font-size:20px;width:100%;border:0;padding:2px 5px}.circular-loading{margin-right:15px;position:relative;top:7px}.toast-success{background-color:#5d9d52!important}.toast-error{background-color:#c33e37!important}.toast-info{background-color:#3477cb!important}.toast-warning{background-color:#f3a33a!important}.toast__icon{font-size:20px}.toast__icon-variant{opacity:.9;margin-right:10px}.node-tooltip-loader,.toast__message{display:flex;align-items:center}.node-tooltip-loader{justify-content:center;padding:20px}.statistic-badge-container-bottom{width:100%;display:flex;justify-content:center}.statistic-badge-right{position:absolute;top:15px;left:60px}.statistic-badge-bottom{position:absolute;top:25px} \ No newline at end of file diff --git a/src/assets/scenariobuilder/js/2.cffd56c7.chunk.js b/src/assets/scenariobuilder/js/2.cffd56c7.chunk.js deleted file mode 100644 index 3784661..0000000 --- a/src/assets/scenariobuilder/js/2.cffd56c7.chunk.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see 2.cffd56c7.chunk.js.LICENSE.txt */ -(this["webpackJsonpremp-scenario-builder"]=this["webpackJsonpremp-scenario-builder"]||[]).push([[2],[function(e,t,n){"use strict";e.exports=n(195)},function(e,t,n){"use strict";function r(){return(r=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}},function(e,t,n){"use strict";function r(e){var t,n,o="";if("string"==typeof e||"number"==typeof e)o+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;t1&&void 0!==arguments[1]?arguments[1]:{};return function(n){var i=t.defaultTheme,l=t.withTheme,f=void 0!==l&&l,p=t.name,h=Object(o.a)(t,["defaultTheme","withTheme","name"]);var m=p,v=Object(u.a)(e,Object(r.a)({defaultTheme:i,Component:n,name:p||n.displayName,classNamePrefix:m},h)),g=a.a.forwardRef((function(e,t){e.classes;var l,s=e.innerRef,u=Object(o.a)(e,["classes","innerRef"]),h=v(Object(r.a)({},n.defaultProps,e)),m=u;return("string"===typeof p||f)&&(l=Object(d.a)()||i,p&&(m=Object(c.a)({theme:l,name:p,props:u})),f&&!m.theme&&(m.theme=l)),a.a.createElement(n,Object(r.a)({ref:s||t,classes:h},m))}));return s()(g,n),g}},p=n(52);t.a=function(e,t){return f(e,Object(r.a)({defaultTheme:p.a},t))}},,function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(173);function o(e){if("string"!==typeof e)throw new Error(Object(r.a)(7));return e.charAt(0).toUpperCase()+e.slice(1)}},function(e,t,n){window,e.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=17)}([function(e,t){e.exports=n(14)},function(e,t){e.exports=n(0)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1);t.BaseWidget=class extends r.Component{constructor(e,t){super(t),this.className=e}bem(e){return(this.props.baseClass||this.className)+e+" "}getClassName(){return(this.props.baseClass||this.className)+" "+(this.props.className?this.props.className+" ":"")}getProps(){return Object.assign({},this.props.extraProps||{},{className:this.getClassName()})}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(38),o=n(8),i=n(40);class a{static UID(){return a.TESTING?(a.TESTING_UID++,""+a.TESTING_UID):"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}static closest(e,t){return document.body.closest?e.closest(t):r(e,t)}static generateLinePath(e,t){return"M".concat(e.x,",").concat(e.y," L ").concat(t.x,",").concat(t.y)}static generateCurvePath(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;var r=Math.abs(e.x-t.x)>Math.abs(e.y-t.y),o=r?"x":"y";let i=n;e[o]>e[o]&&(i=-n);var a=r?i:0,l=r?0:i;return"M".concat(e.x,",").concat(e.y," C ").concat(e.x+a,",").concat(e.y+l,"\n ").concat(t.x-a,",").concat(t.y-l," ").concat(t.x,",").concat(t.y)}static generateDynamicPath(e){let t=i();return t=t.moveto(e[0][0]*o.ROUTING_SCALING_FACTOR,e[0][1]*o.ROUTING_SCALING_FACTOR),e.slice(1).forEach(e=>{t=t.lineto(e[0]*o.ROUTING_SCALING_FACTOR,e[1]*o.ROUTING_SCALING_FACTOR)}),t.print()}}a.TESTING=!1,a.TESTING_UID=0,t.Toolkit=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(9),o=n(0);t.BaseModel=class extends r.BaseEntity{constructor(e,t){super(t),this.type=e,this.selected=!1}getParent(){return this.parent}setParent(e){this.parent=e}getSelectedEntities(){return this.isSelected()?[this]:[]}deSerialize(e,t){super.deSerialize(e,t),this.type=e.type,this.selected=e.selected}serialize(){return o.merge(super.serialize(),{type:this.type,selected:this.selected})}getType(){return this.type}getID(){return this.id}isSelected(){return this.selected}setSelected(){let e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.selected=e,this.iterateListeners((t,n)=>{t.selectionChanged&&t.selectionChanged(Object.assign({},n,{isSelected:e}))})}remove(){this.iterateListeners((e,t)=>{e.entityRemoved&&e.entityRemoved(t)})}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4),o=n(0);t.PointModel=class extends r.BaseModel{constructor(e,t){super(),this.x=t.x,this.y=t.y,this.parent=e}getSelectedEntities(){return super.isSelected()&&!this.isConnectedToPort()?[this]:[]}isConnectedToPort(){return null!==this.parent.getPortForPoint(this)}getLink(){return this.getParent()}deSerialize(e,t){super.deSerialize(e,t),this.x=e.x,this.y=e.y}serialize(){return o.merge(super.serialize(),{x:this.x,y:this.y})}remove(){this.parent&&this.parent.removePoint(this),super.remove()}updateLocation(e){this.x=e.x,this.y=e.y}getX(){return this.x}getY(){return this.y}isLocked(){return super.isLocked()||this.getParent().isLocked()}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4),o=n(0);t.NodeModel=class extends r.BaseModel{constructor(){super(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default",arguments.length>1?arguments[1]:void 0),this.x=0,this.y=0,this.extras={},this.ports={}}setPosition(e,t){let n=this.x,r=this.y;o.forEach(this.ports,i=>{o.forEach(i.getLinks(),o=>{let a=o.getPointForPort(i);a.x=a.x+e-n,a.y=a.y+t-r})}),this.x=e,this.y=t}positionChanged(){this.iterateListeners((e,t)=>e.positionChanged&&e.positionChanged(t))}getSelectedEntities(){let e=super.getSelectedEntities();return this.isSelected()&&o.forEach(this.ports,t=>{e=e.concat(o.map(t.getLinks(),e=>e.getPointForPort(t)))}),e}deSerialize(e,t){super.deSerialize(e,t),this.x=e.x,this.y=e.y,this.extras=e.extras,o.forEach(e.ports,e=>{let n=t.getPortFactory(e.type).getNewInstance();n.deSerialize(e,t),this.addPort(n)})}serialize(){return o.merge(super.serialize(),{x:this.x,y:this.y,extras:this.extras,ports:o.map(this.ports,e=>e.serialize())})}doClone(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;t.ports={},o.forEach(this.ports,n=>{t.addPort(n.clone(e))})}remove(){super.remove(),o.forEach(this.ports,e=>{o.forEach(e.getLinks(),e=>{e.remove()})})}getPortFromID(e){for(var t in this.ports)if(this.ports[t].id===e)return this.ports[t];return null}getPort(e){return this.ports[e]}getPorts(){return this.ports}removePort(e){this.ports[e.name]&&(this.ports[e.name].setParent(null),delete this.ports[e.name])}addPort(e){return e.setParent(this),this.ports[e.name]=e,e}updateDimensions(e){let{width:t,height:n}=e;this.width=t,this.height=n}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AbstractFactory=class{constructor(e){this.type=e}getType(){return this.type}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(39);t.ROUTING_SCALING_FACTOR=5;const o=new r.JumpPointFinder({heuristic:r.Heuristic.manhattan,diagonalMovement:r.DiagonalMovement.Never});t.default=class{constructor(e){this.instance=o,this.diagramEngine=e}calculateDirectPath(e,n){const i=this.diagramEngine.getCanvasMatrix(),a=new r.Grid(i);return o.findPath(this.diagramEngine.translateRoutingX(Math.floor(e.x/t.ROUTING_SCALING_FACTOR)),this.diagramEngine.translateRoutingY(Math.floor(e.y/t.ROUTING_SCALING_FACTOR)),this.diagramEngine.translateRoutingX(Math.floor(n.x/t.ROUTING_SCALING_FACTOR)),this.diagramEngine.translateRoutingY(Math.floor(n.y/t.ROUTING_SCALING_FACTOR)),a)}calculateLinkStartEndCoords(e,t){const n=t.findIndex(t=>0===e[t[1]][t[0]]),r=t.length-1-t.slice().reverse().findIndex(t=>0===e[t[1]][t[0]]);if(-1===n||-1===r)return;const o=t.slice(0,n),i=t.slice(r);return{start:{x:t[n][0],y:t[n][1]},end:{x:t[r][0],y:t[r][1]},pathToStart:o,pathToEnd:i}}calculateDynamicPath(e,t,n,i,a){const l=new r.Grid(e),s=o.findPath(t.x,t.y,n.x,n.y,l),u=i.concat(s,a).map(e=>[this.diagramEngine.translateRoutingX(e[0],!0),this.diagramEngine.translateRoutingY(e[1],!0)]);return r.Util.compressPath(u)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(3),o=n(0);t.BaseEntity=class{constructor(e){this.listeners={},this.id=e||r.Toolkit.UID(),this.locked=!1}getID(){return this.id}doClone(){}clone(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(e[this.id])return e[this.id];let t=o.clone(this);return t.id=r.Toolkit.UID(),t.clearListeners(),e[this.id]=t,this.doClone(e,t),t}clearListeners(){this.listeners={}}deSerialize(e,t){this.id=e.id}serialize(){return{id:this.id}}iterateListeners(e){let t={id:r.Toolkit.UID(),firing:!0,entity:this,stopPropagation:()=>{t.firing=!1}};for(var n in this.listeners)if(this.listeners.hasOwnProperty(n)){if(!t.firing)return;e(this.listeners[n],t)}}removeListener(e){return!!this.listeners[e]&&(delete this.listeners[e],!0)}addListener(e){var t=r.Toolkit.UID();return this.listeners[t]=e,t}isLocked(){return this.locked}setLocked(){let e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.locked=e,this.iterateListeners((t,n)=>{t.lockChanged&&t.lockChanged(Object.assign({},n,{locked:e}))})}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4),o=n(0);t.PortModel=class extends r.BaseModel{constructor(e,t,n,r){super(t,n),this.name=e,this.links={},this.maximumLinks=r}deSerialize(e,t){super.deSerialize(e,t),this.name=e.name,this.maximumLinks=e.maximumLinks}serialize(){return o.merge(super.serialize(),{name:this.name,parentNode:this.parent.id,links:o.map(this.links,e=>e.id),maximumLinks:this.maximumLinks})}doClone(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;t.links={},t.parentNode=this.getParent().clone(e)}getNode(){return this.getParent()}getName(){return this.name}getMaximumLinks(){return this.maximumLinks}setMaximumLinks(e){this.maximumLinks=e}removeLink(e){delete this.links[e.getID()]}addLink(e){this.links[e.getID()]=e}getLinks(){return this.links}createLinkModel(){if(o.isFinite(this.maximumLinks)){var e=o.size(this.links);if(1===this.maximumLinks&&e>=1)return o.values(this.links)[0];if(e>=this.maximumLinks)return null}return null}updateCoords(e){let{x:t,y:n,width:r,height:o}=e;this.x=t,this.y=n,this.width=r,this.height=o}canLinkToPort(e){return!0}isLocked(){return super.isLocked()||this.getParent().isLocked()}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BaseAction=class{constructor(e,t){this.mouseX=e,this.mouseY=t,this.ms=(new Date).getTime()}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4),o=n(5),i=n(0);t.LinkModel=class extends r.BaseModel{constructor(){super(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default",arguments.length>1?arguments[1]:void 0),this.points=[new o.PointModel(this,{x:0,y:0}),new o.PointModel(this,{x:0,y:0})],this.extras={},this.sourcePort=null,this.targetPort=null,this.labels=[]}deSerialize(e,t){super.deSerialize(e,t),this.extras=e.extras,this.points=i.map(e.points||[],e=>{var n=new o.PointModel(this,{x:e.x,y:e.y});return n.deSerialize(e,t),n}),i.forEach(e.labels||[],e=>{let n=t.getLabelFactory(e.type).getNewInstance();n.deSerialize(e,t),this.addLabel(n)}),e.target&&this.setTargetPort(this.getParent().getNode(e.target).getPortFromID(e.targetPort)),e.source&&this.setSourcePort(this.getParent().getNode(e.source).getPortFromID(e.sourcePort))}serialize(){return i.merge(super.serialize(),{source:this.sourcePort?this.sourcePort.getParent().id:null,sourcePort:this.sourcePort?this.sourcePort.id:null,target:this.targetPort?this.targetPort.getParent().id:null,targetPort:this.targetPort?this.targetPort.id:null,points:i.map(this.points,e=>e.serialize()),extras:this.extras,labels:i.map(this.labels,e=>e.serialize())})}doClone(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;t.setPoints(i.map(this.getPoints(),t=>t.clone(e))),this.sourcePort&&t.setSourcePort(this.sourcePort.clone(e)),this.targetPort&&t.setTargetPort(this.targetPort.clone(e))}remove(){this.sourcePort&&this.sourcePort.removeLink(this),this.targetPort&&this.targetPort.removeLink(this),super.remove()}isLastPoint(e){return this.getPointIndex(e)===this.points.length-1}getPointIndex(e){return this.points.indexOf(e)}getPointModel(e){for(var t=0;t{t.sourcePortChanged&&t.sourcePortChanged(Object.assign({},n,{port:e}))})}getSourcePort(){return this.sourcePort}getTargetPort(){return this.targetPort}setTargetPort(e){null!==e&&e.addLink(this),null!==this.targetPort&&this.targetPort.removeLink(this),this.targetPort=e,this.iterateListeners((t,n)=>{t.targetPortChanged&&t.targetPortChanged(Object.assign({},n,{port:e}))})}point(e,t){return this.addPoint(this.generatePoint(e,t))}addLabel(e){e.setParent(this),this.labels.push(e)}getPoints(){return this.points}setPoints(e){i.forEach(e,e=>{e.setParent(this)}),this.points=e}removePoint(e){this.points.splice(this.getPointIndex(e),1)}removePointsBefore(e){this.points.splice(0,this.getPointIndex(e))}removePointsAfter(e){this.points.splice(this.getPointIndex(e)+1)}removeMiddlePoints(){this.points.length>2&&this.points.splice(0,this.points.length-2)}addPoint(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1;return e.setParent(this),this.points.splice(t,0,e),e}generatePoint(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return new o.PointModel(this,{x:e,y:t})}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(0),o=n(10),i=n(14);class a extends o.PortModel{constructor(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;super(t,"default",arguments.length>3?arguments[3]:void 0),this.in=e,this.label=n||t}deSerialize(e,t){super.deSerialize(e,t),this.in=e.in,this.label=e.label}serialize(){return r.merge(super.serialize(),{in:this.in,label:this.label})}link(e){let t=this.createLinkModel();return t.setSourcePort(this),t.setTargetPort(e),t}canLinkToPort(e){return!(e instanceof a)||this.in!==e.in}createLinkModel(){return super.createLinkModel()||new i.DefaultLinkModel}}t.DefaultPortModel=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(12),o=n(0),i=n(15),a=n(16);t.DefaultLinkModel=class extends r.LinkModel{constructor(){super(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default"),this.color="rgba(255,255,255,0.5)",this.width=3,this.curvyness=50}serialize(){return o.merge(super.serialize(),{width:this.width,color:this.color,curvyness:this.curvyness})}deSerialize(e,t){super.deSerialize(e,t),this.color=e.color,this.width=e.width,this.curvyness=e.curvyness}addLabel(e){if(e instanceof a.LabelModel)return super.addLabel(e);let t=new i.DefaultLabelModel;return t.setLabel(e),super.addLabel(t)}setWidth(e){this.width=e,this.iterateListeners((t,n)=>{t.widthChanged&&t.widthChanged(Object.assign({},n,{width:e}))})}setColor(e){this.color=e,this.iterateListeners((t,n)=>{t.colorChanged&&t.colorChanged(Object.assign({},n,{color:e}))})}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(16),o=n(0);t.DefaultLabelModel=class extends r.LabelModel{constructor(){super("default"),this.offsetY=-23}setLabel(e){this.label=e}deSerialize(e,t){super.deSerialize(e,t),this.label=e.label}serialize(){return o.merge(super.serialize(),{label:this.label})}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4),o=n(0);t.LabelModel=class extends r.BaseModel{constructor(e,t){super(e,t),this.offsetX=0,this.offsetY=0}deSerialize(e,t){super.deSerialize(e,t),this.offsetX=e.offsetX,this.offsetY=e.offsetY}serialize(){return o.merge(super.serialize(),{offsetX:this.offsetX,offsetY:this.offsetY})}}},function(e,t,n){"use strict";function r(e){for(var n in e)t.hasOwnProperty(n)||(t[n]=e[n])}Object.defineProperty(t,"__esModule",{value:!0}),r(n(3)),r(n(9)),r(n(41)),r(n(24)),r(n(13)),r(n(14)),r(n(15)),r(n(42)),r(n(43)),r(n(19)),r(n(21)),r(n(25)),r(n(23)),r(n(27)),r(n(28)),r(n(7)),r(n(22)),r(n(26)),r(n(30)),r(n(20)),r(n(8)),r(n(11)),r(n(31)),r(n(32)),r(n(33)),r(n(4)),r(n(18)),r(n(12)),r(n(6)),r(n(5)),r(n(10)),r(n(16)),r(n(44)),r(n(35)),r(n(37)),r(n(29)),r(n(2)),r(n(34)),r(n(36))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(9),o=n(0),i=n(12),a=n(6),l=n(10),s=n(5);t.DiagramModel=class extends r.BaseEntity{constructor(){super(),this.links={},this.nodes={},this.offsetX=0,this.offsetY=0,this.zoom=100,this.rendered=!1,this.gridSize=0}setGridSize(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;this.gridSize=e,this.iterateListeners((t,n)=>{t.gridUpdated&&t.gridUpdated(Object.assign({},n,{size:e}))})}getGridPosition(e){return 0===this.gridSize?e:this.gridSize*Math.floor((e+this.gridSize/2)/this.gridSize)}deSerializeDiagram(e,t){this.deSerialize(e,t),this.offsetX=e.offsetX,this.offsetY=e.offsetY,this.zoom=e.zoom,this.gridSize=e.gridSize,o.forEach(e.nodes,e=>{let n=t.getNodeFactory(e.type).getNewInstance(e);n.setParent(this),n.deSerialize(e,t),this.addNode(n)}),o.forEach(e.links,e=>{let n=t.getLinkFactory(e.type).getNewInstance();n.setParent(this),n.deSerialize(e,t),this.addLink(n)})}serializeDiagram(){return o.merge(this.serialize(),{offsetX:this.offsetX,offsetY:this.offsetY,zoom:this.zoom,gridSize:this.gridSize,links:o.map(this.links,e=>e.serialize()),nodes:o.map(this.nodes,e=>e.serialize())})}clearSelection(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;o.forEach(this.getSelectedItems(),t=>{e&&e.getID()===t.getID()||t.setSelected(!1)})}getSelectedItems(){for(var e=arguments.length,t=new Array(e),n=0;ne.getSelectedEntities()))).concat(o.flatMap(this.links,e=>e.getSelectedEntities()))).concat(o.flatMap(this.links,e=>o.flatMap(e.points,e=>e.getSelectedEntities()))),r=o.uniq(r),t.length>0&&(r=o.filter(o.uniq(r),e=>!!(o.includes(t,"node")&&e instanceof a.NodeModel||o.includes(t,"link")&&e instanceof i.LinkModel||o.includes(t,"port")&&e instanceof l.PortModel||o.includes(t,"point")&&e instanceof s.PointModel))),r}setZoomLevel(e){this.zoom=e,this.iterateListeners((t,n)=>{t.zoomUpdated&&t.zoomUpdated(Object.assign({},n,{zoom:e}))})}setOffset(e,t){this.offsetX=e,this.offsetY=t,this.iterateListeners((n,r)=>{n.offsetUpdated&&n.offsetUpdated(Object.assign({},r,{offsetX:e,offsetY:t}))})}setOffsetX(e){this.offsetX=e,this.iterateListeners((t,n)=>{t.offsetUpdated&&t.offsetUpdated(Object.assign({},n,{offsetX:e,offsetY:this.offsetY}))})}setOffsetY(e){this.offsetY=e,this.iterateListeners((e,t)=>{e.offsetUpdated&&e.offsetUpdated(Object.assign({},t,{offsetX:this.offsetX,offsetY:this.offsetY}))})}getOffsetY(){return this.offsetY}getOffsetX(){return this.offsetX}getZoomLevel(){return this.zoom}getNode(e){return e instanceof a.NodeModel?e:this.nodes[e]?this.nodes[e]:null}getLink(e){return e instanceof i.LinkModel?e:this.links[e]?this.links[e]:null}addAll(){for(var e=arguments.length,t=new Array(e),n=0;n{e instanceof i.LinkModel?this.addLink(e):e instanceof a.NodeModel&&this.addNode(e)}),t}addLink(e){return e.addListener({entityRemoved:()=>{this.removeLink(e)}}),this.links[e.getID()]=e,this.iterateListeners((t,n)=>{t.linksUpdated&&t.linksUpdated(Object.assign({},n,{link:e,isCreated:!0}))}),e}addNode(e){return e.addListener({entityRemoved:()=>{this.removeNode(e)}}),this.nodes[e.getID()]=e,this.iterateListeners((t,n)=>{t.nodesUpdated&&t.nodesUpdated(Object.assign({},n,{node:e,isCreated:!0}))}),e}removeLink(e){e=this.getLink(e),delete this.links[e.getID()],this.iterateListeners((t,n)=>{t.linksUpdated&&t.linksUpdated(Object.assign({},n,{link:e,isCreated:!1}))})}removeNode(e){e=this.getNode(e),delete this.nodes[e.getID()],this.iterateListeners((t,n)=>{t.nodesUpdated&&t.nodesUpdated(Object.assign({},n,{node:e,isCreated:!1}))})}getLinks(){return this.links}getNodes(){return this.nodes}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(13),o=n(20);t.DefaultPortFactory=class extends o.AbstractPortFactory{constructor(){super("default")}getNewInstance(e){return new r.DefaultPortModel(!0,"unknown")}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7);t.AbstractPortFactory=class extends r.AbstractFactory{}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1),o=n(22),i=n(15),a=n(23);t.DefaultLabelFactory=class extends o.AbstractLabelFactory{constructor(){super("default")}generateReactWidget(e,t){return r.createElement(a.DefaultLabelWidget,{model:t})}getNewInstance(e){return new i.DefaultLabelModel}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7);t.AbstractLabelFactory=class extends r.AbstractFactory{}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1),o=n(2);t.DefaultLabelWidget=class extends o.BaseWidget{constructor(e){super("srd-default-label",e)}render(){return r.createElement("div",Object.assign({},this.getProps()),this.props.model.label)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(13),o=n(0),i=n(6),a=n(3);t.DefaultNodeModel=class extends i.NodeModel{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Untitled",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"rgb(0,192,255)";super("default"),this.name=e,this.color=t}addInPort(e){return this.addPort(new r.DefaultPortModel(!0,a.Toolkit.UID(),e))}addOutPort(e){return this.addPort(new r.DefaultPortModel(!1,a.Toolkit.UID(),e))}deSerialize(e,t){super.deSerialize(e,t),this.name=e.name,this.color=e.color}serialize(){return o.merge(super.serialize(),{name:this.name,color:this.color})}getInPorts(){return o.filter(this.ports,e=>e.in)}getOutPorts(){return o.filter(this.ports,e=>!e.in)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1),o=n(5),i=n(3),a=n(8),l=n(0),s=n(2);class u extends s.BaseWidget{constructor(e){super("srd-default-link",e),this.addPointToLink=(e,t)=>{if(!e.shiftKey&&!this.props.diagramEngine.isModelLocked(this.props.link)&&this.props.link.points.length-1<=this.props.diagramEngine.getMaxNumberPointsPerLink()){const n=new o.PointModel(this.props.link,this.props.diagramEngine.getRelativeMousePoint(e));n.setSelected(!0),this.forceUpdate(),this.props.link.addPoint(n,t),this.props.pointAdded(n,e)}},this.findPathAndRelativePositionToRenderLabel=e=>{const t=this.refPaths.map(e=>e.getTotalLength());let n=t.reduce((e,t)=>e+t,0)*(e/(this.props.link.labels.length+1)),r=0;for(;r{if(!this.refLabels[e.id])return;const{path:n,position:r}=this.findPathAndRelativePositionToRenderLabel(t),o=this.refLabels[e.id].offsetWidth,i=this.refLabels[e.id].offsetHeight,a=n.getPointAtLength(r),l=a.x-o/2+e.offsetX,s=a.y-i/2+e.offsetY;this.refLabels[e.id].setAttribute("style","transform: translate(".concat(l,"px, ").concat(s,"px);"))},this.refLabels={},this.refPaths=[],this.state={selected:!1},e.diagramEngine.isSmartRoutingEnabled()&&(this.pathFinding=new a.default(this.props.diagramEngine))}calculateAllLabelPosition(){l.forEach(this.props.link.labels,(e,t)=>{this.calculateLabelPosition(e,t+1)})}componentDidUpdate(){this.props.link.labels.length>0&&window.requestAnimationFrame(this.calculateAllLabelPosition.bind(this))}componentDidMount(){this.props.link.labels.length>0&&window.requestAnimationFrame(this.calculateAllLabelPosition.bind(this))}generatePoint(e){let t=this.props.link.points[e].x,n=this.props.link.points[e].y;return r.createElement("g",{key:"point-"+this.props.link.points[e].id},r.createElement("circle",{cx:t,cy:n,r:5,className:"point "+this.bem("__point")+(this.props.link.points[e].isSelected()?this.bem("--point-selected"):"")}),r.createElement("circle",{onMouseLeave:()=>{this.setState({selected:!1})},onMouseEnter:()=>{this.setState({selected:!0})},"data-id":this.props.link.points[e].id,"data-linkid":this.props.link.id,cx:t,cy:n,r:15,opacity:0,className:"point "+this.bem("__point")}))}generateLabel(e){const t=this.props.diagramEngine.canvas;return r.createElement("foreignObject",{key:e.id,className:this.bem("__label"),width:t.offsetWidth,height:t.offsetHeight},r.createElement("div",{ref:t=>this.refLabels[e.id]=t},this.props.diagramEngine.getFactoryForLabel(e).generateReactWidget(this.props.diagramEngine,e)))}generateLink(e,t,n){var o=this.props,i=r.cloneElement(o.diagramEngine.getFactoryForLink(this.props.link).generateLinkSegment(this.props.link,this,this.state.selected||this.props.link.isSelected(),e),{ref:e=>e&&this.refPaths.push(e)}),a=r.cloneElement(i,Object.assign({},t,{strokeLinecap:"round",onMouseLeave:()=>{this.setState({selected:!1})},onMouseEnter:()=>{this.setState({selected:!0})},ref:null,"data-linkid":this.props.link.getID(),strokeOpacity:this.state.selected?.1:0,strokeWidth:20,onContextMenu:()=>{this.props.diagramEngine.isModelLocked(this.props.link)||(event.preventDefault(),this.props.link.remove())}}));return r.createElement("g",{key:"link-"+n},i,a)}isSmartRoutingApplicable(){const{diagramEngine:e,link:t}=this.props;return!!e.isSmartRoutingEnabled()&&2===t.points.length&&null!==t.sourcePort&&null!==t.targetPort}render(){const{diagramEngine:e}=this.props;if(!e.nodesRendered)return null;var t=this.props.link.points,n=[];if(this.isSmartRoutingApplicable()){const r=this.pathFinding.calculateDirectPath(l.first(t),l.last(t)),o=e.getRoutingMatrix(),a=this.pathFinding.calculateLinkStartEndCoords(o,r);if(a){const{start:e,end:t,pathToStart:r,pathToEnd:l}=a,s=this.pathFinding.calculateDynamicPath(o,e,t,r,l);n.push(this.generateLink(i.Toolkit.generateDynamicPath(s),{onMouseDown:e=>{this.addPointToLink(e,1)}},"0"))}}if(0===n.length)if(2===t.length){var o=Math.abs(t[0].x-t[1].x)>Math.abs(t[0].y-t[1].y)?"x":"y";Math.abs(t[0][o]-t[1][o]);var a=t[0],s=t[1];n.push(this.generateLink(i.Toolkit.generateCurvePath(a,s,this.props.link.curvyness),{onMouseDown:e=>{this.addPointToLink(e,1)}},"0")),null===this.props.link.targetPort&&n.push(this.generatePoint(1))}else{for(let e=0;e{this.addPointToLink(t,e+1)}},e));for(var u=1;uthis.generateLabel(e)))}}u.defaultProps={color:"black",width:3,link:null,engine:null,smooth:!1,diagramEngine:null},t.DefaultLinkWidget=u},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7);t.AbstractLinkFactory=class extends r.AbstractFactory{}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1),o=n(0),i=n(28),a=n(2);t.DefaultNodeWidget=class extends a.BaseWidget{constructor(e){super("srd-default-node",e),this.state={}}generatePort(e){return r.createElement(i.DefaultPortLabel,{model:e,key:e.id})}render(){return r.createElement("div",Object.assign({},this.getProps(),{style:{background:this.props.node.color}}),r.createElement("div",{className:this.bem("__title")},r.createElement("div",{className:this.bem("__name")},this.props.node.name)),r.createElement("div",{className:this.bem("__ports")},r.createElement("div",{className:this.bem("__in")},o.map(this.props.node.getInPorts(),this.generatePort.bind(this))),r.createElement("div",{className:this.bem("__out")},o.map(this.props.node.getOutPorts(),this.generatePort.bind(this)))))}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1),o=n(29),i=n(2);t.DefaultPortLabel=class extends i.BaseWidget{constructor(e){super("srd-default-port",e)}getClassName(){return super.getClassName()+(this.props.model.in?this.bem("--in"):this.bem("--out"))}render(){var e=r.createElement(o.PortWidget,{node:this.props.model.getParent(),name:this.props.model.name}),t=r.createElement("div",{className:"name"},this.props.model.label);return r.createElement("div",Object.assign({},this.getProps()),this.props.model.in?e:t,this.props.model.in?t:e)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1),o=n(2);t.PortWidget=class extends o.BaseWidget{constructor(e){super("srd-port",e),this.state={selected:!1}}getClassName(){return"port "+super.getClassName()+(this.state.selected?this.bem("--selected"):"")}render(){return r.createElement("div",Object.assign({},this.getProps(),{onMouseEnter:()=>{this.setState({selected:!0})},onMouseLeave:()=>{this.setState({selected:!1})},"data-name":this.props.name,"data-nodeid":this.props.node.getID()}))}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7);t.AbstractNodeFactory=class extends r.AbstractFactory{}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(11);t.MoveCanvasAction=class extends r.BaseAction{constructor(e,t,n){super(e,t),this.initialOffsetX=n.getOffsetX(),this.initialOffsetY=n.getOffsetY()}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(11);t.MoveItemsAction=class extends r.BaseAction{constructor(e,t,n){super(e,t),this.moved=!1,n.enableRepaintEntities(n.getDiagramModel().getSelectedItems());var r=n.getDiagramModel().getSelectedItems();r=r.filter(e=>!n.isModelLocked(e)),this.selectionModels=r.map(e=>({model:e,initialX:e.x,initialY:e.y}))}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(11);t.SelectingAction=class extends r.BaseAction{constructor(e,t){super(e,t),this.mouseX2=e,this.mouseY2=t}getBoxDimensions(){return{left:this.mouseX2>this.mouseX?this.mouseX:this.mouseX2,top:this.mouseY2>this.mouseY?this.mouseY:this.mouseY2,width:Math.abs(this.mouseX2-this.mouseX),height:Math.abs(this.mouseY2-this.mouseY),right:this.mouseX2o.left&&e*r+n.getOffsetX()o.top&&t*r+n.getOffsetY(){if(this.props.diagramEngine.nodesRendered&&!this.props.diagramEngine.linksThatHaveInitiallyRendered[e.id]){if(null!==e.sourcePort)try{const t=this.props.diagramEngine.getPortCenter(e.sourcePort);e.points[0].updateLocation(t);const n=this.props.diagramEngine.getPortCoords(e.sourcePort);e.sourcePort.updateCoords(n),this.props.diagramEngine.linksThatHaveInitiallyRendered[e.id]=!0}catch(e){}if(null!==e.targetPort)try{const t=this.props.diagramEngine.getPortCenter(e.targetPort);i.last(e.points).updateLocation(t);const n=this.props.diagramEngine.getPortCoords(e.targetPort);e.targetPort.updateCoords(n),this.props.diagramEngine.linksThatHaveInitiallyRendered[e.id]=!0}catch(e){}}var t=this.props.diagramEngine.generateWidgetForLink(e);if(!t)throw new Error("no link generated for type: ".concat(e.getType()));return r.createElement(o.LinkWidget,{key:e.getID(),link:e,diagramEngine:this.props.diagramEngine},r.cloneElement(t,{pointAdded:this.props.pointAdded}))}))}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(2);t.LinkWidget=class extends r.BaseWidget{constructor(e){super("srd-link",e),this.state={}}shouldComponentUpdate(){return this.props.diagramEngine.canEntityRepaint(this.props.link)}render(){return this.props.children}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1),o=n(0),i=n(37),a=n(2);t.NodeLayerWidget=class extends a.BaseWidget{constructor(e){super("srd-node-layer",e),this.updateNodeDimensions=()=>{if(!this.props.diagramEngine.nodesRendered){const e=this.props.diagramEngine.getDiagramModel();o.map(e.getNodes(),e=>{e.updateDimensions(this.props.diagramEngine.getNodeDimensions(e))})}},this.state={}}componentDidUpdate(){this.updateNodeDimensions(),this.props.diagramEngine.nodesRendered=!0}render(){var e=this.props.diagramEngine.getDiagramModel();return r.createElement("div",Object.assign({},this.getProps(),{style:{transform:"translate("+e.getOffsetX()+"px,"+e.getOffsetY()+"px) scale("+e.getZoomLevel()/100+")"}}),o.map(e.getNodes(),e=>r.createElement(i.NodeWidget,{diagramEngine:this.props.diagramEngine,key:e.id,node:e},this.props.diagramEngine.generateWidgetForNode(e))))}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1),o=n(2);t.NodeWidget=class extends o.BaseWidget{constructor(e){super("srd-node",e),this.state={}}shouldComponentUpdate(){return this.props.diagramEngine.canEntityRepaint(this.props.node)}getClassName(){return"node "+super.getClassName()+(this.props.node.isSelected()?this.bem("--selected"):"")}render(){return r.createElement("div",Object.assign({},this.getProps(),{"data-nodeid":this.props.node.id,style:{top:this.props.node.y,left:this.props.node.x}}),this.props.children)}}},function(e,t){e.exports=n(220)},function(e,t){e.exports=n(222)},function(e,t){e.exports=n(238)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(9),o=n(18),i=n(0),a=n(6),l=n(5),s=n(17),u=n(8),c=n(19),d=n(21),f=n(3);t.DiagramEngine=class extends r.BaseEntity{constructor(){super(),this.canvasMatrix=[],this.routingMatrix=[],this.hAdjustmentFactor=0,this.vAdjustmentFactor=0,this.calculateMatrixDimensions=()=>{const e=i.values(this.diagramModel.nodes).map(e=>({x:e.x,width:e.width,y:e.y,height:e.height})),t=i.values(this.diagramModel.links),n=i.flatMap(t.map(e=>[e.sourcePort,e.targetPort])).filter(e=>null!==e).map(e=>({x:e.x,width:e.width,y:e.y,height:e.height})),r=i.flatMap(t.map(e=>e.points)).map(e=>({x:e.x,width:0,y:e.y,height:0})),o=this.canvas,a=Math.floor(Math.min(i.minBy(i.concat(e,n,r),e=>e.x).x,0)/u.ROUTING_SCALING_FACTOR)*u.ROUTING_SCALING_FACTOR,l=i.maxBy(i.concat(e,n,r),e=>e.x+e.width),s=Math.max(l.x+l.width,o.offsetWidth),c=Math.floor(Math.min(i.minBy(i.concat(e,n,r),e=>e.y).y,0)/u.ROUTING_SCALING_FACTOR)*u.ROUTING_SCALING_FACTOR,d=i.maxBy(i.concat(e,n,r),e=>e.y+e.height),f=Math.max(d.y+d.height,o.offsetHeight);return{width:Math.ceil(Math.abs(a)+s),hAdjustmentFactor:Math.abs(a)/u.ROUTING_SCALING_FACTOR+1,height:Math.ceil(Math.abs(c)+f),vAdjustmentFactor:Math.abs(c)/u.ROUTING_SCALING_FACTOR+1}},this.markNodes=e=>{i.values(this.diagramModel.nodes).forEach(t=>{const n=Math.floor(t.x/u.ROUTING_SCALING_FACTOR),r=Math.ceil((t.x+t.width)/u.ROUTING_SCALING_FACTOR),o=Math.floor(t.y/u.ROUTING_SCALING_FACTOR),i=Math.ceil((t.y+t.height)/u.ROUTING_SCALING_FACTOR);for(let a=n-1;a<=r+1;a++)for(let t=o-1;t{i.flatMap(i.values(this.diagramModel.links).map(e=>[].concat(e.sourcePort,e.targetPort))).filter(e=>null!==e).forEach(t=>{const n=Math.floor(t.x/u.ROUTING_SCALING_FACTOR),r=Math.ceil((t.x+t.width)/u.ROUTING_SCALING_FACTOR),o=Math.floor(t.y/u.ROUTING_SCALING_FACTOR),i=Math.ceil((t.y+t.height)/u.ROUTING_SCALING_FACTOR);for(let a=n-1;a<=r+1;a++)for(let t=o-1;t{void 0!==e[n]&&void 0!==e[n][t]&&(e[n][t]=1)},this.diagramModel=new o.DiagramModel,this.nodeFactories={},this.linkFactories={},this.portFactories={},this.labelFactories={},this.canvas=null,this.paintableWidgets=null,this.linksThatHaveInitiallyRendered={},f.Toolkit.TESTING&&(f.Toolkit.TESTING_UID=0,window&&(window.diagram_instance=this))}installDefaultFactories(){this.registerNodeFactory(new s.DefaultNodeFactory),this.registerLinkFactory(new s.DefaultLinkFactory),this.registerPortFactory(new c.DefaultPortFactory),this.registerLabelFactory(new d.DefaultLabelFactory)}repaintCanvas(){this.iterateListeners(e=>{e.repaintCanvas&&e.repaintCanvas()})}clearRepaintEntities(){this.paintableWidgets=null}enableRepaintEntities(e){this.paintableWidgets={},e.forEach(e=>{e instanceof a.NodeModel&&i.forEach(e.getPorts(),e=>{i.forEach(e.getLinks(),e=>{this.paintableWidgets[e.getID()]=!0})}),e instanceof l.PointModel&&(this.paintableWidgets[e.getLink().getID()]=!0),this.paintableWidgets[e.getID()]=!0})}isModelLocked(e){return!!this.diagramModel.isLocked()||e.isLocked()}recalculatePortsVisually(){this.nodesRendered=!1,this.linksThatHaveInitiallyRendered={}}canEntityRepaint(e){return null===this.paintableWidgets||void 0!==this.paintableWidgets[e.getID()]}setCanvas(e){this.canvas=e}setDiagramModel(e){this.diagramModel=e,this.recalculatePortsVisually()}getDiagramModel(){return this.diagramModel}getNodeFactories(){return this.nodeFactories}getLinkFactories(){return this.linkFactories}getLabelFactories(){return this.labelFactories}registerLabelFactory(e){this.labelFactories[e.getType()]=e,this.iterateListeners(e=>{e.labelFactoriesUpdated&&e.labelFactoriesUpdated()})}registerPortFactory(e){this.portFactories[e.getType()]=e,this.iterateListeners(e=>{e.portFactoriesUpdated&&e.portFactoriesUpdated()})}registerNodeFactory(e){this.nodeFactories[e.getType()]=e,this.iterateListeners(e=>{e.nodeFactoriesUpdated&&e.nodeFactoriesUpdated()})}registerLinkFactory(e){this.linkFactories[e.getType()]=e,this.iterateListeners(e=>{e.linkFactoriesUpdated&&e.linkFactoriesUpdated()})}getPortFactory(e){if(this.portFactories[e])return this.portFactories[e];throw new Error("cannot find factory for port of type: [".concat(e,"]"))}getNodeFactory(e){if(this.nodeFactories[e])return this.nodeFactories[e];throw new Error("cannot find factory for node of type: [".concat(e,"]"))}getLinkFactory(e){if(this.linkFactories[e])return this.linkFactories[e];throw new Error("cannot find factory for link of type: [".concat(e,"]"))}getLabelFactory(e){if(this.labelFactories[e])return this.labelFactories[e];throw new Error("cannot find factory for label of type: [".concat(e,"]"))}getFactoryForNode(e){return this.getNodeFactory(e.getType())}getFactoryForLink(e){return this.getLinkFactory(e.getType())}getFactoryForLabel(e){return this.getLabelFactory(e.getType())}generateWidgetForLink(e){var t=this.getFactoryForLink(e);if(!t)throw new Error("Cannot find link factory for link: "+e.getType());return t.generateReactWidget(this,e)}generateWidgetForNode(e){var t=this.getFactoryForNode(e);if(!t)throw new Error("Cannot find widget factory for node: "+e.getType());return t.generateReactWidget(this,e)}getRelativeMousePoint(e){var t=this.getRelativePoint(e.clientX,e.clientY);return{x:(t.x-this.diagramModel.getOffsetX())/(this.diagramModel.getZoomLevel()/100),y:(t.y-this.diagramModel.getOffsetY())/(this.diagramModel.getZoomLevel()/100)}}getRelativePoint(e,t){var n=this.canvas.getBoundingClientRect();return{x:e-n.left,y:t-n.top}}getNodeElement(e){const t=this.canvas.querySelector('.node[data-nodeid="'.concat(e.getID(),'"]'));if(null===t)throw new Error("Cannot find Node element with nodeID: ["+e.getID()+"]");return t}getNodePortElement(e){var t=this.canvas.querySelector('.port[data-name="'.concat(e.getName(),'"][data-nodeid="').concat(e.getParent().getID(),'"]'));if(null===t)throw new Error("Cannot find Node Port element with nodeID: ["+e.getParent().getID()+"] and name: ["+e.getName()+"]");return t}getPortCenter(e){var t=this.getNodePortElement(e),n=t.getBoundingClientRect(),r=this.getRelativePoint(n.left,n.top);return{x:t.offsetWidth/2+(r.x-this.diagramModel.getOffsetX())/(this.diagramModel.getZoomLevel()/100),y:t.offsetHeight/2+(r.y-this.diagramModel.getOffsetY())/(this.diagramModel.getZoomLevel()/100)}}getPortCoords(e){const t=this.getNodePortElement(e).getBoundingClientRect(),n=this.canvas.getBoundingClientRect();return{x:(t.x-this.diagramModel.getOffsetX())/(this.diagramModel.getZoomLevel()/100)-n.left,y:(t.y-this.diagramModel.getOffsetY())/(this.diagramModel.getZoomLevel()/100)-n.top,width:t.width,height:t.height}}getNodeDimensions(e){if(!this.canvas)return{width:0,height:0};const t=this.getNodeElement(e).getBoundingClientRect();return{width:t.width,height:t.height}}getMaxNumberPointsPerLink(){return this.maxNumberPointsPerLink}setMaxNumberPointsPerLink(e){this.maxNumberPointsPerLink=e}isSmartRoutingEnabled(){return!!this.smartRouting}setSmartRoutingStatus(e){this.smartRouting=e}getCanvasMatrix(){return 0===this.canvasMatrix.length&&this.calculateCanvasMatrix(),this.canvasMatrix}calculateCanvasMatrix(){const{width:e,hAdjustmentFactor:t,height:n,vAdjustmentFactor:r}=this.calculateMatrixDimensions();this.hAdjustmentFactor=t,this.vAdjustmentFactor=r;const o=Math.ceil(e/u.ROUTING_SCALING_FACTOR),a=Math.ceil(n/u.ROUTING_SCALING_FACTOR);this.canvasMatrix=i.range(0,a).map(()=>new Array(o).fill(0))}getRoutingMatrix(){return 0===this.routingMatrix.length&&this.calculateRoutingMatrix(),this.routingMatrix}calculateRoutingMatrix(){const e=i.cloneDeep(this.getCanvasMatrix());this.markNodes(e),this.markPorts(e),this.routingMatrix=e}translateRoutingX(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return e+this.hAdjustmentFactor*(t?-1:1)}translateRoutingY(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return e+this.vAdjustmentFactor*(t?-1:1)}zoomToFit(){const e=this.canvas.clientWidth/this.canvas.scrollWidth,t=this.canvas.clientHeight/this.canvas.scrollHeight,n=ethis.forceUpdate()});this.setState({diagramEngineListener:t})}}componentWillUpdate(e){this.props.diagramEngine.diagramModel.id!==e.diagramEngine.diagramModel.id&&(this.setState({renderedNodes:!1}),e.diagramEngine.diagramModel.rendered=!0),e.diagramEngine.diagramModel.rendered||(this.setState({renderedNodes:!1}),e.diagramEngine.diagramModel.rendered=!0)}componentDidUpdate(){this.state.renderedNodes||this.setState({renderedNodes:!0})}componentDidMount(){this.onKeyUpPointer=this.onKeyUp.bind(this),this.setState({document:document,renderedNodes:!0,diagramEngineListener:this.props.diagramEngine.addListener({repaintCanvas:()=>{this.forceUpdate()}})}),window.addEventListener("keyup",this.onKeyUpPointer,!1),window.focus()}getMouseElement(e){var t=e.target,n=this.props.diagramEngine.diagramModel,r=l.Toolkit.closest(t,".port[data-name]");if(r){var o=l.Toolkit.closest(t,".node[data-nodeid]");return{model:n.getNode(o.getAttribute("data-nodeid")).getPort(r.getAttribute("data-name")),element:r}}return(r=l.Toolkit.closest(t,".point[data-id]"))?{model:n.getLink(r.getAttribute("data-linkid")).getPointModel(r.getAttribute("data-id")),element:r}:(r=l.Toolkit.closest(t,"[data-linkid]"))?{model:n.getLink(r.getAttribute("data-linkid")),element:r}:(r=l.Toolkit.closest(t,".node[data-nodeid]"))?{model:n.getNode(r.getAttribute("data-nodeid")),element:r}:null}fireAction(){this.state.action&&this.props.actionStillFiring&&this.props.actionStillFiring(this.state.action)}stopFiringAction(e){this.props.actionStoppedFiring&&!e&&this.props.actionStoppedFiring(this.state.action),this.setState({action:null})}startFiringAction(e){var t=!0;this.props.actionStartedFiring&&(t=this.props.actionStartedFiring(e)),t&&this.setState({action:e})}onMouseMove(e){var t=this.props.diagramEngine,n=t.getDiagramModel();if(this.state.action instanceof c.SelectingAction){var r=t.getRelativePoint(e.clientX,e.clientY);return o.forEach(n.getNodes(),e=>{this.state.action.containsElement(e.x,e.y,n)&&e.setSelected(!0)}),o.forEach(n.getLinks(),e=>{var t=!0;o.forEach(e.points,e=>{this.state.action.containsElement(e.x,e.y,n)?e.setSelected(!0):t=!1}),t&&e.setSelected(!0)}),this.state.action.mouseX2=r.x,this.state.action.mouseY2=r.y,this.fireAction(),void this.setState({action:this.state.action})}if(this.state.action instanceof u.MoveItemsAction){let r=e.clientX-this.state.action.mouseX,i=e.clientY-this.state.action.mouseY,a=n.getZoomLevel()/100;o.forEach(this.state.action.selectionModels,e=>{e.model instanceof d.NodeModel||e.model instanceof f.PointModel&&!e.model.isConnectedToPort()?(e.model.x=n.getGridPosition(e.initialX+r/a),e.model.y=n.getGridPosition(e.initialY+i/a),e.model instanceof d.NodeModel&&(e.model.positionChanged(),o.forEach(e.model.getPorts(),e=>{const t=this.props.diagramEngine.getPortCoords(e);e.updateCoords(t)})),t.isSmartRoutingEnabled()&&t.calculateRoutingMatrix()):e.model instanceof f.PointModel&&(e.model.x=e.initialX+n.getGridPosition(r/a),e.model.y=e.initialY+n.getGridPosition(i/a))}),t.isSmartRoutingEnabled()&&t.calculateCanvasMatrix(),this.fireAction(),this.state.wasMoved?this.forceUpdate():this.setState({wasMoved:!0})}else this.state.action instanceof s.MoveCanvasAction&&this.props.allowCanvasTranslation&&(n.setOffset(this.state.action.initialOffsetX+(e.clientX-this.state.action.mouseX),this.state.action.initialOffsetY+(e.clientY-this.state.action.mouseY)),this.fireAction(),this.forceUpdate())}onKeyUp(e){-1!==this.props.deleteKeys.indexOf(e.keyCode)&&(o.forEach(this.props.diagramEngine.getDiagramModel().getSelectedItems(),e=>{this.props.diagramEngine.isModelLocked(e)||e.remove()}),this.forceUpdate())}onMouseUp(e){var t=this.props.diagramEngine;if(this.state.action instanceof u.MoveItemsAction){var n=this.getMouseElement(e);o.forEach(this.state.action.selectionModels,e=>{if(e.model instanceof f.PointModel&&n&&n.model instanceof p.PortModel&&!t.isModelLocked(n.model)){let r=e.model.getLink();if(null!==r.getTargetPort())if(r.getTargetPort()!==n.model&&r.getSourcePort()!==n.model){const o=r.getTargetPort();let i=r.clone({});i.setSourcePort(n.model),i.setTargetPort(o),r.setTargetPort(n.model),o.removeLink(r),i.removePointsBefore(i.getPoints()[r.getPointIndex(e.model)]),r.removePointsAfter(e.model),t.getDiagramModel().addLink(i)}else r.getTargetPort()===n.model?r.removePointsAfter(e.model):r.getSourcePort()===n.model&&r.removePointsBefore(e.model);else r.setTargetPort(n.model);delete this.props.diagramEngine.linksThatHaveInitiallyRendered[r.getID()]}}),!this.props.allowLooseLinks&&this.state.wasMoved&&o.forEach(this.state.action.selectionModels,e=>{if(!(e.model instanceof f.PointModel))return;let t=e.model.getLink();null!==t.getSourcePort()&&null!==t.getTargetPort()||t.remove()}),o.forEach(this.state.action.selectionModels,e=>{if(!(e.model instanceof f.PointModel))return;let t=e.model.getLink(),n=t.getSourcePort(),r=t.getTargetPort();null!==n&&null!==r&&(n.canLinkToPort(r)?o.some(o.values(r.getLinks()),e=>e!==t&&(e.getSourcePort()===n||e.getTargetPort()===n))&&t.remove():t.remove())}),t.clearRepaintEntities(),this.stopFiringAction(!this.state.wasMoved)}else t.clearRepaintEntities(),this.stopFiringAction();this.state.document.removeEventListener("mousemove",this.onMouseMove),this.state.document.removeEventListener("mouseup",this.onMouseUp)}drawSelectionBox(){let e=this.state.action.getBoxDimensions();return r.createElement("div",{className:this.bem("__selector"),style:{top:e.top,left:e.left,width:e.width,height:e.height}})}render(){var e=this.props.diagramEngine;e.setMaxNumberPointsPerLink(this.props.maxNumberPointsPerLink),e.setSmartRoutingStatus(this.props.smartRouting);var t=e.getDiagramModel();return r.createElement("div",Object.assign({},this.getProps(),{ref:e=>{e&&this.props.diagramEngine.setCanvas(e)},onWheel:n=>{if(this.props.allowCanvasZoom){n.preventDefault(),n.stopPropagation();const r=t.getZoomLevel()/100;let o=this.props.inverseZoom?-n.deltaY:n.deltaY;n.ctrlKey&&o%1!=0?o/=3:o/=60,t.getZoomLevel()+o>10&&t.setZoomLevel(t.getZoomLevel()+o);const i=t.getZoomLevel()/100,a=n.currentTarget.getBoundingClientRect(),l=a.width,s=a.height,u=l*i-l*r,c=s*i-s*r,d=n.clientX-a.left,f=n.clientY-a.top,p=(d-t.getOffsetX())/r/l,h=(f-t.getOffsetY())/r/s;t.setOffset(t.getOffsetX()-u*p,t.getOffsetY()-c*h),e.enableRepaintEntities([]),this.forceUpdate()}},onMouseDown:n=>{if(3!==n.nativeEvent.which){this.setState(Object.assign({},this.state,{wasMoved:!1})),e.clearRepaintEntities();var r=this.getMouseElement(n);if(null===r)if(n.shiftKey){var o=e.getRelativePoint(n.clientX,n.clientY);this.startFiringAction(new c.SelectingAction(o.x,o.y))}else t.clearSelection(),this.startFiringAction(new s.MoveCanvasAction(n.clientX,n.clientY,t));else if(r.model instanceof p.PortModel)if(this.props.diagramEngine.isModelLocked(r.model))t.clearSelection();else{o=e.getRelativeMousePoint(n);var i=r.model,a=i.createLinkModel();a.setSourcePort(i),a&&(a.removeMiddlePoints(),a.getSourcePort()!==i&&a.setSourcePort(i),a.setTargetPort(null),a.getFirstPoint().updateLocation(o),a.getLastPoint().updateLocation(o),t.clearSelection(),a.getLastPoint().setSelected(!0),t.addLink(a),this.startFiringAction(new u.MoveItemsAction(n.clientX,n.clientY,e)))}else n.shiftKey||r.model.isSelected()||t.clearSelection(),r.model.setSelected(!0),this.startFiringAction(new u.MoveItemsAction(n.clientX,n.clientY,e));this.state.document.addEventListener("mousemove",this.onMouseMove),this.state.document.addEventListener("mouseup",this.onMouseUp)}}}),this.state.renderedNodes&&r.createElement(i.LinkLayerWidget,{diagramEngine:e,pointAdded:(n,r)=>{this.state.document.addEventListener("mousemove",this.onMouseMove),this.state.document.addEventListener("mouseup",this.onMouseUp),r.stopPropagation(),t.clearSelection(n),this.setState({action:new u.MoveItemsAction(r.clientX,r.clientY,e)})}}),r.createElement(a.NodeLayerWidget,{diagramEngine:e}),this.state.action instanceof c.SelectingAction&&this.drawSelectionBox())}}m.defaultProps={diagramEngine:null,allowLooseLinks:!0,allowCanvasTranslation:!0,allowCanvasZoom:!0,inverseZoom:!1,maxNumberPointsPerLink:1/0,smartRouting:!1,deleteKeys:[46,8]},t.DiagramWidget=m}])},function(e,t,n){"use strict";n.d(t,"d",(function(){return l})),n.d(t,"c",(function(){return u})),n.d(t,"a",(function(){return c})),n.d(t,"b",(function(){return d})),n.d(t,"e",(function(){return f}));var r=n(173);function o(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return Math.min(Math.max(t,e),n)}function i(e){if(e.type)return e;if("#"===e.charAt(0))return i(function(e){e=e.substr(1);var t=new RegExp(".{1,".concat(e.length>=6?2:1,"}"),"g"),n=e.match(t);return n&&1===n[0].length&&(n=n.map((function(e){return e+e}))),n?"rgb".concat(4===n.length?"a":"","(").concat(n.map((function(e,t){return t<3?parseInt(e,16):Math.round(parseInt(e,16)/255*1e3)/1e3})).join(", "),")"):""}(e));var t=e.indexOf("("),n=e.substring(0,t);if(-1===["rgb","rgba","hsl","hsla"].indexOf(n))throw new Error(Object(r.a)(3,e));var o=e.substring(t+1,e.length-1).split(",");return{type:n,values:o=o.map((function(e){return parseFloat(e)}))}}function a(e){var t=e.type,n=e.values;return-1!==t.indexOf("rgb")?n=n.map((function(e,t){return t<3?parseInt(e,10):e})):-1!==t.indexOf("hsl")&&(n[1]="".concat(n[1],"%"),n[2]="".concat(n[2],"%")),"".concat(t,"(").concat(n.join(", "),")")}function l(e,t){var n=s(e),r=s(t);return(Math.max(n,r)+.05)/(Math.min(n,r)+.05)}function s(e){var t="hsl"===(e=i(e)).type?i(function(e){var t=(e=i(e)).values,n=t[0],r=t[1]/100,o=t[2]/100,l=r*Math.min(o,1-o),s=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(e+n/30)%12;return o-l*Math.max(Math.min(t-3,9-t,1),-1)},u="rgb",c=[Math.round(255*s(0)),Math.round(255*s(8)),Math.round(255*s(4))];return"hsla"===e.type&&(u+="a",c.push(t[3])),a({type:u,values:c})}(e)).values:e.values;return t=t.map((function(e){return(e/=255)<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4)})),Number((.2126*t[0]+.7152*t[1]+.0722*t[2]).toFixed(3))}function u(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.15;return s(e)>.5?d(e,t):f(e,t)}function c(e,t){return e=i(e),t=o(t),"rgb"!==e.type&&"hsl"!==e.type||(e.type+="a"),e.values[3]=t,a(e)}function d(e,t){if(e=i(e),t=o(t),-1!==e.type.indexOf("hsl"))e.values[2]*=1-t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]*=1-t;return a(e)}function f(e,t){if(e=i(e),t=o(t),-1!==e.type.indexOf("hsl"))e.values[2]+=(100-e.values[2])*t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]+=(255-e.values[n])*t;return a(e)}},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(0),o=n(29);function i(e,t){return r.useMemo((function(){return null==e&&null==t?null:function(n){Object(o.a)(e,n),Object(o.a)(t,n)}}),[e,t])}},,function(e,t,n){"use strict";n.d(t,"a",(function(){return d})),n.d(t,"b",(function(){return V})),n.d(t,"c",(function(){return G}));var r=n(0),o=n.n(r),i=o.a.createContext(null);var a=function(e){e()};function l(){var e=a,t=null,n=null;return{clear:function(){t=null,n=null},notify:function(){e((function(){for(var e=t;e;)e.callback(),e=e.next}))},get:function(){for(var e=[],n=t;n;)e.push(n),n=n.next;return e},subscribe:function(e){var r=!0,o=n={callback:e,next:null,prev:n};return o.prev?o.prev.next=o:t=o,function(){r&&null!==t&&(r=!1,o.next?o.next.prev=o.prev:n=o.prev,o.prev?o.prev.next=o.next:t=o.next)}}}}var s={notify:function(){},get:function(){return[]}};function u(e,t){var n,r=s;function o(){a.onStateChange&&a.onStateChange()}function i(){n||(n=t?t.addNestedSub(o):e.subscribe(o),r=l())}var a={addNestedSub:function(e){return i(),r.subscribe(e)},notifyNestedSubs:function(){r.notify()},handleChangeWrapper:o,isSubscribed:function(){return Boolean(n)},trySubscribe:i,tryUnsubscribe:function(){n&&(n(),n=void 0,r.clear(),r=s)},getListeners:function(){return r}};return a}var c="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?r.useLayoutEffect:r.useEffect;var d=function(e){var t=e.store,n=e.context,a=e.children,l=Object(r.useMemo)((function(){var e=u(t);return{store:t,subscription:e}}),[t]),s=Object(r.useMemo)((function(){return t.getState()}),[t]);c((function(){var e=l.subscription;return e.onStateChange=e.notifyNestedSubs,e.trySubscribe(),s!==t.getState()&&e.notifyNestedSubs(),function(){e.tryUnsubscribe(),e.onStateChange=null}}),[l,s]);var d=n||i;return o.a.createElement(d.Provider,{value:l},a)},f=n(1),p=n(23),h=n(58),m=n.n(h),v=n(67),g=["getDisplayName","methodName","renderCountProp","shouldHandleStateChanges","storeKey","withRef","forwardRef","context"],b=["reactReduxForwardedRef"],y=[],x=[null,null];function w(e,t){var n=e[1];return[t.payload,n+1]}function O(e,t,n){c((function(){return e.apply(void 0,t)}),n)}function k(e,t,n,r,o,i,a){e.current=r,t.current=o,n.current=!1,i.current&&(i.current=null,a())}function E(e,t,n,r,o,i,a,l,s,u){if(e){var c=!1,d=null,f=function(){if(!c){var e,n,f=t.getState();try{e=r(f,o.current)}catch(p){n=p,d=p}n||(d=null),e===i.current?a.current||s():(i.current=e,l.current=e,a.current=!0,u({type:"STORE_UPDATED",payload:{error:n}}))}};n.onStateChange=f,n.trySubscribe(),f();return function(){if(c=!0,n.tryUnsubscribe(),n.onStateChange=null,d)throw d}}}var S=function(){return[null,0]};function j(e,t){void 0===t&&(t={});var n=t,a=n.getDisplayName,l=void 0===a?function(e){return"ConnectAdvanced("+e+")"}:a,s=n.methodName,c=void 0===s?"connectAdvanced":s,d=n.renderCountProp,h=void 0===d?void 0:d,j=n.shouldHandleStateChanges,C=void 0===j||j,_=n.storeKey,P=void 0===_?"store":_,T=(n.withRef,n.forwardRef),M=void 0!==T&&T,R=n.context,N=void 0===R?i:R,A=Object(p.a)(n,g),L=N;return function(t){var n=t.displayName||t.name||"Component",i=l(n),a=Object(f.a)({},A,{getDisplayName:l,methodName:c,renderCountProp:h,shouldHandleStateChanges:C,storeKey:P,displayName:i,wrappedComponentName:n,WrappedComponent:t}),s=A.pure;var d=s?r.useMemo:function(e){return e()};function g(n){var i=Object(r.useMemo)((function(){var e=n.reactReduxForwardedRef,t=Object(p.a)(n,b);return[n.context,e,t]}),[n]),l=i[0],s=i[1],c=i[2],h=Object(r.useMemo)((function(){return l&&l.Consumer&&Object(v.isContextConsumer)(o.a.createElement(l.Consumer,null))?l:L}),[l,L]),m=Object(r.useContext)(h),g=Boolean(n.store)&&Boolean(n.store.getState)&&Boolean(n.store.dispatch);Boolean(m)&&Boolean(m.store);var j=g?n.store:m.store,_=Object(r.useMemo)((function(){return function(t){return e(t.dispatch,a)}(j)}),[j]),P=Object(r.useMemo)((function(){if(!C)return x;var e=u(j,g?null:m.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]}),[j,g,m]),T=P[0],M=P[1],R=Object(r.useMemo)((function(){return g?m:Object(f.a)({},m,{subscription:T})}),[g,m,T]),N=Object(r.useReducer)(w,y,S),A=N[0][0],I=N[1];if(A&&A.error)throw A.error;var z=Object(r.useRef)(),D=Object(r.useRef)(c),F=Object(r.useRef)(),W=Object(r.useRef)(!1),B=d((function(){return F.current&&c===D.current?F.current:_(j.getState(),c)}),[j,A,c]);O(k,[D,z,W,c,B,F,M]),O(E,[C,j,T,_,D,z,W,F,M,I],[j,T,_]);var U=Object(r.useMemo)((function(){return o.a.createElement(t,Object(f.a)({},B,{ref:s}))}),[s,t,B]);return Object(r.useMemo)((function(){return C?o.a.createElement(h.Provider,{value:R},U):U}),[h,U,R])}var j=s?o.a.memo(g):g;if(j.WrappedComponent=t,j.displayName=g.displayName=i,M){var _=o.a.forwardRef((function(e,t){return o.a.createElement(j,Object(f.a)({},e,{reactReduxForwardedRef:t}))}));return _.displayName=i,_.WrappedComponent=t,m()(_,t)}return m()(j,t)}}function C(e,t){return e===t?0!==e||0!==t||1/e===1/t:e!==e&&t!==t}function _(e,t){if(C(e,t))return!0;if("object"!==typeof e||null===e||"object"!==typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var o=0;o=0;r--){var o=t[r](e);if(o)return o}return function(t,r){throw new Error("Invalid value of type "+typeof e+" for "+n+" argument when connecting component "+r.wrappedComponentName+".")}}function U(e,t){return e===t}function $(e){var t=void 0===e?{}:e,n=t.connectHOC,r=void 0===n?j:n,o=t.mapStateToPropsFactories,i=void 0===o?N:o,a=t.mapDispatchToPropsFactories,l=void 0===a?R:a,s=t.mergePropsFactories,u=void 0===s?L:s,c=t.selectorFactory,d=void 0===c?F:c;return function(e,t,n,o){void 0===o&&(o={});var a=o,s=a.pure,c=void 0===s||s,h=a.areStatesEqual,m=void 0===h?U:h,v=a.areOwnPropsEqual,g=void 0===v?_:v,b=a.areStatePropsEqual,y=void 0===b?_:b,x=a.areMergedPropsEqual,w=void 0===x?_:x,O=Object(p.a)(a,W),k=B(e,i,"mapStateToProps"),E=B(t,l,"mapDispatchToProps"),S=B(n,u,"mergeProps");return r(d,Object(f.a)({methodName:"connect",getDisplayName:function(e){return"Connect("+e+")"},shouldHandleStateChanges:Boolean(e),initMapStateToProps:k,initMapDispatchToProps:E,initMergeProps:S,pure:c,areStatesEqual:m,areOwnPropsEqual:g,areStatePropsEqual:y,areMergedPropsEqual:w},O))}}var V=$();function H(){return Object(r.useContext)(i)}var q=function(e,t){return e===t};function Y(e){void 0===e&&(e=i);var t=e===i?H:function(){return Object(r.useContext)(e)};return function(e,n){void 0===n&&(n=q);var o=t(),i=function(e,t,n,o){var i,a=Object(r.useReducer)((function(e){return e+1}),0)[1],l=Object(r.useMemo)((function(){return u(n,o)}),[n,o]),s=Object(r.useRef)(),d=Object(r.useRef)(),f=Object(r.useRef)(),p=Object(r.useRef)(),h=n.getState();try{if(e!==d.current||h!==f.current||s.current){var m=e(h);i=void 0!==p.current&&t(m,p.current)?p.current:m}else i=p.current}catch(v){throw s.current&&(v.message+="\nThe error may be correlated with this previous error:\n"+s.current.stack+"\n\n"),v}return c((function(){d.current=e,f.current=h,p.current=i,s.current=void 0})),c((function(){function e(){try{var e=n.getState();if(e===f.current)return;var r=d.current(e);if(t(r,p.current))return;p.current=r,f.current=e}catch(v){s.current=v}a()}return l.onStateChange=e,l.trySubscribe(),e(),function(){return l.tryUnsubscribe()}}),[n,l]),i}(e,n,o.store,o.subscription);return Object(r.useDebugValue)(i),i}}var X,G=Y(),K=n(13);X=K.unstable_batchedUpdates,a=X},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(109);function o(e,t,n){return(t=Object(r.a)(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}},function(e,t,n){"use strict";!function e(){if("undefined"!==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE){0;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}}(),e.exports=n(196)},function(e,t,n){(function(e,r){var o;(function(){var i="Expected a function",a="__lodash_placeholder__",l=[["ary",128],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",32],["partialRight",64],["rearg",256]],s="[object Arguments]",u="[object Array]",c="[object Boolean]",d="[object Date]",f="[object Error]",p="[object Function]",h="[object GeneratorFunction]",m="[object Map]",v="[object Number]",g="[object Object]",b="[object RegExp]",y="[object Set]",x="[object String]",w="[object Symbol]",O="[object WeakMap]",k="[object ArrayBuffer]",E="[object DataView]",S="[object Float32Array]",j="[object Float64Array]",C="[object Int8Array]",_="[object Int16Array]",P="[object Int32Array]",T="[object Uint8Array]",M="[object Uint16Array]",R="[object Uint32Array]",N=/\b__p \+= '';/g,A=/\b(__p \+=) '' \+/g,L=/(__e\(.*?\)|\b__t\)) \+\n'';/g,I=/&(?:amp|lt|gt|quot|#39);/g,z=/[&<>"']/g,D=RegExp(I.source),F=RegExp(z.source),W=/<%-([\s\S]+?)%>/g,B=/<%([\s\S]+?)%>/g,U=/<%=([\s\S]+?)%>/g,$=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,V=/^\w*$/,H=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,q=/[\\^$.*+?()[\]{}|]/g,Y=RegExp(q.source),X=/^\s+/,G=/\s/,K=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,Q=/\{\n\/\* \[wrapped with (.+)\] \*/,Z=/,? & /,J=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,ee=/[()=,{}\[\]\/\s]/,te=/\\(\\)?/g,ne=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,re=/\w*$/,oe=/^[-+]0x[0-9a-f]+$/i,ie=/^0b[01]+$/i,ae=/^\[object .+?Constructor\]$/,le=/^0o[0-7]+$/i,se=/^(?:0|[1-9]\d*)$/,ue=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,ce=/($^)/,de=/['\n\r\u2028\u2029\\]/g,fe="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",pe="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",he="[\\ud800-\\udfff]",me="["+pe+"]",ve="["+fe+"]",ge="\\d+",be="[\\u2700-\\u27bf]",ye="[a-z\\xdf-\\xf6\\xf8-\\xff]",xe="[^\\ud800-\\udfff"+pe+ge+"\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde]",we="\\ud83c[\\udffb-\\udfff]",Oe="[^\\ud800-\\udfff]",ke="(?:\\ud83c[\\udde6-\\uddff]){2}",Ee="[\\ud800-\\udbff][\\udc00-\\udfff]",Se="[A-Z\\xc0-\\xd6\\xd8-\\xde]",je="(?:"+ye+"|"+xe+")",Ce="(?:"+Se+"|"+xe+")",_e="(?:"+ve+"|"+we+")"+"?",Pe="[\\ufe0e\\ufe0f]?"+_e+("(?:\\u200d(?:"+[Oe,ke,Ee].join("|")+")[\\ufe0e\\ufe0f]?"+_e+")*"),Te="(?:"+[be,ke,Ee].join("|")+")"+Pe,Me="(?:"+[Oe+ve+"?",ve,ke,Ee,he].join("|")+")",Re=RegExp("['\u2019]","g"),Ne=RegExp(ve,"g"),Ae=RegExp(we+"(?="+we+")|"+Me+Pe,"g"),Le=RegExp([Se+"?"+ye+"+(?:['\u2019](?:d|ll|m|re|s|t|ve))?(?="+[me,Se,"$"].join("|")+")",Ce+"+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?(?="+[me,Se+je,"$"].join("|")+")",Se+"?"+je+"+(?:['\u2019](?:d|ll|m|re|s|t|ve))?",Se+"+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?","\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",ge,Te].join("|"),"g"),Ie=RegExp("[\\u200d\\ud800-\\udfff"+fe+"\\ufe0e\\ufe0f]"),ze=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,De=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Fe=-1,We={};We[S]=We[j]=We[C]=We[_]=We[P]=We[T]=We["[object Uint8ClampedArray]"]=We[M]=We[R]=!0,We[s]=We[u]=We[k]=We[c]=We[E]=We[d]=We[f]=We[p]=We[m]=We[v]=We[g]=We[b]=We[y]=We[x]=We[O]=!1;var Be={};Be[s]=Be[u]=Be[k]=Be[E]=Be[c]=Be[d]=Be[S]=Be[j]=Be[C]=Be[_]=Be[P]=Be[m]=Be[v]=Be[g]=Be[b]=Be[y]=Be[x]=Be[w]=Be[T]=Be["[object Uint8ClampedArray]"]=Be[M]=Be[R]=!0,Be[f]=Be[p]=Be[O]=!1;var Ue={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},$e=parseFloat,Ve=parseInt,He="object"==typeof e&&e&&e.Object===Object&&e,qe="object"==typeof self&&self&&self.Object===Object&&self,Ye=He||qe||Function("return this")(),Xe=t&&!t.nodeType&&t,Ge=Xe&&"object"==typeof r&&r&&!r.nodeType&&r,Ke=Ge&&Ge.exports===Xe,Qe=Ke&&He.process,Ze=function(){try{var e=Ge&&Ge.require&&Ge.require("util").types;return e||Qe&&Qe.binding&&Qe.binding("util")}catch(t){}}(),Je=Ze&&Ze.isArrayBuffer,et=Ze&&Ze.isDate,tt=Ze&&Ze.isMap,nt=Ze&&Ze.isRegExp,rt=Ze&&Ze.isSet,ot=Ze&&Ze.isTypedArray;function it(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function at(e,t,n,r){for(var o=-1,i=null==e?0:e.length;++o-1}function ft(e,t,n){for(var r=-1,o=null==e?0:e.length;++r-1;);return n}function Lt(e,t){for(var n=e.length;n--&&wt(t,e[n],0)>-1;);return n}function It(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}var zt=jt({"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"}),Dt=jt({"&":"&","<":"<",">":">",'"':""","'":"'"});function Ft(e){return"\\"+Ue[e]}function Wt(e){return Ie.test(e)}function Bt(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}function Ut(e,t){return function(n){return e(t(n))}}function $t(e,t){for(var n=-1,r=e.length,o=0,i=[];++n",""":'"',"'":"'"});var Kt=function e(t){var n=(t=null==t?Ye:Kt.defaults(Ye.Object(),t,Kt.pick(Ye,De))).Array,r=t.Date,o=t.Error,G=t.Function,fe=t.Math,pe=t.Object,he=t.RegExp,me=t.String,ve=t.TypeError,ge=n.prototype,be=G.prototype,ye=pe.prototype,xe=t["__core-js_shared__"],we=be.toString,Oe=ye.hasOwnProperty,ke=0,Ee=function(){var e=/[^.]+$/.exec(xe&&xe.keys&&xe.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""}(),Se=ye.toString,je=we.call(pe),Ce=Ye._,_e=he("^"+we.call(Oe).replace(q,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Pe=Ke?t.Buffer:void 0,Te=t.Symbol,Me=t.Uint8Array,Ae=Pe?Pe.allocUnsafe:void 0,Ie=Ut(pe.getPrototypeOf,pe),Ue=pe.create,He=ye.propertyIsEnumerable,qe=ge.splice,Xe=Te?Te.isConcatSpreadable:void 0,Ge=Te?Te.iterator:void 0,Qe=Te?Te.toStringTag:void 0,Ze=function(){try{var e=ei(pe,"defineProperty");return e({},"",{}),e}catch(t){}}(),bt=t.clearTimeout!==Ye.clearTimeout&&t.clearTimeout,jt=r&&r.now!==Ye.Date.now&&r.now,Qt=t.setTimeout!==Ye.setTimeout&&t.setTimeout,Zt=fe.ceil,Jt=fe.floor,en=pe.getOwnPropertySymbols,tn=Pe?Pe.isBuffer:void 0,nn=t.isFinite,rn=ge.join,on=Ut(pe.keys,pe),an=fe.max,ln=fe.min,sn=r.now,un=t.parseInt,cn=fe.random,dn=ge.reverse,fn=ei(t,"DataView"),pn=ei(t,"Map"),hn=ei(t,"Promise"),mn=ei(t,"Set"),vn=ei(t,"WeakMap"),gn=ei(pe,"create"),bn=vn&&new vn,yn={},xn=Ci(fn),wn=Ci(pn),On=Ci(hn),kn=Ci(mn),En=Ci(vn),Sn=Te?Te.prototype:void 0,jn=Sn?Sn.valueOf:void 0,Cn=Sn?Sn.toString:void 0;function _n(e){if(Va(e)&&!Na(e)&&!(e instanceof Rn)){if(e instanceof Mn)return e;if(Oe.call(e,"__wrapped__"))return _i(e)}return new Mn(e)}var Pn=function(){function e(){}return function(t){if(!$a(t))return{};if(Ue)return Ue(t);e.prototype=t;var n=new e;return e.prototype=void 0,n}}();function Tn(){}function Mn(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=void 0}function Rn(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=4294967295,this.__views__=[]}function Nn(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t=t?e:t)),e}function Kn(e,t,n,r,o,i){var a,l=1&t,u=2&t,f=4&t;if(n&&(a=o?n(e,r,o,i):n(e)),void 0!==a)return a;if(!$a(e))return e;var O=Na(e);if(O){if(a=function(e){var t=e.length,n=new e.constructor(t);t&&"string"==typeof e[0]&&Oe.call(e,"index")&&(n.index=e.index,n.input=e.input);return n}(e),!l)return bo(e,a)}else{var N=ri(e),A=N==p||N==h;if(za(e))return fo(e,l);if(N==g||N==s||A&&!o){if(a=u||A?{}:ii(e),!l)return u?function(e,t){return yo(e,ni(e),t)}(e,function(e,t){return e&&yo(t,wl(t),e)}(a,e)):function(e,t){return yo(e,ti(e),t)}(e,qn(a,e))}else{if(!Be[N])return o?e:{};a=function(e,t,n){var r=e.constructor;switch(t){case k:return po(e);case c:case d:return new r(+e);case E:return function(e,t){var n=t?po(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}(e,n);case S:case j:case C:case _:case P:case T:case"[object Uint8ClampedArray]":case M:case R:return ho(e,n);case m:return new r;case v:case x:return new r(e);case b:return function(e){var t=new e.constructor(e.source,re.exec(e));return t.lastIndex=e.lastIndex,t}(e);case y:return new r;case w:return o=e,jn?pe(jn.call(o)):{}}var o}(e,N,l)}}i||(i=new zn);var L=i.get(e);if(L)return L;i.set(e,a),Ga(e)?e.forEach((function(r){a.add(Kn(r,t,n,r,e,i))})):Ha(e)&&e.forEach((function(r,o){a.set(o,Kn(r,t,n,o,e,i))}));var I=O?void 0:(f?u?Yo:qo:u?wl:xl)(e);return lt(I||e,(function(r,o){I&&(r=e[o=r]),$n(a,o,Kn(r,t,n,o,e,i))})),a}function Qn(e,t,n){var r=n.length;if(null==e)return!r;for(e=pe(e);r--;){var o=n[r],i=t[o],a=e[o];if(void 0===a&&!(o in e)||!i(a))return!1}return!0}function Zn(e,t,n){if("function"!=typeof e)throw new ve(i);return xi((function(){e.apply(void 0,n)}),t)}function Jn(e,t,n,r){var o=-1,i=dt,a=!0,l=e.length,s=[],u=t.length;if(!l)return s;n&&(t=pt(t,Mt(n))),r?(i=ft,a=!1):t.length>=200&&(i=Nt,a=!1,t=new In(t));e:for(;++o-1},An.prototype.set=function(e,t){var n=this.__data__,r=Vn(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},Ln.prototype.clear=function(){this.size=0,this.__data__={hash:new Nn,map:new(pn||An),string:new Nn}},Ln.prototype.delete=function(e){var t=Zo(this,e).delete(e);return this.size-=t?1:0,t},Ln.prototype.get=function(e){return Zo(this,e).get(e)},Ln.prototype.has=function(e){return Zo(this,e).has(e)},Ln.prototype.set=function(e,t){var n=Zo(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},In.prototype.add=In.prototype.push=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this},In.prototype.has=function(e){return this.__data__.has(e)},zn.prototype.clear=function(){this.__data__=new An,this.size=0},zn.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},zn.prototype.get=function(e){return this.__data__.get(e)},zn.prototype.has=function(e){return this.__data__.has(e)},zn.prototype.set=function(e,t){var n=this.__data__;if(n instanceof An){var r=n.__data__;if(!pn||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new Ln(r)}return n.set(e,t),this.size=n.size,this};var er=Oo(sr),tr=Oo(ur,!0);function nr(e,t){var n=!0;return er(e,(function(e,r,o){return n=!!t(e,r,o)})),n}function rr(e,t,n){for(var r=-1,o=e.length;++r0&&n(l)?t>1?ir(l,t-1,n,r,o):ht(o,l):r||(o[o.length]=l)}return o}var ar=ko(),lr=ko(!0);function sr(e,t){return e&&ar(e,t,xl)}function ur(e,t){return e&&lr(e,t,xl)}function cr(e,t){return ct(t,(function(t){return Wa(e[t])}))}function dr(e,t){for(var n=0,r=(t=lo(t,e)).length;null!=e&&nt}function mr(e,t){return null!=e&&Oe.call(e,t)}function vr(e,t){return null!=e&&t in pe(e)}function gr(e,t,r){for(var o=r?ft:dt,i=e[0].length,a=e.length,l=a,s=n(a),u=1/0,c=[];l--;){var d=e[l];l&&t&&(d=pt(d,Mt(t))),u=ln(d.length,u),s[l]=!r&&(t||i>=120&&d.length>=120)?new In(l&&d):void 0}d=e[0];var f=-1,p=s[0];e:for(;++f=l)return s;var u=n[r];return s*("desc"==u?-1:1)}}return e.index-t.index}(e,t,n)}))}function Nr(e,t,n){for(var r=-1,o=t.length,i={};++r-1;)l!==e&&qe.call(l,s,1),qe.call(e,s,1);return e}function Lr(e,t){for(var n=e?t.length:0,r=n-1;n--;){var o=t[n];if(n==r||o!==i){var i=o;li(o)?qe.call(e,o,1):Jr(e,o)}}return e}function Ir(e,t){return e+Jt(cn()*(t-e+1))}function zr(e,t){var n="";if(!e||t<1||t>9007199254740991)return n;do{t%2&&(n+=e),(t=Jt(t/2))&&(e+=e)}while(t);return n}function Dr(e,t){return wi(mi(e,t,ql),e+"")}function Fr(e){return Fn(Pl(e))}function Wr(e,t){var n=Pl(e);return Ei(n,Gn(t,0,n.length))}function Br(e,t,n,r){if(!$a(e))return e;for(var o=-1,i=(t=lo(t,e)).length,a=i-1,l=e;null!=l&&++oi?0:i+t),(r=r>i?i:r)<0&&(r+=i),i=t>r?0:r-t>>>0,t>>>=0;for(var a=n(i);++o>>1,a=e[i];null!==a&&!Qa(a)&&(n?a<=t:a=200){var u=t?null:Do(e);if(u)return Vt(u);a=!1,o=Nt,s=new In}else s=t?[]:l;e:for(;++r=r?e:Hr(e,t,n)}var co=bt||function(e){return Ye.clearTimeout(e)};function fo(e,t){if(t)return e.slice();var n=e.length,r=Ae?Ae(n):new e.constructor(n);return e.copy(r),r}function po(e){var t=new e.constructor(e.byteLength);return new Me(t).set(new Me(e)),t}function ho(e,t){var n=t?po(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function mo(e,t){if(e!==t){var n=void 0!==e,r=null===e,o=e===e,i=Qa(e),a=void 0!==t,l=null===t,s=t===t,u=Qa(t);if(!l&&!u&&!i&&e>t||i&&a&&s&&!l&&!u||r&&a&&s||!n&&s||!o)return 1;if(!r&&!i&&!u&&e1?n[o-1]:void 0,a=o>2?n[2]:void 0;for(i=e.length>3&&"function"==typeof i?(o--,i):void 0,a&&si(n[0],n[1],a)&&(i=o<3?void 0:i,o=1),t=pe(t);++r-1?o[i?t[a]:a]:void 0}}function _o(e){return Ho((function(t){var n=t.length,r=n,o=Mn.prototype.thru;for(e&&t.reverse();r--;){var a=t[r];if("function"!=typeof a)throw new ve(i);if(o&&!l&&"wrapper"==Go(a))var l=new Mn([],!0)}for(r=l?r:n;++r1&&y.reverse(),d&&ul))return!1;var u=i.get(e),c=i.get(t);if(u&&c)return u==t&&c==e;var d=-1,f=!0,p=2&n?new In:void 0;for(i.set(e,t),i.set(t,e);++d-1&&e%1==0&&e1?"& ":"")+t[r],t=t.join(n>2?", ":" "),e.replace(K,"{\n/* [wrapped with "+t+"] */\n")}(r,function(e,t){return lt(l,(function(n){var r="_."+n[0];t&n[1]&&!dt(e,r)&&e.push(r)})),e.sort()}(function(e){var t=e.match(Q);return t?t[1].split(Z):[]}(r),n)))}function ki(e){var t=0,n=0;return function(){var r=sn(),o=16-(r-n);if(n=r,o>0){if(++t>=800)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}function Ei(e,t){var n=-1,r=e.length,o=r-1;for(t=void 0===t?r:t;++n1?e[t-1]:void 0;return n="function"==typeof n?(e.pop(),n):void 0,Xi(e,n)}));function ta(e){var t=_n(e);return t.__chain__=!0,t}function na(e,t){return t(e)}var ra=Ho((function(e){var t=e.length,n=t?e[0]:0,r=this.__wrapped__,o=function(t){return Xn(t,e)};return!(t>1||this.__actions__.length)&&r instanceof Rn&&li(n)?((r=r.slice(n,+n+(t?1:0))).__actions__.push({func:na,args:[o],thisArg:void 0}),new Mn(r,this.__chain__).thru((function(e){return t&&!e.length&&e.push(void 0),e}))):this.thru(o)}));var oa=xo((function(e,t,n){Oe.call(e,n)?++e[n]:Yn(e,n,1)}));var ia=Co(Ri),aa=Co(Ni);function la(e,t){return(Na(e)?lt:er)(e,Qo(t,3))}function sa(e,t){return(Na(e)?st:tr)(e,Qo(t,3))}var ua=xo((function(e,t,n){Oe.call(e,n)?e[n].push(t):Yn(e,n,[t])}));var ca=Dr((function(e,t,r){var o=-1,i="function"==typeof t,a=La(e)?n(e.length):[];return er(e,(function(e){a[++o]=i?it(t,e,r):br(e,t,r)})),a})),da=xo((function(e,t,n){Yn(e,n,t)}));function fa(e,t){return(Na(e)?pt:Cr)(e,Qo(t,3))}var pa=xo((function(e,t,n){e[n?0:1].push(t)}),(function(){return[[],[]]}));var ha=Dr((function(e,t){if(null==e)return[];var n=t.length;return n>1&&si(e,t[0],t[1])?t=[]:n>2&&si(t[0],t[1],t[2])&&(t=[t[0]]),Rr(e,ir(t,1),[])})),ma=jt||function(){return Ye.Date.now()};function va(e,t,n){return t=n?void 0:t,Wo(e,128,void 0,void 0,void 0,void 0,t=e&&null==t?e.length:t)}function ga(e,t){var n;if("function"!=typeof t)throw new ve(i);return e=rl(e),function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=void 0),n}}var ba=Dr((function(e,t,n){var r=1;if(n.length){var o=$t(n,Ko(ba));r|=32}return Wo(e,r,t,n,o)})),ya=Dr((function(e,t,n){var r=3;if(n.length){var o=$t(n,Ko(ya));r|=32}return Wo(t,r,e,n,o)}));function xa(e,t,n){var r,o,a,l,s,u,c=0,d=!1,f=!1,p=!0;if("function"!=typeof e)throw new ve(i);function h(t){var n=r,i=o;return r=o=void 0,c=t,l=e.apply(i,n)}function m(e){return c=e,s=xi(g,t),d?h(e):l}function v(e){var n=e-u;return void 0===u||n>=t||n<0||f&&e-c>=a}function g(){var e=ma();if(v(e))return b(e);s=xi(g,function(e){var n=t-(e-u);return f?ln(n,a-(e-c)):n}(e))}function b(e){return s=void 0,p&&r?h(e):(r=o=void 0,l)}function y(){var e=ma(),n=v(e);if(r=arguments,o=this,u=e,n){if(void 0===s)return m(u);if(f)return co(s),s=xi(g,t),h(u)}return void 0===s&&(s=xi(g,t)),l}return t=il(t)||0,$a(n)&&(d=!!n.leading,a=(f="maxWait"in n)?an(il(n.maxWait)||0,t):a,p="trailing"in n?!!n.trailing:p),y.cancel=function(){void 0!==s&&co(s),c=0,r=u=o=s=void 0},y.flush=function(){return void 0===s?l:b(ma())},y}var wa=Dr((function(e,t){return Zn(e,1,t)})),Oa=Dr((function(e,t,n){return Zn(e,il(t)||0,n)}));function ka(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new ve(i);var n=function(){var r=arguments,o=t?t.apply(this,r):r[0],i=n.cache;if(i.has(o))return i.get(o);var a=e.apply(this,r);return n.cache=i.set(o,a)||i,a};return n.cache=new(ka.Cache||Ln),n}function Ea(e){if("function"!=typeof e)throw new ve(i);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}ka.Cache=Ln;var Sa=so((function(e,t){var n=(t=1==t.length&&Na(t[0])?pt(t[0],Mt(Qo())):pt(ir(t,1),Mt(Qo()))).length;return Dr((function(r){for(var o=-1,i=ln(r.length,n);++o=t})),Ra=yr(function(){return arguments}())?yr:function(e){return Va(e)&&Oe.call(e,"callee")&&!He.call(e,"callee")},Na=n.isArray,Aa=Je?Mt(Je):function(e){return Va(e)&&pr(e)==k};function La(e){return null!=e&&Ua(e.length)&&!Wa(e)}function Ia(e){return Va(e)&&La(e)}var za=tn||is,Da=et?Mt(et):function(e){return Va(e)&&pr(e)==d};function Fa(e){if(!Va(e))return!1;var t=pr(e);return t==f||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!Ya(e)}function Wa(e){if(!$a(e))return!1;var t=pr(e);return t==p||t==h||"[object AsyncFunction]"==t||"[object Proxy]"==t}function Ba(e){return"number"==typeof e&&e==rl(e)}function Ua(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991}function $a(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function Va(e){return null!=e&&"object"==typeof e}var Ha=tt?Mt(tt):function(e){return Va(e)&&ri(e)==m};function qa(e){return"number"==typeof e||Va(e)&&pr(e)==v}function Ya(e){if(!Va(e)||pr(e)!=g)return!1;var t=Ie(e);if(null===t)return!0;var n=Oe.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&we.call(n)==je}var Xa=nt?Mt(nt):function(e){return Va(e)&&pr(e)==b};var Ga=rt?Mt(rt):function(e){return Va(e)&&ri(e)==y};function Ka(e){return"string"==typeof e||!Na(e)&&Va(e)&&pr(e)==x}function Qa(e){return"symbol"==typeof e||Va(e)&&pr(e)==w}var Za=ot?Mt(ot):function(e){return Va(e)&&Ua(e.length)&&!!We[pr(e)]};var Ja=Lo(jr),el=Lo((function(e,t){return e<=t}));function tl(e){if(!e)return[];if(La(e))return Ka(e)?Yt(e):bo(e);if(Ge&&e[Ge])return function(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}(e[Ge]());var t=ri(e);return(t==m?Bt:t==y?Vt:Pl)(e)}function nl(e){return e?(e=il(e))===1/0||e===-1/0?17976931348623157e292*(e<0?-1:1):e===e?e:0:0===e?e:0}function rl(e){var t=nl(e),n=t%1;return t===t?n?t-n:t:0}function ol(e){return e?Gn(rl(e),0,4294967295):0}function il(e){if("number"==typeof e)return e;if(Qa(e))return NaN;if($a(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=$a(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=Tt(e);var n=ie.test(e);return n||le.test(e)?Ve(e.slice(2),n?2:8):oe.test(e)?NaN:+e}function al(e){return yo(e,wl(e))}function ll(e){return null==e?"":Qr(e)}var sl=wo((function(e,t){if(fi(t)||La(t))yo(t,xl(t),e);else for(var n in t)Oe.call(t,n)&&$n(e,n,t[n])})),ul=wo((function(e,t){yo(t,wl(t),e)})),cl=wo((function(e,t,n,r){yo(t,wl(t),e,r)})),dl=wo((function(e,t,n,r){yo(t,xl(t),e,r)})),fl=Ho(Xn);var pl=Dr((function(e,t){e=pe(e);var n=-1,r=t.length,o=r>2?t[2]:void 0;for(o&&si(t[0],t[1],o)&&(r=1);++n1),t})),yo(e,Yo(e),n),r&&(n=Kn(n,7,$o));for(var o=t.length;o--;)Jr(n,t[o]);return n}));var Sl=Ho((function(e,t){return null==e?{}:function(e,t){return Nr(e,t,(function(t,n){return vl(e,n)}))}(e,t)}));function jl(e,t){if(null==e)return{};var n=pt(Yo(e),(function(e){return[e]}));return t=Qo(t),Nr(e,n,(function(e,n){return t(e,n[0])}))}var Cl=Fo(xl),_l=Fo(wl);function Pl(e){return null==e?[]:Rt(e,xl(e))}var Tl=So((function(e,t,n){return t=t.toLowerCase(),e+(n?Ml(t):t)}));function Ml(e){return Fl(ll(e).toLowerCase())}function Rl(e){return(e=ll(e))&&e.replace(ue,zt).replace(Ne,"")}var Nl=So((function(e,t,n){return e+(n?"-":"")+t.toLowerCase()})),Al=So((function(e,t,n){return e+(n?" ":"")+t.toLowerCase()})),Ll=Eo("toLowerCase");var Il=So((function(e,t,n){return e+(n?"_":"")+t.toLowerCase()}));var zl=So((function(e,t,n){return e+(n?" ":"")+Fl(t)}));var Dl=So((function(e,t,n){return e+(n?" ":"")+t.toUpperCase()})),Fl=Eo("toUpperCase");function Wl(e,t,n){return e=ll(e),void 0===(t=n?void 0:t)?function(e){return ze.test(e)}(e)?function(e){return e.match(Le)||[]}(e):function(e){return e.match(J)||[]}(e):e.match(t)||[]}var Bl=Dr((function(e,t){try{return it(e,void 0,t)}catch(n){return Fa(n)?n:new o(n)}})),Ul=Ho((function(e,t){return lt(t,(function(t){t=ji(t),Yn(e,t,ba(e[t],e))})),e}));function $l(e){return function(){return e}}var Vl=_o(),Hl=_o(!0);function ql(e){return e}function Yl(e){return kr("function"==typeof e?e:Kn(e,1))}var Xl=Dr((function(e,t){return function(n){return br(n,e,t)}})),Gl=Dr((function(e,t){return function(n){return br(e,n,t)}}));function Kl(e,t,n){var r=xl(t),o=cr(t,r);null!=n||$a(t)&&(o.length||!r.length)||(n=t,t=e,e=this,o=cr(t,xl(t)));var i=!($a(n)&&"chain"in n)||!!n.chain,a=Wa(e);return lt(o,(function(n){var r=t[n];e[n]=r,a&&(e.prototype[n]=function(){var t=this.__chain__;if(i||t){var n=e(this.__wrapped__),o=n.__actions__=bo(this.__actions__);return o.push({func:r,args:arguments,thisArg:e}),n.__chain__=t,n}return r.apply(e,ht([this.value()],arguments))})})),e}function Ql(){}var Zl=Ro(pt),Jl=Ro(ut),es=Ro(gt);function ts(e){return ui(e)?St(ji(e)):function(e){return function(t){return dr(t,e)}}(e)}var ns=Ao(),rs=Ao(!0);function os(){return[]}function is(){return!1}var as=Mo((function(e,t){return e+t}),0),ls=zo("ceil"),ss=Mo((function(e,t){return e/t}),1),us=zo("floor");var cs=Mo((function(e,t){return e*t}),1),ds=zo("round"),fs=Mo((function(e,t){return e-t}),0);return _n.after=function(e,t){if("function"!=typeof t)throw new ve(i);return e=rl(e),function(){if(--e<1)return t.apply(this,arguments)}},_n.ary=va,_n.assign=sl,_n.assignIn=ul,_n.assignInWith=cl,_n.assignWith=dl,_n.at=fl,_n.before=ga,_n.bind=ba,_n.bindAll=Ul,_n.bindKey=ya,_n.castArray=function(){if(!arguments.length)return[];var e=arguments[0];return Na(e)?e:[e]},_n.chain=ta,_n.chunk=function(e,t,r){t=(r?si(e,t,r):void 0===t)?1:an(rl(t),0);var o=null==e?0:e.length;if(!o||t<1)return[];for(var i=0,a=0,l=n(Zt(o/t));io?0:o+n),(r=void 0===r||r>o?o:rl(r))<0&&(r+=o),r=n>r?0:ol(r);n>>0)?(e=ll(e))&&("string"==typeof t||null!=t&&!Xa(t))&&!(t=Qr(t))&&Wt(e)?uo(Yt(e),0,n):e.split(t,n):[]},_n.spread=function(e,t){if("function"!=typeof e)throw new ve(i);return t=null==t?0:an(rl(t),0),Dr((function(n){var r=n[t],o=uo(n,0,t);return r&&ht(o,r),it(e,this,o)}))},_n.tail=function(e){var t=null==e?0:e.length;return t?Hr(e,1,t):[]},_n.take=function(e,t,n){return e&&e.length?Hr(e,0,(t=n||void 0===t?1:rl(t))<0?0:t):[]},_n.takeRight=function(e,t,n){var r=null==e?0:e.length;return r?Hr(e,(t=r-(t=n||void 0===t?1:rl(t)))<0?0:t,r):[]},_n.takeRightWhile=function(e,t){return e&&e.length?to(e,Qo(t,3),!1,!0):[]},_n.takeWhile=function(e,t){return e&&e.length?to(e,Qo(t,3)):[]},_n.tap=function(e,t){return t(e),e},_n.throttle=function(e,t,n){var r=!0,o=!0;if("function"!=typeof e)throw new ve(i);return $a(n)&&(r="leading"in n?!!n.leading:r,o="trailing"in n?!!n.trailing:o),xa(e,t,{leading:r,maxWait:t,trailing:o})},_n.thru=na,_n.toArray=tl,_n.toPairs=Cl,_n.toPairsIn=_l,_n.toPath=function(e){return Na(e)?pt(e,ji):Qa(e)?[e]:bo(Si(ll(e)))},_n.toPlainObject=al,_n.transform=function(e,t,n){var r=Na(e),o=r||za(e)||Za(e);if(t=Qo(t,4),null==n){var i=e&&e.constructor;n=o?r?new i:[]:$a(e)&&Wa(i)?Pn(Ie(e)):{}}return(o?lt:sr)(e,(function(e,r,o){return t(n,e,r,o)})),n},_n.unary=function(e){return va(e,1)},_n.union=Vi,_n.unionBy=Hi,_n.unionWith=qi,_n.uniq=function(e){return e&&e.length?Zr(e):[]},_n.uniqBy=function(e,t){return e&&e.length?Zr(e,Qo(t,2)):[]},_n.uniqWith=function(e,t){return t="function"==typeof t?t:void 0,e&&e.length?Zr(e,void 0,t):[]},_n.unset=function(e,t){return null==e||Jr(e,t)},_n.unzip=Yi,_n.unzipWith=Xi,_n.update=function(e,t,n){return null==e?e:eo(e,t,ao(n))},_n.updateWith=function(e,t,n,r){return r="function"==typeof r?r:void 0,null==e?e:eo(e,t,ao(n),r)},_n.values=Pl,_n.valuesIn=function(e){return null==e?[]:Rt(e,wl(e))},_n.without=Gi,_n.words=Wl,_n.wrap=function(e,t){return ja(ao(t),e)},_n.xor=Ki,_n.xorBy=Qi,_n.xorWith=Zi,_n.zip=Ji,_n.zipObject=function(e,t){return oo(e||[],t||[],$n)},_n.zipObjectDeep=function(e,t){return oo(e||[],t||[],Br)},_n.zipWith=ea,_n.entries=Cl,_n.entriesIn=_l,_n.extend=ul,_n.extendWith=cl,Kl(_n,_n),_n.add=as,_n.attempt=Bl,_n.camelCase=Tl,_n.capitalize=Ml,_n.ceil=ls,_n.clamp=function(e,t,n){return void 0===n&&(n=t,t=void 0),void 0!==n&&(n=(n=il(n))===n?n:0),void 0!==t&&(t=(t=il(t))===t?t:0),Gn(il(e),t,n)},_n.clone=function(e){return Kn(e,4)},_n.cloneDeep=function(e){return Kn(e,5)},_n.cloneDeepWith=function(e,t){return Kn(e,5,t="function"==typeof t?t:void 0)},_n.cloneWith=function(e,t){return Kn(e,4,t="function"==typeof t?t:void 0)},_n.conformsTo=function(e,t){return null==t||Qn(e,t,xl(t))},_n.deburr=Rl,_n.defaultTo=function(e,t){return null==e||e!==e?t:e},_n.divide=ss,_n.endsWith=function(e,t,n){e=ll(e),t=Qr(t);var r=e.length,o=n=void 0===n?r:Gn(rl(n),0,r);return(n-=t.length)>=0&&e.slice(n,o)==t},_n.eq=Pa,_n.escape=function(e){return(e=ll(e))&&F.test(e)?e.replace(z,Dt):e},_n.escapeRegExp=function(e){return(e=ll(e))&&Y.test(e)?e.replace(q,"\\$&"):e},_n.every=function(e,t,n){var r=Na(e)?ut:nr;return n&&si(e,t,n)&&(t=void 0),r(e,Qo(t,3))},_n.find=ia,_n.findIndex=Ri,_n.findKey=function(e,t){return yt(e,Qo(t,3),sr)},_n.findLast=aa,_n.findLastIndex=Ni,_n.findLastKey=function(e,t){return yt(e,Qo(t,3),ur)},_n.floor=us,_n.forEach=la,_n.forEachRight=sa,_n.forIn=function(e,t){return null==e?e:ar(e,Qo(t,3),wl)},_n.forInRight=function(e,t){return null==e?e:lr(e,Qo(t,3),wl)},_n.forOwn=function(e,t){return e&&sr(e,Qo(t,3))},_n.forOwnRight=function(e,t){return e&&ur(e,Qo(t,3))},_n.get=ml,_n.gt=Ta,_n.gte=Ma,_n.has=function(e,t){return null!=e&&oi(e,t,mr)},_n.hasIn=vl,_n.head=Li,_n.identity=ql,_n.includes=function(e,t,n,r){e=La(e)?e:Pl(e),n=n&&!r?rl(n):0;var o=e.length;return n<0&&(n=an(o+n,0)),Ka(e)?n<=o&&e.indexOf(t,n)>-1:!!o&&wt(e,t,n)>-1},_n.indexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var o=null==n?0:rl(n);return o<0&&(o=an(r+o,0)),wt(e,t,o)},_n.inRange=function(e,t,n){return t=nl(t),void 0===n?(n=t,t=0):n=nl(n),function(e,t,n){return e>=ln(t,n)&&e=-9007199254740991&&e<=9007199254740991},_n.isSet=Ga,_n.isString=Ka,_n.isSymbol=Qa,_n.isTypedArray=Za,_n.isUndefined=function(e){return void 0===e},_n.isWeakMap=function(e){return Va(e)&&ri(e)==O},_n.isWeakSet=function(e){return Va(e)&&"[object WeakSet]"==pr(e)},_n.join=function(e,t){return null==e?"":rn.call(e,t)},_n.kebabCase=Nl,_n.last=Fi,_n.lastIndexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var o=r;return void 0!==n&&(o=(o=rl(n))<0?an(r+o,0):ln(o,r-1)),t===t?function(e,t,n){for(var r=n+1;r--;)if(e[r]===t)return r;return r}(e,t,o):xt(e,kt,o,!0)},_n.lowerCase=Al,_n.lowerFirst=Ll,_n.lt=Ja,_n.lte=el,_n.max=function(e){return e&&e.length?rr(e,ql,hr):void 0},_n.maxBy=function(e,t){return e&&e.length?rr(e,Qo(t,2),hr):void 0},_n.mean=function(e){return Et(e,ql)},_n.meanBy=function(e,t){return Et(e,Qo(t,2))},_n.min=function(e){return e&&e.length?rr(e,ql,jr):void 0},_n.minBy=function(e,t){return e&&e.length?rr(e,Qo(t,2),jr):void 0},_n.stubArray=os,_n.stubFalse=is,_n.stubObject=function(){return{}},_n.stubString=function(){return""},_n.stubTrue=function(){return!0},_n.multiply=cs,_n.nth=function(e,t){return e&&e.length?Mr(e,rl(t)):void 0},_n.noConflict=function(){return Ye._===this&&(Ye._=Ce),this},_n.noop=Ql,_n.now=ma,_n.pad=function(e,t,n){e=ll(e);var r=(t=rl(t))?qt(e):0;if(!t||r>=t)return e;var o=(t-r)/2;return No(Jt(o),n)+e+No(Zt(o),n)},_n.padEnd=function(e,t,n){e=ll(e);var r=(t=rl(t))?qt(e):0;return t&&rt){var r=e;e=t,t=r}if(n||e%1||t%1){var o=cn();return ln(e+o*(t-e+$e("1e-"+((o+"").length-1))),t)}return Ir(e,t)},_n.reduce=function(e,t,n){var r=Na(e)?mt:Ct,o=arguments.length<3;return r(e,Qo(t,4),n,o,er)},_n.reduceRight=function(e,t,n){var r=Na(e)?vt:Ct,o=arguments.length<3;return r(e,Qo(t,4),n,o,tr)},_n.repeat=function(e,t,n){return t=(n?si(e,t,n):void 0===t)?1:rl(t),zr(ll(e),t)},_n.replace=function(){var e=arguments,t=ll(e[0]);return e.length<3?t:t.replace(e[1],e[2])},_n.result=function(e,t,n){var r=-1,o=(t=lo(t,e)).length;for(o||(o=1,e=void 0);++r9007199254740991)return[];var n=4294967295,r=ln(e,4294967295);e-=4294967295;for(var o=Pt(r,t=Qo(t));++n=i)return e;var l=n-qt(r);if(l<1)return r;var s=a?uo(a,0,l).join(""):e.slice(0,l);if(void 0===o)return s+r;if(a&&(l+=s.length-l),Xa(o)){if(e.slice(l).search(o)){var u,c=s;for(o.global||(o=he(o.source,ll(re.exec(o))+"g")),o.lastIndex=0;u=o.exec(c);)var d=u.index;s=s.slice(0,void 0===d?l:d)}}else if(e.indexOf(Qr(o),l)!=l){var f=s.lastIndexOf(o);f>-1&&(s=s.slice(0,f))}return s+r},_n.unescape=function(e){return(e=ll(e))&&D.test(e)?e.replace(I,Gt):e},_n.uniqueId=function(e){var t=++ke;return ll(e)+t},_n.upperCase=Dl,_n.upperFirst=Fl,_n.each=la,_n.eachRight=sa,_n.first=Li,Kl(_n,function(){var e={};return sr(_n,(function(t,n){Oe.call(_n.prototype,n)||(e[n]=t)})),e}(),{chain:!1}),_n.VERSION="4.17.21",lt(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(e){_n[e].placeholder=_n})),lt(["drop","take"],(function(e,t){Rn.prototype[e]=function(n){n=void 0===n?1:an(rl(n),0);var r=this.__filtered__&&!t?new Rn(this):this.clone();return r.__filtered__?r.__takeCount__=ln(n,r.__takeCount__):r.__views__.push({size:ln(n,4294967295),type:e+(r.__dir__<0?"Right":"")}),r},Rn.prototype[e+"Right"]=function(t){return this.reverse()[e](t).reverse()}})),lt(["filter","map","takeWhile"],(function(e,t){var n=t+1,r=1==n||3==n;Rn.prototype[e]=function(e){var t=this.clone();return t.__iteratees__.push({iteratee:Qo(e,3),type:n}),t.__filtered__=t.__filtered__||r,t}})),lt(["head","last"],(function(e,t){var n="take"+(t?"Right":"");Rn.prototype[e]=function(){return this[n](1).value()[0]}})),lt(["initial","tail"],(function(e,t){var n="drop"+(t?"":"Right");Rn.prototype[e]=function(){return this.__filtered__?new Rn(this):this[n](1)}})),Rn.prototype.compact=function(){return this.filter(ql)},Rn.prototype.find=function(e){return this.filter(e).head()},Rn.prototype.findLast=function(e){return this.reverse().find(e)},Rn.prototype.invokeMap=Dr((function(e,t){return"function"==typeof e?new Rn(this):this.map((function(n){return br(n,e,t)}))})),Rn.prototype.reject=function(e){return this.filter(Ea(Qo(e)))},Rn.prototype.slice=function(e,t){e=rl(e);var n=this;return n.__filtered__&&(e>0||t<0)?new Rn(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),void 0!==t&&(n=(t=rl(t))<0?n.dropRight(-t):n.take(t-e)),n)},Rn.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},Rn.prototype.toArray=function(){return this.take(4294967295)},sr(Rn.prototype,(function(e,t){var n=/^(?:filter|find|map|reject)|While$/.test(t),r=/^(?:head|last)$/.test(t),o=_n[r?"take"+("last"==t?"Right":""):t],i=r||/^find/.test(t);o&&(_n.prototype[t]=function(){var t=this.__wrapped__,a=r?[1]:arguments,l=t instanceof Rn,s=a[0],u=l||Na(t),c=function(e){var t=o.apply(_n,ht([e],a));return r&&d?t[0]:t};u&&n&&"function"==typeof s&&1!=s.length&&(l=u=!1);var d=this.__chain__,f=!!this.__actions__.length,p=i&&!d,h=l&&!f;if(!i&&u){t=h?t:new Rn(this);var m=e.apply(t,a);return m.__actions__.push({func:na,args:[c],thisArg:void 0}),new Mn(m,d)}return p&&h?e.apply(this,a):(m=this.thru(c),p?r?m.value()[0]:m.value():m)})})),lt(["pop","push","shift","sort","splice","unshift"],(function(e){var t=ge[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",r=/^(?:pop|shift)$/.test(e);_n.prototype[e]=function(){var e=arguments;if(r&&!this.__chain__){var o=this.value();return t.apply(Na(o)?o:[],e)}return this[n]((function(n){return t.apply(Na(n)?n:[],e)}))}})),sr(Rn.prototype,(function(e,t){var n=_n[t];if(n){var r=n.name+"";Oe.call(yn,r)||(yn[r]=[]),yn[r].push({name:t,func:n})}})),yn[Po(void 0,2).name]=[{name:"wrapper",func:void 0}],Rn.prototype.clone=function(){var e=new Rn(this.__wrapped__);return e.__actions__=bo(this.__actions__),e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=bo(this.__iteratees__),e.__takeCount__=this.__takeCount__,e.__views__=bo(this.__views__),e},Rn.prototype.reverse=function(){if(this.__filtered__){var e=new Rn(this);e.__dir__=-1,e.__filtered__=!0}else(e=this.clone()).__dir__*=-1;return e},Rn.prototype.value=function(){var e=this.__wrapped__.value(),t=this.__dir__,n=Na(e),r=t<0,o=n?e.length:0,i=function(e,t,n){var r=-1,o=n.length;for(;++r=this.__values__.length;return{done:e,value:e?void 0:this.__values__[this.__index__++]}},_n.prototype.plant=function(e){for(var t,n=this;n instanceof Tn;){var r=_i(n);r.__index__=0,r.__values__=void 0,t?o.__wrapped__=r:t=r;var o=r;n=n.__wrapped__}return o.__wrapped__=e,t},_n.prototype.reverse=function(){var e=this.__wrapped__;if(e instanceof Rn){var t=e;return this.__actions__.length&&(t=new Rn(this)),(t=t.reverse()).__actions__.push({func:na,args:[$i],thisArg:void 0}),new Mn(t,this.__chain__)}return this.thru($i)},_n.prototype.toJSON=_n.prototype.valueOf=_n.prototype.value=function(){return no(this.__wrapped__,this.__actions__)},_n.prototype.first=_n.prototype.head,Ge&&(_n.prototype[Ge]=function(){return this}),_n}();Ye._=Kt,void 0===(o=function(){return Kt}.call(t,n,t,r))||(r.exports=o)}).call(this)}).call(this,n(114),n(84)(e))},function(e,t,n){e.exports=n(202)},function(e,t){e.exports=function(e){return e&&e.__esModule?e:{default:e}},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var r=n(240).default;function o(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(o=function(e){return e?n:t})(e)}e.exports=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=r(e)&&"function"!=typeof e)return{default:e};var n=o(t);if(n&&n.has(e))return n.get(e);var i={__proto__:null},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var s=a?Object.getOwnPropertyDescriptor(e,l):null;s&&(s.get||s.set)?Object.defineProperty(i,l,s):i[l]=e[l]}return i.default=e,n&&n.set(e,i),i},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return r.createSvgIcon}});var r=n(87)},function(e,t,n){"use strict";function r(e){return e&&e.ownerDocument||document}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(0),o="undefined"!==typeof window?r.useLayoutEffect:r.useEffect;function i(e){var t=r.useRef(e);return o((function(){t.current=e})),r.useCallback((function(){return t.current.apply(void 0,arguments)}),[])}},function(e,t,n){"use strict";n.d(t,"b",(function(){return i}));var r=n(2),o={easeInOut:"cubic-bezier(0.4, 0, 0.2, 1)",easeOut:"cubic-bezier(0.0, 0, 0.2, 1)",easeIn:"cubic-bezier(0.4, 0, 1, 1)",sharp:"cubic-bezier(0.4, 0, 0.6, 1)"},i={shortest:150,shorter:200,short:250,standard:300,complex:375,enteringScreen:225,leavingScreen:195};function a(e){return"".concat(Math.round(e),"ms")}t.a={easing:o,duration:i,create:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["all"],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.duration,l=void 0===n?i.standard:n,s=t.easing,u=void 0===s?o.easeInOut:s,c=t.delay,d=void 0===c?0:c;Object(r.a)(t,["duration","easing","delay"]);return(Array.isArray(e)?e:[e]).map((function(e){return"".concat(e," ").concat("string"===typeof l?l:a(l)," ").concat(u," ").concat("string"===typeof d?d:a(d))})).join(",")},getAutoHeightDuration:function(e){if(!e)return 0;var t=e/36;return Math.round(10*(4+15*Math.pow(t,.25)+t/5))}}},,function(e,t,n){"use strict";function r(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}n.d(t,"a",(function(){return r}))},,function(e,t,n){"use strict";n.d(t,"a",(function(){return a}));var r=n(162);var o=n(94),i=n(163);function a(e,t){return Object(r.a)(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,i,a,l=[],s=!0,u=!1;try{if(i=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;s=!1}else for(;!(s=(r=i.call(n)).done)&&(l.push(r.value),l.length!==t);s=!0);}catch(e){u=!0,o=e}finally{try{if(!s&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(u)throw o}}return l}}(e,t)||Object(o.a)(e,t)||Object(i.a)()}},,function(e,t){e.exports={Always:1,Never:2,IfAtMostOneObstacle:3,OnlyWhenNoObstacles:4}},function(e,t,n){"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";function r(e,t){"function"===typeof e?e(t):e&&(e.current=t)}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";function r(){for(var e=arguments.length,t=new Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:166;function r(){for(var r=arguments.length,o=new Array(r),i=0;i0?r:e)-u/100).concat(i,")")},between:f,only:function(e){return f(e,e)},width:function(e){return n[e]}},c)}function u(e,t,n){var o;return Object(a.a)({gutters:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return console.warn(["Material-UI: theme.mixins.gutters() is deprecated.","You can use the source of the mixin directly:","\n paddingLeft: theme.spacing(2),\n paddingRight: theme.spacing(2),\n [theme.breakpoints.up('sm')]: {\n paddingLeft: theme.spacing(3),\n paddingRight: theme.spacing(3),\n },\n "].join("\n")),Object(a.a)({paddingLeft:t(2),paddingRight:t(2)},n,Object(r.a)({},e.up("sm"),Object(a.a)({paddingLeft:t(3),paddingRight:t(3)},n[e.up("sm")])))},toolbar:(o={minHeight:56},Object(r.a)(o,"".concat(e.up("xs")," and (orientation: landscape)"),{minHeight:48}),Object(r.a)(o,e.up("sm"),{minHeight:64}),o)},n)}var c=n(173),d={black:"#000",white:"#fff"},f={50:"#fafafa",100:"#f5f5f5",200:"#eeeeee",300:"#e0e0e0",400:"#bdbdbd",500:"#9e9e9e",600:"#757575",700:"#616161",800:"#424242",900:"#212121",A100:"#d5d5d5",A200:"#aaaaaa",A400:"#303030",A700:"#616161"},p={50:"#e8eaf6",100:"#c5cae9",200:"#9fa8da",300:"#7986cb",400:"#5c6bc0",500:"#3f51b5",600:"#3949ab",700:"#303f9f",800:"#283593",900:"#1a237e",A100:"#8c9eff",A200:"#536dfe",A400:"#3d5afe",A700:"#304ffe"},h={50:"#fce4ec",100:"#f8bbd0",200:"#f48fb1",300:"#f06292",400:"#ec407a",500:"#e91e63",600:"#d81b60",700:"#c2185b",800:"#ad1457",900:"#880e4f",A100:"#ff80ab",A200:"#ff4081",A400:"#f50057",A700:"#c51162"},m={50:"#ffebee",100:"#ffcdd2",200:"#ef9a9a",300:"#e57373",400:"#ef5350",500:"#f44336",600:"#e53935",700:"#d32f2f",800:"#c62828",900:"#b71c1c",A100:"#ff8a80",A200:"#ff5252",A400:"#ff1744",A700:"#d50000"},v={50:"#fff3e0",100:"#ffe0b2",200:"#ffcc80",300:"#ffb74d",400:"#ffa726",500:"#ff9800",600:"#fb8c00",700:"#f57c00",800:"#ef6c00",900:"#e65100",A100:"#ffd180",A200:"#ffab40",A400:"#ff9100",A700:"#ff6d00"},g={50:"#e3f2fd",100:"#bbdefb",200:"#90caf9",300:"#64b5f6",400:"#42a5f5",500:"#2196f3",600:"#1e88e5",700:"#1976d2",800:"#1565c0",900:"#0d47a1",A100:"#82b1ff",A200:"#448aff",A400:"#2979ff",A700:"#2962ff"},b={50:"#e8f5e9",100:"#c8e6c9",200:"#a5d6a7",300:"#81c784",400:"#66bb6a",500:"#4caf50",600:"#43a047",700:"#388e3c",800:"#2e7d32",900:"#1b5e20",A100:"#b9f6ca",A200:"#69f0ae",A400:"#00e676",A700:"#00c853"},y=n(8),x={text:{primary:"rgba(0, 0, 0, 0.87)",secondary:"rgba(0, 0, 0, 0.54)",disabled:"rgba(0, 0, 0, 0.38)",hint:"rgba(0, 0, 0, 0.38)"},divider:"rgba(0, 0, 0, 0.12)",background:{paper:d.white,default:f[50]},action:{active:"rgba(0, 0, 0, 0.54)",hover:"rgba(0, 0, 0, 0.04)",hoverOpacity:.04,selected:"rgba(0, 0, 0, 0.08)",selectedOpacity:.08,disabled:"rgba(0, 0, 0, 0.26)",disabledBackground:"rgba(0, 0, 0, 0.12)",disabledOpacity:.38,focus:"rgba(0, 0, 0, 0.12)",focusOpacity:.12,activatedOpacity:.12}},w={text:{primary:d.white,secondary:"rgba(255, 255, 255, 0.7)",disabled:"rgba(255, 255, 255, 0.5)",hint:"rgba(255, 255, 255, 0.5)",icon:"rgba(255, 255, 255, 0.5)"},divider:"rgba(255, 255, 255, 0.12)",background:{paper:f[800],default:"#303030"},action:{active:d.white,hover:"rgba(255, 255, 255, 0.08)",hoverOpacity:.08,selected:"rgba(255, 255, 255, 0.16)",selectedOpacity:.16,disabled:"rgba(255, 255, 255, 0.3)",disabledBackground:"rgba(255, 255, 255, 0.12)",disabledOpacity:.38,focus:"rgba(255, 255, 255, 0.12)",focusOpacity:.12,activatedOpacity:.24}};function O(e,t,n,r){var o=r.light||r,i=r.dark||1.5*r;e[t]||(e.hasOwnProperty(n)?e[t]=e[n]:"light"===t?e.light=Object(y.e)(e.main,o):"dark"===t&&(e.dark=Object(y.b)(e.main,i)))}function k(e){var t=e.primary,n=void 0===t?{light:p[300],main:p[500],dark:p[700]}:t,r=e.secondary,l=void 0===r?{light:h.A200,main:h.A400,dark:h.A700}:r,s=e.error,u=void 0===s?{light:m[300],main:m[500],dark:m[700]}:s,k=e.warning,E=void 0===k?{light:v[300],main:v[500],dark:v[700]}:k,S=e.info,j=void 0===S?{light:g[300],main:g[500],dark:g[700]}:S,C=e.success,_=void 0===C?{light:b[300],main:b[500],dark:b[700]}:C,P=e.type,T=void 0===P?"light":P,M=e.contrastThreshold,R=void 0===M?3:M,N=e.tonalOffset,A=void 0===N?.2:N,L=Object(o.a)(e,["primary","secondary","error","warning","info","success","type","contrastThreshold","tonalOffset"]);function I(e){return Object(y.d)(e,w.text.primary)>=R?w.text.primary:x.text.primary}var z=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:500,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:300,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:700;if(!(e=Object(a.a)({},e)).main&&e[t]&&(e.main=e[t]),!e.main)throw new Error(Object(c.a)(4,t));if("string"!==typeof e.main)throw new Error(Object(c.a)(5,JSON.stringify(e.main)));return O(e,"light",n,A),O(e,"dark",r,A),e.contrastText||(e.contrastText=I(e.main)),e},D={dark:w,light:x};return Object(i.a)(Object(a.a)({common:d,type:T,primary:z(n),secondary:z(l,"A400","A200","A700"),error:z(u),warning:z(E),info:z(j),success:z(_),grey:f,contrastThreshold:R,getContrastText:I,augmentColor:z,tonalOffset:A},D[T]),L)}function E(e){return Math.round(1e5*e)/1e5}function S(e){return E(e)}var j={textTransform:"uppercase"};function C(e,t){var n="function"===typeof t?t(e):t,r=n.fontFamily,l=void 0===r?'"Roboto", "Helvetica", "Arial", sans-serif':r,s=n.fontSize,u=void 0===s?14:s,c=n.fontWeightLight,d=void 0===c?300:c,f=n.fontWeightRegular,p=void 0===f?400:f,h=n.fontWeightMedium,m=void 0===h?500:h,v=n.fontWeightBold,g=void 0===v?700:v,b=n.htmlFontSize,y=void 0===b?16:b,x=n.allVariants,w=n.pxToRem,O=Object(o.a)(n,["fontFamily","fontSize","fontWeightLight","fontWeightRegular","fontWeightMedium","fontWeightBold","htmlFontSize","allVariants","pxToRem"]);var k=u/14,C=w||function(e){return"".concat(e/y*k,"rem")},_=function(e,t,n,r,o){return Object(a.a)({fontFamily:l,fontWeight:e,fontSize:C(t),lineHeight:n},'"Roboto", "Helvetica", "Arial", sans-serif'===l?{letterSpacing:"".concat(E(r/t),"em")}:{},o,x)},P={h1:_(d,96,1.167,-1.5),h2:_(d,60,1.2,-.5),h3:_(p,48,1.167,0),h4:_(p,34,1.235,.25),h5:_(p,24,1.334,0),h6:_(m,20,1.6,.15),subtitle1:_(p,16,1.75,.15),subtitle2:_(m,14,1.57,.1),body1:_(p,16,1.5,.15),body2:_(p,14,1.43,.15),button:_(m,14,1.75,.4,j),caption:_(p,12,1.66,.4),overline:_(p,12,2.66,1,j)};return Object(i.a)(Object(a.a)({htmlFontSize:y,pxToRem:C,round:S,fontFamily:l,fontSize:u,fontWeightLight:d,fontWeightRegular:p,fontWeightMedium:m,fontWeightBold:g},P),O,{clone:!1})}function _(){return["".concat(arguments.length<=0?void 0:arguments[0],"px ").concat(arguments.length<=1?void 0:arguments[1],"px ").concat(arguments.length<=2?void 0:arguments[2],"px ").concat(arguments.length<=3?void 0:arguments[3],"px rgba(0,0,0,").concat(.2,")"),"".concat(arguments.length<=4?void 0:arguments[4],"px ").concat(arguments.length<=5?void 0:arguments[5],"px ").concat(arguments.length<=6?void 0:arguments[6],"px ").concat(arguments.length<=7?void 0:arguments[7],"px rgba(0,0,0,").concat(.14,")"),"".concat(arguments.length<=8?void 0:arguments[8],"px ").concat(arguments.length<=9?void 0:arguments[9],"px ").concat(arguments.length<=10?void 0:arguments[10],"px ").concat(arguments.length<=11?void 0:arguments[11],"px rgba(0,0,0,").concat(.12,")")].join(",")}var P=["none",_(0,2,1,-1,0,1,1,0,0,1,3,0),_(0,3,1,-2,0,2,2,0,0,1,5,0),_(0,3,3,-2,0,3,4,0,0,1,8,0),_(0,2,4,-1,0,4,5,0,0,1,10,0),_(0,3,5,-1,0,5,8,0,0,1,14,0),_(0,3,5,-1,0,6,10,0,0,1,18,0),_(0,4,5,-2,0,7,10,1,0,2,16,1),_(0,5,5,-3,0,8,10,1,0,3,14,2),_(0,5,6,-3,0,9,12,1,0,3,16,2),_(0,6,6,-3,0,10,14,1,0,4,18,3),_(0,6,7,-4,0,11,15,1,0,4,20,3),_(0,7,8,-4,0,12,17,2,0,5,22,4),_(0,7,8,-4,0,13,19,2,0,5,24,4),_(0,7,9,-4,0,14,21,2,0,5,26,4),_(0,8,9,-5,0,15,22,2,0,6,28,5),_(0,8,10,-5,0,16,24,2,0,6,30,5),_(0,8,11,-5,0,17,26,2,0,6,32,5),_(0,9,11,-5,0,18,28,2,0,7,34,6),_(0,9,12,-6,0,19,29,2,0,7,36,6),_(0,10,13,-6,0,20,31,3,0,8,38,7),_(0,10,13,-6,0,21,33,3,0,8,40,7),_(0,10,14,-6,0,22,35,3,0,8,42,7),_(0,11,14,-7,0,23,36,3,0,9,44,8),_(0,11,15,-7,0,24,38,3,0,9,46,8)],T={borderRadius:4},M=n(25),R=(n(53),n(28));var N=function(e,t){return t?Object(i.a)(e,t,{clone:!1}):e},A={xs:0,sm:600,md:960,lg:1280,xl:1920},L={keys:["xs","sm","md","lg","xl"],up:function(e){return"@media (min-width:".concat(A[e],"px)")}};var I={m:"margin",p:"padding"},z={t:"Top",r:"Right",b:"Bottom",l:"Left",x:["Left","Right"],y:["Top","Bottom"]},D={marginX:"mx",marginY:"my",paddingX:"px",paddingY:"py"},F=function(e){var t={};return function(n){return void 0===t[n]&&(t[n]=e(n)),t[n]}}((function(e){if(e.length>2){if(!D[e])return[e];e=D[e]}var t=e.split(""),n=Object(M.a)(t,2),r=n[0],o=n[1],i=I[r],a=z[o]||"";return Array.isArray(a)?a.map((function(e){return i+e})):[i+a]})),W=["m","mt","mr","mb","ml","mx","my","p","pt","pr","pb","pl","px","py","margin","marginTop","marginRight","marginBottom","marginLeft","marginX","marginY","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","paddingX","paddingY"];function B(e){var t=e.spacing||8;return"number"===typeof t?function(e){return t*e}:Array.isArray(t)?function(e){return t[e]}:"function"===typeof t?t:function(){}}function U(e,t){return function(n){return e.reduce((function(e,r){return e[r]=function(e,t){if("string"===typeof t||null==t)return t;var n=e(Math.abs(t));return t>=0?n:"number"===typeof n?-n:"-".concat(n)}(t,n),e}),{})}}function $(e){var t=B(e.theme);return Object.keys(e).map((function(n){if(-1===W.indexOf(n))return null;var r=U(F(n),t),o=e[n];return function(e,t,n){if(Array.isArray(t)){var r=e.theme.breakpoints||L;return t.reduce((function(e,o,i){return e[r.up(r.keys[i])]=n(t[i]),e}),{})}if("object"===Object(R.a)(t)){var o=e.theme.breakpoints||L;return Object.keys(t).reduce((function(e,r){return e[o.up(r)]=n(t[r]),e}),{})}return n(t)}(e,o,r)})).reduce(N,{})}$.propTypes={},$.filterProps=W;function V(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:8;if(e.mui)return e;var t=B({spacing:e}),n=function(){for(var e=arguments.length,n=new Array(e),r=0;r0&&void 0!==arguments[0]?arguments[0]:{},t=e.breakpoints,n=void 0===t?{}:t,r=e.mixins,a=void 0===r?{}:r,l=e.palette,c=void 0===l?{}:l,d=e.spacing,f=e.typography,p=void 0===f?{}:f,h=Object(o.a)(e,["breakpoints","mixins","palette","spacing","typography"]),m=k(c),v=s(n),g=V(d),b=Object(i.a)({breakpoints:v,direction:"ltr",mixins:u(v,g,a),overrides:{},palette:m,props:{},shadows:P,typography:C(m,p),spacing:g,shape:T,transitions:H.a,zIndex:q.a},h),y=arguments.length,x=new Array(y>1?y-1:0),w=1;w-l&&(s-=l,e+=o),u1&&void 0!==arguments[1]&&arguments[1];return e&&(r(e.value)&&""!==e.value||t&&r(e.defaultValue)&&""!==e.defaultValue)}function i(e){return e.startAdornment}n.d(t,"b",(function(){return o})),n.d(t,"a",(function(){return i}))},function(e,t,n){var r=n(241),o=n(242);e.exports=function(e,t,n){var i=t&&n||0;"string"==typeof e&&(t="binary"===e?new Array(16):null,e=null);var a=(e=e||{}).random||(e.rng||r)();if(a[6]=15&a[6]|64,a[8]=63&a[8]|128,t)for(var l=0;l<16;++l)t[i+l]=a[l];return t||o(a)}},,,,,function(e,t,n){var r=n(263),o=n(266);e.exports=function(e,t){var n=o(e,t);return r(n)?n:void 0}},function(e,t,n){"use strict";e.exports=n(201)},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(19);function o(e){return Object(r.a)(e).defaultView||window}},function(e,t,n){"use strict";var r=n(16),o=n(17);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=o(n(0)),a=(0,r(n(18)).default)(i.createElement("path",{d:"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"}),"Check");t.default=a},function(e,t,n){"use strict";var r=n(16),o=n(17);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=o(n(0)),a=(0,r(n(18)).default)(i.createElement("path",{d:"M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"}),"AddCircleOutline");t.default=a},function(e,t,n){"use strict";var r=n(0),o=n.n(r);t.a=o.a.createContext(null)},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(109);function o(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};r.forEach(["delete","get","head"],(function(e){l.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){l.headers[e]=r.merge(i)})),e.exports=l}).call(this,n(205))},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(r){"object"===typeof window&&(n=window)}e.exports=n},function(e,t){e.exports=function(e,t,n){this.x=e,this.y=t,this.walkable=void 0===n||n}},function(e,t,n){var r=n(85),o=n(57),i=n(76),a=n(27);function l(e){e=e||{},this.allowDiagonal=e.allowDiagonal,this.dontCrossCorners=e.dontCrossCorners,this.heuristic=e.heuristic||i.manhattan,this.weight=e.weight||1,this.diagonalMovement=e.diagonalMovement,this.diagonalMovement||(this.allowDiagonal?this.dontCrossCorners?this.diagonalMovement=a.OnlyWhenNoObstacles:this.diagonalMovement=a.IfAtMostOneObstacle:this.diagonalMovement=a.Never),this.diagonalMovement===a.Never?this.heuristic=e.heuristic||i.manhattan:this.heuristic=e.heuristic||i.octile}l.prototype.findPath=function(e,t,n,i,a){var l,s,u,c,d,f,p,h,m=new r((function(e,t){return e.f-t.f})),v=a.getNodeAt(e,t),g=a.getNodeAt(n,i),b=this.heuristic,y=this.diagonalMovement,x=this.weight,w=Math.abs,O=Math.SQRT2;for(v.g=0,v.f=0,m.push(v),v.opened=!0;!m.empty();){if((l=m.pop()).closed=!0,l===g)return o.backtrace(g);for(c=0,d=(s=a.getNeighbors(l,y)).length;c-1&&e%1==0&&e<=9007199254740991}},function(e,t,n){var r=n(148),o=n(125);e.exports=function(e){return null!=e&&o(e.length)&&!r(e)}},function(e,t,n){var r=n(43),o=n(128),i=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,a=/^\w*$/;e.exports=function(e,t){if(r(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!o(e))||(a.test(e)||!i.test(e)||null!=t&&e in Object(t))}},function(e,t,n){var r=n(78),o=n(79);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(0);function o(e){var t=r.useState(e),n=t[0],o=t[1],i=e||n;return r.useEffect((function(){null==n&&o("mui-".concat(Math.round(1e5*Math.random())))}),[n]),i}},function(e,t,n){"use strict";var r=n(1),o=n(2),i=n(0),a=n.n(i),l=n(13),s=n(3),u=n(9),c=n(20),d=n(4),f=n(93),p=n(53),h=n(23),m=n(81),v=n(59),g=n(71);function b(e,t){var n=Object.create(null);return e&&i.Children.map(e,(function(e){return e})).forEach((function(e){n[e.key]=function(e){return t&&Object(i.isValidElement)(e)?t(e):e}(e)})),n}function y(e,t,n){return null!=n[t]?n[t]:e.props[t]}function x(e,t,n){var r=b(e.children),o=function(e,t){function n(n){return n in t?t[n]:e[n]}e=e||{},t=t||{};var r,o=Object.create(null),i=[];for(var a in e)a in t?i.length&&(o[a]=i,i=[]):i.push(a);var l={};for(var s in t){if(o[s])for(r=0;r0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0,r=t.pulsate,o=void 0!==r&&r,i=t.center,l=void 0===i?a||t.pulsate:i,s=t.fakeElement,u=void 0!==s&&s;if("mousedown"===e.type&&g.current)g.current=!1;else{"touchstart"===e.type&&(g.current=!0);var c,d,f,p=u?null:x.current,h=p?p.getBoundingClientRect():{width:0,height:0,left:0,top:0};if(l||0===e.clientX&&0===e.clientY||!e.clientX&&!e.touches)c=Math.round(h.width/2),d=Math.round(h.height/2);else{var m=e.touches?e.touches[0]:e,v=m.clientX,O=m.clientY;c=Math.round(v-h.left),d=Math.round(O-h.top)}if(l)(f=Math.sqrt((2*Math.pow(h.width,2)+Math.pow(h.height,2))/3))%2===0&&(f+=1);else{var k=2*Math.max(Math.abs((p?p.clientWidth:0)-c),c)+2,E=2*Math.max(Math.abs((p?p.clientHeight:0)-d),d)+2;f=Math.sqrt(Math.pow(k,2)+Math.pow(E,2))}e.touches?null===y.current&&(y.current=function(){w({pulsate:o,rippleX:c,rippleY:d,rippleSize:f,cb:n})},b.current=setTimeout((function(){y.current&&(y.current(),y.current=null)}),80)):w({pulsate:o,rippleX:c,rippleY:d,rippleSize:f,cb:n})}}),[a,w]),E=i.useCallback((function(){O({},{pulsate:!0})}),[O]),j=i.useCallback((function(e,t){if(clearTimeout(b.current),"touchend"===e.type&&y.current)return e.persist(),y.current(),y.current=null,void(b.current=setTimeout((function(){j(e,t)})));y.current=null,h((function(e){return e.length>0?e.slice(1):e})),v.current=t}),[]);return i.useImperativeHandle(t,(function(){return{pulsate:E,start:O,stop:j}}),[E,O,j]),i.createElement("span",Object(r.a)({className:Object(s.a)(l.root,u),ref:x},c),i.createElement(k,{component:null,exit:!0},f))})),C=Object(d.a)((function(e){return{root:{overflow:"hidden",pointerEvents:"none",position:"absolute",zIndex:0,top:0,right:0,bottom:0,left:0,borderRadius:"inherit"},ripple:{opacity:0,position:"absolute"},rippleVisible:{opacity:.3,transform:"scale(1)",animation:"$enter ".concat(550,"ms ").concat(e.transitions.easing.easeInOut)},ripplePulsate:{animationDuration:"".concat(e.transitions.duration.shorter,"ms")},child:{opacity:1,display:"block",width:"100%",height:"100%",borderRadius:"50%",backgroundColor:"currentColor"},childLeaving:{opacity:0,animation:"$exit ".concat(550,"ms ").concat(e.transitions.easing.easeInOut)},childPulsate:{position:"absolute",left:0,top:0,animation:"$pulsate 2500ms ".concat(e.transitions.easing.easeInOut," 200ms infinite")},"@keyframes enter":{"0%":{transform:"scale(0)",opacity:.1},"100%":{transform:"scale(1)",opacity:.3}},"@keyframes exit":{"0%":{opacity:1},"100%":{opacity:0}},"@keyframes pulsate":{"0%":{transform:"scale(1)"},"50%":{transform:"scale(0.92)"},"100%":{transform:"scale(1)"}}}}),{flip:!1,name:"MuiTouchRipple"})(i.memo(j)),_=i.forwardRef((function(e,t){var n=e.action,a=e.buttonRef,d=e.centerRipple,p=void 0!==d&&d,h=e.children,m=e.classes,v=e.className,g=e.component,b=void 0===g?"button":g,y=e.disabled,x=void 0!==y&&y,w=e.disableRipple,O=void 0!==w&&w,k=e.disableTouchRipple,E=void 0!==k&&k,S=e.focusRipple,j=void 0!==S&&S,_=e.focusVisibleClassName,P=e.onBlur,T=e.onClick,M=e.onFocus,R=e.onFocusVisible,N=e.onKeyDown,A=e.onKeyUp,L=e.onMouseDown,I=e.onMouseLeave,z=e.onMouseUp,D=e.onTouchEnd,F=e.onTouchMove,W=e.onTouchStart,B=e.onDragLeave,U=e.tabIndex,$=void 0===U?0:U,V=e.TouchRippleProps,H=e.type,q=void 0===H?"button":H,Y=Object(o.a)(e,["action","buttonRef","centerRipple","children","classes","className","component","disabled","disableRipple","disableTouchRipple","focusRipple","focusVisibleClassName","onBlur","onClick","onFocus","onFocusVisible","onKeyDown","onKeyUp","onMouseDown","onMouseLeave","onMouseUp","onTouchEnd","onTouchMove","onTouchStart","onDragLeave","tabIndex","TouchRippleProps","type"]),X=i.useRef(null);var G=i.useRef(null),K=i.useState(!1),Q=K[0],Z=K[1];x&&Q&&Z(!1);var J=Object(f.a)(),ee=J.isFocusVisible,te=J.onBlurVisible,ne=J.ref;function re(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:E;return Object(c.a)((function(r){return t&&t(r),!n&&G.current&&G.current[e](r),!0}))}i.useImperativeHandle(n,(function(){return{focusVisible:function(){Z(!0),X.current.focus()}}}),[]),i.useEffect((function(){Q&&j&&!O&&G.current.pulsate()}),[O,j,Q]);var oe=re("start",L),ie=re("stop",B),ae=re("stop",z),le=re("stop",(function(e){Q&&e.preventDefault(),I&&I(e)})),se=re("start",W),ue=re("stop",D),ce=re("stop",F),de=re("stop",(function(e){Q&&(te(e),Z(!1)),P&&P(e)}),!1),fe=Object(c.a)((function(e){X.current||(X.current=e.currentTarget),ee(e)&&(Z(!0),R&&R(e)),M&&M(e)})),pe=function(){var e=l.findDOMNode(X.current);return b&&"button"!==b&&!("A"===e.tagName&&e.href)},he=i.useRef(!1),me=Object(c.a)((function(e){j&&!he.current&&Q&&G.current&&" "===e.key&&(he.current=!0,e.persist(),G.current.stop(e,(function(){G.current.start(e)}))),e.target===e.currentTarget&&pe()&&" "===e.key&&e.preventDefault(),N&&N(e),e.target===e.currentTarget&&pe()&&"Enter"===e.key&&!x&&(e.preventDefault(),T&&T(e))})),ve=Object(c.a)((function(e){j&&" "===e.key&&G.current&&Q&&!e.defaultPrevented&&(he.current=!1,e.persist(),G.current.stop(e,(function(){G.current.pulsate(e)}))),A&&A(e),T&&e.target===e.currentTarget&&pe()&&" "===e.key&&!e.defaultPrevented&&T(e)})),ge=b;"button"===ge&&Y.href&&(ge="a");var be={};"button"===ge?(be.type=q,be.disabled=x):("a"===ge&&Y.href||(be.role="button"),be["aria-disabled"]=x);var ye=Object(u.a)(a,t),xe=Object(u.a)(ne,X),we=Object(u.a)(ye,xe),Oe=i.useState(!1),ke=Oe[0],Ee=Oe[1];i.useEffect((function(){Ee(!0)}),[]);var Se=ke&&!O&&!x;return i.createElement(ge,Object(r.a)({className:Object(s.a)(m.root,v,Q&&[m.focusVisible,_],x&&m.disabled),onBlur:de,onClick:T,onFocus:fe,onKeyDown:me,onKeyUp:ve,onMouseDown:oe,onMouseLeave:le,onMouseUp:ae,onDragLeave:ie,onTouchEnd:ue,onTouchMove:ce,onTouchStart:se,ref:we,tabIndex:x?-1:$},be,Y),h,Se?i.createElement(C,Object(r.a)({ref:G,center:p},V)):null)}));t.a=Object(d.a)({root:{display:"inline-flex",alignItems:"center",justifyContent:"center",position:"relative",WebkitTapHighlightColor:"transparent",backgroundColor:"transparent",outline:0,border:0,margin:0,borderRadius:0,padding:0,cursor:"pointer",userSelect:"none",verticalAlign:"middle","-moz-appearance":"none","-webkit-appearance":"none",textDecoration:"none",color:"inherit","&::-moz-focus-inner":{borderStyle:"none"},"&$disabled":{pointerEvents:"none",cursor:"default"},"@media print":{colorAdjust:"exact"}},disabled:{},focusVisible:{}},{name:"MuiButtonBase"})(_)},,,,,,,function(e,t,n){"use strict";var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(o){return!1}}()?Object.assign:function(e,t){for(var n,l,s=a(e),u=1;uc))return!1;var f=s.get(e),p=s.get(t);if(f&&p)return f==t&&p==e;var h=-1,m=!0,v=2&n?new r:void 0;for(s.set(e,t),s.set(t,e);++h-1&&e%1==0&&e=0)return 1;return 0}();var o=n&&window.Promise?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then((function(){t=!1,e()})))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout((function(){t=!1,e()}),r))}};function i(e){return e&&"[object Function]"==={}.toString.call(e)}function a(e,t){if(1!==e.nodeType)return[];var n=e.ownerDocument.defaultView.getComputedStyle(e,null);return t?n[t]:n}function l(e){return"HTML"===e.nodeName?e:e.parentNode||e.host}function s(e){if(!e)return document.body;switch(e.nodeName){case"HTML":case"BODY":return e.ownerDocument.body;case"#document":return e.body}var t=a(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/(auto|scroll|overlay)/.test(n+o+r)?e:s(l(e))}function u(e){return e&&e.referenceNode?e.referenceNode:e}var c=n&&!(!window.MSInputMethodContext||!document.documentMode),d=n&&/MSIE 10/.test(navigator.userAgent);function f(e){return 11===e?c:10===e?d:c||d}function p(e){if(!e)return document.documentElement;for(var t=f(10)?document.body:null,n=e.offsetParent||null;n===t&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var r=n&&n.nodeName;return r&&"BODY"!==r&&"HTML"!==r?-1!==["TH","TD","TABLE"].indexOf(n.nodeName)&&"static"===a(n,"position")?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function h(e){return null!==e.parentNode?h(e.parentNode):e}function m(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var n=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,r=n?e:t,o=n?t:e,i=document.createRange();i.setStart(r,0),i.setEnd(o,0);var a=i.commonAncestorContainer;if(e!==a&&t!==a||r.contains(o))return function(e){var t=e.nodeName;return"BODY"!==t&&("HTML"===t||p(e.firstElementChild)===e)}(a)?a:p(a);var l=h(e);return l.host?m(l.host,t):m(e,h(t).host)}function v(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top",n="top"===t?"scrollTop":"scrollLeft",r=e.nodeName;if("BODY"===r||"HTML"===r){var o=e.ownerDocument.documentElement,i=e.ownerDocument.scrollingElement||o;return i[n]}return e[n]}function g(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=v(t,"top"),o=v(t,"left"),i=n?-1:1;return e.top+=r*i,e.bottom+=r*i,e.left+=o*i,e.right+=o*i,e}function b(e,t){var n="x"===t?"Left":"Top",r="Left"===n?"Right":"Bottom";return parseFloat(e["border"+n+"Width"])+parseFloat(e["border"+r+"Width"])}function y(e,t,n,r){return Math.max(t["offset"+e],t["scroll"+e],n["client"+e],n["offset"+e],n["scroll"+e],f(10)?parseInt(n["offset"+e])+parseInt(r["margin"+("Height"===e?"Top":"Left")])+parseInt(r["margin"+("Height"===e?"Bottom":"Right")]):0)}function x(e){var t=e.body,n=e.documentElement,r=f(10)&&getComputedStyle(n);return{height:y("Height",t,n,r),width:y("Width",t,n,r)}}var w=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},O=function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]&&arguments[2],r=f(10),o="HTML"===t.nodeName,i=j(e),l=j(t),u=s(e),c=a(t),d=parseFloat(c.borderTopWidth),p=parseFloat(c.borderLeftWidth);n&&o&&(l.top=Math.max(l.top,0),l.left=Math.max(l.left,0));var h=S({top:i.top-l.top-d,left:i.left-l.left-p,width:i.width,height:i.height});if(h.marginTop=0,h.marginLeft=0,!r&&o){var m=parseFloat(c.marginTop),v=parseFloat(c.marginLeft);h.top-=d-m,h.bottom-=d-m,h.left-=p-v,h.right-=p-v,h.marginTop=m,h.marginLeft=v}return(r&&!n?t.contains(u):t===u&&"BODY"!==u.nodeName)&&(h=g(h,t)),h}function _(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=e.ownerDocument.documentElement,r=C(e,n),o=Math.max(n.clientWidth,window.innerWidth||0),i=Math.max(n.clientHeight,window.innerHeight||0),a=t?0:v(n),l=t?0:v(n,"left"),s={top:a-r.top+r.marginTop,left:l-r.left+r.marginLeft,width:o,height:i};return S(s)}function P(e){var t=e.nodeName;if("BODY"===t||"HTML"===t)return!1;if("fixed"===a(e,"position"))return!0;var n=l(e);return!!n&&P(n)}function T(e){if(!e||!e.parentElement||f())return document.documentElement;for(var t=e.parentElement;t&&"none"===a(t,"transform");)t=t.parentElement;return t||document.documentElement}function M(e,t,n,r){var o=arguments.length>4&&void 0!==arguments[4]&&arguments[4],i={top:0,left:0},a=o?T(e):m(e,u(t));if("viewport"===r)i=_(a,o);else{var c=void 0;"scrollParent"===r?"BODY"===(c=s(l(t))).nodeName&&(c=e.ownerDocument.documentElement):c="window"===r?e.ownerDocument.documentElement:r;var d=C(c,a,o);if("HTML"!==c.nodeName||P(a))i=d;else{var f=x(e.ownerDocument),p=f.height,h=f.width;i.top+=d.top-d.marginTop,i.bottom=p+d.top,i.left+=d.left-d.marginLeft,i.right=h+d.left}}var v="number"===typeof(n=n||0);return i.left+=v?n:n.left||0,i.top+=v?n:n.top||0,i.right-=v?n:n.right||0,i.bottom-=v?n:n.bottom||0,i}function R(e){return e.width*e.height}function N(e,t,n,r,o){var i=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf("auto"))return e;var a=M(n,r,i,o),l={top:{width:a.width,height:t.top-a.top},right:{width:a.right-t.right,height:a.height},bottom:{width:a.width,height:a.bottom-t.bottom},left:{width:t.left-a.left,height:a.height}},s=Object.keys(l).map((function(e){return E({key:e},l[e],{area:R(l[e])})})).sort((function(e,t){return t.area-e.area})),u=s.filter((function(e){var t=e.width,r=e.height;return t>=n.clientWidth&&r>=n.clientHeight})),c=u.length>0?u[0].key:s[0].key,d=e.split("-")[1];return c+(d?"-"+d:"")}function A(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=r?T(t):m(t,u(n));return C(n,o,r)}function L(e){var t=e.ownerDocument.defaultView.getComputedStyle(e),n=parseFloat(t.marginTop||0)+parseFloat(t.marginBottom||0),r=parseFloat(t.marginLeft||0)+parseFloat(t.marginRight||0);return{width:e.offsetWidth+r,height:e.offsetHeight+n}}function I(e){var t={left:"right",right:"left",bottom:"top",top:"bottom"};return e.replace(/left|right|bottom|top/g,(function(e){return t[e]}))}function z(e,t,n){n=n.split("-")[0];var r=L(e),o={width:r.width,height:r.height},i=-1!==["right","left"].indexOf(n),a=i?"top":"left",l=i?"left":"top",s=i?"height":"width",u=i?"width":"height";return o[a]=t[a]+t[s]/2-r[s]/2,o[l]=n===l?t[l]-r[u]:t[I(l)],o}function D(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function F(e,t,n){return(void 0===n?e:e.slice(0,function(e,t,n){if(Array.prototype.findIndex)return e.findIndex((function(e){return e[t]===n}));var r=D(e,(function(e){return e[t]===n}));return e.indexOf(r)}(e,"name",n))).forEach((function(e){e.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=e.function||e.fn;e.enabled&&i(n)&&(t.offsets.popper=S(t.offsets.popper),t.offsets.reference=S(t.offsets.reference),t=n(t,e))})),t}function W(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=A(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=N(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=z(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",e=F(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function B(e,t){return e.some((function(e){var n=e.name;return e.enabled&&n===t}))}function U(e){for(var t=[!1,"ms","Webkit","Moz","O"],n=e.charAt(0).toUpperCase()+e.slice(1),r=0;r1&&void 0!==arguments[1]&&arguments[1],n=J.indexOf(e),r=J.slice(n+1).concat(J.slice(0,n));return t?r.reverse():r}var te="flip",ne="clockwise",re="counterclockwise";function oe(e,t,n,r){var o=[0,0],i=-1!==["right","left"].indexOf(r),a=e.split(/(\+|\-)/).map((function(e){return e.trim()})),l=a.indexOf(D(a,(function(e){return-1!==e.search(/,|\s/)})));a[l]&&-1===a[l].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var s=/\s*,\s*|\s+/,u=-1!==l?[a.slice(0,l).concat([a[l].split(s)[0]]),[a[l].split(s)[1]].concat(a.slice(l+1))]:[a];return(u=u.map((function(e,r){var o=(1===r?!i:i)?"height":"width",a=!1;return e.reduce((function(e,t){return""===e[e.length-1]&&-1!==["+","-"].indexOf(t)?(e[e.length-1]=t,a=!0,e):a?(e[e.length-1]+=t,a=!1,e):e.concat(t)}),[]).map((function(e){return function(e,t,n,r){var o=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),i=+o[1],a=o[2];if(!i)return e;if(0===a.indexOf("%")){var l=void 0;switch(a){case"%p":l=n;break;case"%":case"%r":default:l=r}return S(l)[t]/100*i}if("vh"===a||"vw"===a){return("vh"===a?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*i}return i}(e,o,t,n)}))}))).forEach((function(e,t){e.forEach((function(n,r){X(n)&&(o[t]+=n*("-"===e[r-1]?-1:1))}))})),o}var ie={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,n=t.split("-")[0],r=t.split("-")[1];if(r){var o=e.offsets,i=o.reference,a=o.popper,l=-1!==["bottom","top"].indexOf(n),s=l?"left":"top",u=l?"width":"height",c={start:k({},s,i[s]),end:k({},s,i[s]+i[u]-a[u])};e.offsets.popper=E({},a,c[r])}return e}},offset:{order:200,enabled:!0,fn:function(e,t){var n=t.offset,r=e.placement,o=e.offsets,i=o.popper,a=o.reference,l=r.split("-")[0],s=void 0;return s=X(+n)?[+n,0]:oe(n,i,a,l),"left"===l?(i.top+=s[0],i.left-=s[1]):"right"===l?(i.top+=s[0],i.left+=s[1]):"top"===l?(i.left+=s[0],i.top-=s[1]):"bottom"===l&&(i.left+=s[0],i.top+=s[1]),e.popper=i,e},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,t){var n=t.boundariesElement||p(e.instance.popper);e.instance.reference===n&&(n=p(n));var r=U("transform"),o=e.instance.popper.style,i=o.top,a=o.left,l=o[r];o.top="",o.left="",o[r]="";var s=M(e.instance.popper,e.instance.reference,t.padding,n,e.positionFixed);o.top=i,o.left=a,o[r]=l,t.boundaries=s;var u=t.priority,c=e.offsets.popper,d={primary:function(e){var n=c[e];return c[e]s[e]&&!t.escapeWithReference&&(r=Math.min(c[n],s[e]-("right"===e?c.width:c.height))),k({},n,r)}};return u.forEach((function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";c=E({},c,d[t](e))})),e.offsets.popper=c,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,r=t.reference,o=e.placement.split("-")[0],i=Math.floor,a=-1!==["top","bottom"].indexOf(o),l=a?"right":"bottom",s=a?"left":"top",u=a?"width":"height";return n[l]i(r[l])&&(e.offsets.popper[s]=i(r[l])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!Q(e.instance.modifiers,"arrow","keepTogether"))return e;var r=t.element;if("string"===typeof r){if(!(r=e.instance.popper.querySelector(r)))return e}else if(!e.instance.popper.contains(r))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var o=e.placement.split("-")[0],i=e.offsets,l=i.popper,s=i.reference,u=-1!==["left","right"].indexOf(o),c=u?"height":"width",d=u?"Top":"Left",f=d.toLowerCase(),p=u?"left":"top",h=u?"bottom":"right",m=L(r)[c];s[h]-ml[h]&&(e.offsets.popper[f]+=s[f]+m-l[h]),e.offsets.popper=S(e.offsets.popper);var v=s[f]+s[c]/2-m/2,g=a(e.instance.popper),b=parseFloat(g["margin"+d]),y=parseFloat(g["border"+d+"Width"]),x=v-e.offsets.popper[f]-b-y;return x=Math.max(Math.min(l[c]-m,x),0),e.arrowElement=r,e.offsets.arrow=(k(n={},f,Math.round(x)),k(n,p,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(e,t){if(B(e.instance.modifiers,"inner"))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var n=M(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),r=e.placement.split("-")[0],o=I(r),i=e.placement.split("-")[1]||"",a=[];switch(t.behavior){case te:a=[r,o];break;case ne:a=ee(r);break;case re:a=ee(r,!0);break;default:a=t.behavior}return a.forEach((function(l,s){if(r!==l||a.length===s+1)return e;r=e.placement.split("-")[0],o=I(r);var u=e.offsets.popper,c=e.offsets.reference,d=Math.floor,f="left"===r&&d(u.right)>d(c.left)||"right"===r&&d(u.left)d(c.top)||"bottom"===r&&d(u.top)d(n.right),m=d(u.top)d(n.bottom),g="left"===r&&p||"right"===r&&h||"top"===r&&m||"bottom"===r&&v,b=-1!==["top","bottom"].indexOf(r),y=!!t.flipVariations&&(b&&"start"===i&&p||b&&"end"===i&&h||!b&&"start"===i&&m||!b&&"end"===i&&v),x=!!t.flipVariationsByContent&&(b&&"start"===i&&h||b&&"end"===i&&p||!b&&"start"===i&&v||!b&&"end"===i&&m),w=y||x;(f||g||w)&&(e.flipped=!0,(f||g)&&(r=a[s+1]),w&&(i=function(e){return"end"===e?"start":"start"===e?"end":e}(i)),e.placement=r+(i?"-"+i:""),e.offsets.popper=E({},e.offsets.popper,z(e.instance.popper,e.offsets.reference,e.placement)),e=F(e.instance.modifiers,e,"flip"))})),e},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],r=e.offsets,o=r.popper,i=r.reference,a=-1!==["left","right"].indexOf(n),l=-1===["top","left"].indexOf(n);return o[a?"left":"top"]=i[n]-(l?o[a?"width":"height"]:0),e.placement=I(t),e.offsets.popper=S(o),e}},hide:{order:800,enabled:!0,fn:function(e){if(!Q(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=D(e.instance.modifiers,(function(e){return"preventOverflow"===e.name})).boundaries;if(t.bottomn.right||t.top>n.bottom||t.right2&&void 0!==arguments[2]?arguments[2]:{};w(this,e),this.scheduleUpdate=function(){return requestAnimationFrame(r.update)},this.update=o(this.update.bind(this)),this.options=E({},e.Defaults,a),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=t&&t.jquery?t[0]:t,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(E({},e.Defaults.modifiers,a.modifiers)).forEach((function(t){r.options.modifiers[t]=E({},e.Defaults.modifiers[t]||{},a.modifiers?a.modifiers[t]:{})})),this.modifiers=Object.keys(this.options.modifiers).map((function(e){return E({name:e},r.options.modifiers[e])})).sort((function(e,t){return e.order-t.order})),this.modifiers.forEach((function(e){e.enabled&&i(e.onLoad)&&e.onLoad(r.reference,r.popper,r.options,e,r.state)})),this.update();var l=this.options.eventsEnabled;l&&this.enableEventListeners(),this.state.eventsEnabled=l}return O(e,[{key:"update",value:function(){return W.call(this)}},{key:"destroy",value:function(){return $.call(this)}},{key:"enableEventListeners",value:function(){return q.call(this)}},{key:"disableEventListeners",value:function(){return Y.call(this)}}]),e}();ae.Utils=("undefined"!==typeof window?window:e).PopperUtils,ae.placements=Z,ae.Defaults=ie,t.a=ae}).call(this,n(114))},function(e,t,n){"use strict";var r=n(16),o=n(17);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=o(n(0)),a=(0,r(n(18)).default)(i.createElement(i.Fragment,null,i.createElement("path",{d:"M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"}),i.createElement("path",{d:"M12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z"})),"AccessTime");t.default=a},function(e,t,n){"use strict";var r=n(16),o=n(17);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=o(n(0)),a=(0,r(n(18)).default)(i.createElement("path",{d:"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"}),"Edit");t.default=a},function(e,t,n){"use strict";var r=n(16),o=n(17);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=o(n(0)),a=(0,r(n(18)).default)(i.createElement("path",{d:"M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"}),"Visibility");t.default=a},function(e,t,n){"use strict";function r(e){return function(t){var n=t.dispatch,r=t.getState;return function(t){return function(o){return"function"===typeof o?o(n,r,e):t(o)}}}}var o=r();o.withExtraArgument=r,t.a=o},function(e,t,n){"use strict";function r(e){for(var t="https://mui.com/production-error/?code="+e,n=1;n3&&void 0!==arguments[3]?arguments[3]:[],o=arguments.length>4?arguments[4]:void 0,i=[t,n].concat(Object(v.a)(r)),a=["TEMPLATE","SCRIPT","STYLE"];[].forEach.call(e.children,(function(e){1===e.nodeType&&-1===i.indexOf(e)&&-1===a.indexOf(e.tagName)&&y(e,o)}))}function O(e,t){var n=-1;return e.some((function(e,r){return!!t(e)&&(n=r,!0)})),n}function k(e,t){var n,r=[],o=[],i=e.container;if(!t.disableScrollLock){if(function(e){var t=Object(u.a)(e);return t.body===e?Object(b.a)(t).innerWidth>t.documentElement.clientWidth:e.scrollHeight>e.clientHeight}(i)){var a=Object(g.a)();r.push({value:i.style.paddingRight,key:"padding-right",el:i}),i.style["padding-right"]="".concat(x(i)+a,"px"),n=Object(u.a)(i).querySelectorAll(".mui-fixed"),[].forEach.call(n,(function(e){o.push(e.style.paddingRight),e.style.paddingRight="".concat(x(e)+a,"px")}))}var l=i.parentElement,s="HTML"===l.nodeName&&"scroll"===window.getComputedStyle(l)["overflow-y"]?l:i;r.push({value:s.style.overflow,key:"overflow",el:s}),s.style.overflow="hidden"}return function(){n&&[].forEach.call(n,(function(e,t){o[t]?e.style.paddingRight=o[t]:e.style.removeProperty("padding-right")})),r.forEach((function(e){var t=e.value,n=e.el,r=e.key;t?n.style.setProperty(r,t):n.style.removeProperty(r)}))}}var E=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.modals=[],this.containers=[]}return Object(m.a)(e,[{key:"add",value:function(e,t){var n=this.modals.indexOf(e);if(-1!==n)return n;n=this.modals.length,this.modals.push(e),e.modalRef&&y(e.modalRef,!1);var r=function(e){var t=[];return[].forEach.call(e.children,(function(e){e.getAttribute&&"true"===e.getAttribute("aria-hidden")&&t.push(e)})),t}(t);w(t,e.mountNode,e.modalRef,r,!0);var o=O(this.containers,(function(e){return e.container===t}));return-1!==o?(this.containers[o].modals.push(e),n):(this.containers.push({modals:[e],container:t,restore:null,hiddenSiblingNodes:r}),n)}},{key:"mount",value:function(e,t){var n=O(this.containers,(function(t){return-1!==t.modals.indexOf(e)})),r=this.containers[n];r.restore||(r.restore=k(r,t))}},{key:"remove",value:function(e){var t=this.modals.indexOf(e);if(-1===t)return t;var n=O(this.containers,(function(t){return-1!==t.modals.indexOf(e)})),r=this.containers[n];if(r.modals.splice(r.modals.indexOf(e),1),this.modals.splice(t,1),0===r.modals.length)r.restore&&r.restore(),e.modalRef&&y(e.modalRef,!0),w(r.container,e.mountNode,e.modalRef,r.hiddenSiblingNodes,!1),this.containers.splice(n,1);else{var o=r.modals[r.modals.length-1];o.modalRef&&y(o.modalRef,!1)}return t}},{key:"isTopModal",value:function(e){return this.modals.length>0&&this.modals[this.modals.length-1]===e}}]),e}();var S=function(e){var t=e.children,n=e.disableAutoFocus,r=void 0!==n&&n,o=e.disableEnforceFocus,l=void 0!==o&&o,s=e.disableRestoreFocus,c=void 0!==s&&s,d=e.getDoc,p=e.isEnabled,h=e.open,m=i.useRef(),v=i.useRef(null),g=i.useRef(null),b=i.useRef(),y=i.useRef(null),x=i.useCallback((function(e){y.current=a.findDOMNode(e)}),[]),w=Object(f.a)(t.ref,x),O=i.useRef();return i.useEffect((function(){O.current=h}),[h]),!O.current&&h&&"undefined"!==typeof window&&(b.current=d().activeElement),i.useEffect((function(){if(h){var e=Object(u.a)(y.current);r||!y.current||y.current.contains(e.activeElement)||(y.current.hasAttribute("tabIndex")||y.current.setAttribute("tabIndex",-1),y.current.focus());var t=function(){null!==y.current&&(e.hasFocus()&&!l&&p()&&!m.current?y.current&&!y.current.contains(e.activeElement)&&y.current.focus():m.current=!1)},n=function(t){!l&&p()&&9===t.keyCode&&e.activeElement===y.current&&(m.current=!0,t.shiftKey?g.current.focus():v.current.focus())};e.addEventListener("focus",t,!0),e.addEventListener("keydown",n,!0);var o=setInterval((function(){t()}),50);return function(){clearInterval(o),e.removeEventListener("focus",t,!0),e.removeEventListener("keydown",n,!0),c||(b.current&&b.current.focus&&b.current.focus(),b.current=null)}}}),[r,l,c,p,h]),i.createElement(i.Fragment,null,i.createElement("div",{tabIndex:0,ref:v,"data-test":"sentinelStart"}),i.cloneElement(t,{ref:w}),i.createElement("div",{tabIndex:0,ref:g,"data-test":"sentinelEnd"}))},j={root:{zIndex:-1,position:"fixed",right:0,bottom:0,top:0,left:0,backgroundColor:"rgba(0, 0, 0, 0.5)",WebkitTapHighlightColor:"transparent"},invisible:{backgroundColor:"transparent"}},C=i.forwardRef((function(e,t){var n=e.invisible,a=void 0!==n&&n,l=e.open,s=Object(r.a)(e,["invisible","open"]);return l?i.createElement("div",Object(o.a)({"aria-hidden":!0,ref:t},s,{style:Object(o.a)({},j.root,a?j.invisible:{},s.style)})):null}));var _=new E,P=i.forwardRef((function(e,t){var n=Object(l.a)(),m=Object(s.a)({name:"MuiModal",props:Object(o.a)({},e),theme:n}),v=m.BackdropComponent,g=void 0===v?C:v,b=m.BackdropProps,x=m.children,w=m.closeAfterTransition,O=void 0!==w&&w,k=m.container,E=m.disableAutoFocus,j=void 0!==E&&E,P=m.disableBackdropClick,T=void 0!==P&&P,M=m.disableEnforceFocus,R=void 0!==M&&M,N=m.disableEscapeKeyDown,A=void 0!==N&&N,L=m.disablePortal,I=void 0!==L&&L,z=m.disableRestoreFocus,D=void 0!==z&&z,F=m.disableScrollLock,W=void 0!==F&&F,B=m.hideBackdrop,U=void 0!==B&&B,$=m.keepMounted,V=void 0!==$&&$,H=m.manager,q=void 0===H?_:H,Y=m.onBackdropClick,X=m.onClose,G=m.onEscapeKeyDown,K=m.onRendered,Q=m.open,Z=Object(r.a)(m,["BackdropComponent","BackdropProps","children","closeAfterTransition","container","disableAutoFocus","disableBackdropClick","disableEnforceFocus","disableEscapeKeyDown","disablePortal","disableRestoreFocus","disableScrollLock","hideBackdrop","keepMounted","manager","onBackdropClick","onClose","onEscapeKeyDown","onRendered","open"]),J=i.useState(!0),ee=J[0],te=J[1],ne=i.useRef({}),re=i.useRef(null),oe=i.useRef(null),ie=Object(f.a)(oe,t),ae=function(e){return!!e.children&&e.children.props.hasOwnProperty("in")}(m),le=function(){return Object(u.a)(re.current)},se=function(){return ne.current.modalRef=oe.current,ne.current.mountNode=re.current,ne.current},ue=function(){q.mount(se(),{disableScrollLock:W}),oe.current.scrollTop=0},ce=Object(p.a)((function(){var e=function(e){return e="function"===typeof e?e():e,a.findDOMNode(e)}(k)||le().body;q.add(se(),e),oe.current&&ue()})),de=i.useCallback((function(){return q.isTopModal(se())}),[q]),fe=Object(p.a)((function(e){re.current=e,e&&(K&&K(),Q&&de()?ue():y(oe.current,!0))})),pe=i.useCallback((function(){q.remove(se())}),[q]);if(i.useEffect((function(){return function(){pe()}}),[pe]),i.useEffect((function(){Q?ce():ae&&O||pe()}),[Q,pe,ae,O,ce]),!V&&!Q&&(!ae||ee))return null;var he=function(e){return{root:{position:"fixed",zIndex:e.zIndex.modal,right:0,bottom:0,top:0,left:0},hidden:{visibility:"hidden"}}}(n||{zIndex:h.a}),me={};return void 0===x.props.tabIndex&&(me.tabIndex=x.props.tabIndex||"-1"),ae&&(me.onEnter=Object(d.a)((function(){te(!1)}),x.props.onEnter),me.onExited=Object(d.a)((function(){te(!0),O&&pe()}),x.props.onExited)),i.createElement(c.a,{ref:fe,container:k,disablePortal:I},i.createElement("div",Object(o.a)({ref:ie,onKeyDown:function(e){"Escape"===e.key&&de()&&(G&&G(e),A||(e.stopPropagation(),X&&X(e,"escapeKeyDown")))},role:"presentation"},Z,{style:Object(o.a)({},he.root,!Q&&ee?he.hidden:{},Z.style)}),U?null:i.createElement(g,Object(o.a)({open:Q,onClick:function(e){e.target===e.currentTarget&&(Y&&Y(e),!T&&X&&X(e,"backdropClick"))}},b)),i.createElement(S,{disableEnforceFocus:R,disableAutoFocus:j,disableRestoreFocus:D,getDoc:le,isEnabled:de,open:Q},i.cloneElement(x,me))))}));t.a=P},function(e,t,n){"use strict";var r=n(23),o=n(59),i=n(0),a=n.n(i),l=n(13),s=n.n(l),u=!1,c=n(71),d=function(e){function t(t,n){var r;r=e.call(this,t,n)||this;var o,i=n&&!n.isMounting?t.enter:t.appear;return r.appearStatus=null,t.in?i?(o="exited",r.appearStatus="entering"):o="entered":o=t.unmountOnExit||t.mountOnEnter?"unmounted":"exited",r.state={status:o},r.nextCallback=null,r}Object(o.a)(t,e),t.getDerivedStateFromProps=function(e,t){return e.in&&"unmounted"===t.status?{status:"exited"}:null};var n=t.prototype;return n.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},n.componentDidUpdate=function(e){var t=null;if(e!==this.props){var n=this.state.status;this.props.in?"entering"!==n&&"entered"!==n&&(t="entering"):"entering"!==n&&"entered"!==n||(t="exiting")}this.updateStatus(!1,t)},n.componentWillUnmount=function(){this.cancelNextCallback()},n.getTimeouts=function(){var e,t,n,r=this.props.timeout;return e=t=n=r,null!=r&&"number"!==typeof r&&(e=r.exit,t=r.enter,n=void 0!==r.appear?r.appear:t),{exit:e,enter:t,appear:n}},n.updateStatus=function(e,t){if(void 0===e&&(e=!1),null!==t)if(this.cancelNextCallback(),"entering"===t){if(this.props.unmountOnExit||this.props.mountOnEnter){var n=this.props.nodeRef?this.props.nodeRef.current:s.a.findDOMNode(this);n&&function(e){e.scrollTop}(n)}this.performEnter(e)}else this.performExit();else this.props.unmountOnExit&&"exited"===this.state.status&&this.setState({status:"unmounted"})},n.performEnter=function(e){var t=this,n=this.props.enter,r=this.context?this.context.isMounting:e,o=this.props.nodeRef?[r]:[s.a.findDOMNode(this),r],i=o[0],a=o[1],l=this.getTimeouts(),c=r?l.appear:l.enter;!e&&!n||u?this.safeSetState({status:"entered"},(function(){t.props.onEntered(i)})):(this.props.onEnter(i,a),this.safeSetState({status:"entering"},(function(){t.props.onEntering(i,a),t.onTransitionEnd(c,(function(){t.safeSetState({status:"entered"},(function(){t.props.onEntered(i,a)}))}))})))},n.performExit=function(){var e=this,t=this.props.exit,n=this.getTimeouts(),r=this.props.nodeRef?void 0:s.a.findDOMNode(this);t&&!u?(this.props.onExit(r),this.safeSetState({status:"exiting"},(function(){e.props.onExiting(r),e.onTransitionEnd(n.exit,(function(){e.safeSetState({status:"exited"},(function(){e.props.onExited(r)}))}))}))):this.safeSetState({status:"exited"},(function(){e.props.onExited(r)}))},n.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},n.safeSetState=function(e,t){t=this.setNextCallback(t),this.setState(e,t)},n.setNextCallback=function(e){var t=this,n=!0;return this.nextCallback=function(r){n&&(n=!1,t.nextCallback=null,e(r))},this.nextCallback.cancel=function(){n=!1},this.nextCallback},n.onTransitionEnd=function(e,t){this.setNextCallback(t);var n=this.props.nodeRef?this.props.nodeRef.current:s.a.findDOMNode(this),r=null==e&&!this.props.addEndListener;if(n&&!r){if(this.props.addEndListener){var o=this.props.nodeRef?[this.nextCallback]:[n,this.nextCallback],i=o[0],a=o[1];this.props.addEndListener(i,a)}null!=e&&setTimeout(this.nextCallback,e)}else setTimeout(this.nextCallback,0)},n.render=function(){var e=this.state.status;if("unmounted"===e)return null;var t=this.props,n=t.children,o=(t.in,t.mountOnEnter,t.unmountOnExit,t.appear,t.enter,t.exit,t.timeout,t.addEndListener,t.onEnter,t.onEntering,t.onEntered,t.onExit,t.onExiting,t.onExited,t.nodeRef,Object(r.a)(t,["children","in","mountOnEnter","unmountOnExit","appear","enter","exit","timeout","addEndListener","onEnter","onEntering","onEntered","onExit","onExiting","onExited","nodeRef"]));return a.a.createElement(c.a.Provider,{value:null},"function"===typeof n?n(e,o):a.a.cloneElement(a.a.Children.only(n),o))},t}(a.a.Component);function f(){}d.contextType=c.a,d.propTypes={},d.defaultProps={in:!1,mountOnEnter:!1,unmountOnExit:!1,appear:!1,enter:!0,exit:!0,onEnter:f,onEntering:f,onEntered:f,onExit:f,onExiting:f,onExited:f},d.UNMOUNTED="unmounted",d.EXITED="exited",d.ENTERING="entering",d.ENTERED="entered",d.EXITING="exiting";t.a=d},function(e,t,n){"use strict";var r=n(2),o=n(1),i=n(173),a=n(0),l=n(3),s=n(36),u=n(45),c=n(4),d=n(6),f=n(9),p=n(50);function h(e,t){return parseInt(e[t],10)||0}var m="undefined"!==typeof window?a.useLayoutEffect:a.useEffect,v={visibility:"hidden",position:"absolute",overflow:"hidden",height:0,top:0,left:0,transform:"translateZ(0)"},g=a.forwardRef((function(e,t){var n=e.onChange,i=e.rows,l=e.rowsMax,s=e.rowsMin,u=e.maxRows,c=e.minRows,d=void 0===c?1:c,g=e.style,b=e.value,y=Object(r.a)(e,["onChange","rows","rowsMax","rowsMin","maxRows","minRows","style","value"]),x=u||l,w=i||s||d,O=a.useRef(null!=b).current,k=a.useRef(null),E=Object(f.a)(t,k),S=a.useRef(null),j=a.useRef(0),C=a.useState({}),_=C[0],P=C[1],T=a.useCallback((function(){var t=k.current,n=window.getComputedStyle(t),r=S.current;r.style.width=n.width,r.value=t.value||e.placeholder||"x","\n"===r.value.slice(-1)&&(r.value+=" ");var o=n["box-sizing"],i=h(n,"padding-bottom")+h(n,"padding-top"),a=h(n,"border-bottom-width")+h(n,"border-top-width"),l=r.scrollHeight-i;r.value="x";var s=r.scrollHeight-i,u=l;w&&(u=Math.max(Number(w)*s,u)),x&&(u=Math.min(Number(x)*s,u));var c=(u=Math.max(u,s))+("border-box"===o?i+a:0),d=Math.abs(u-l)<=1;P((function(e){return j.current<20&&(c>0&&Math.abs((e.outerHeightStyle||0)-c)>1||e.overflow!==d)?(j.current+=1,{overflow:d,outerHeightStyle:c}):e}))}),[x,w,e.placeholder]);a.useEffect((function(){var e=Object(p.a)((function(){j.current=0,T()}));return window.addEventListener("resize",e),function(){e.clear(),window.removeEventListener("resize",e)}}),[T]),m((function(){T()})),a.useEffect((function(){j.current=0}),[b]);return a.createElement(a.Fragment,null,a.createElement("textarea",Object(o.a)({value:b,onChange:function(e){j.current=0,O||T(),n&&n(e)},ref:E,rows:w,style:Object(o.a)({height:_.outerHeightStyle,overflow:_.overflow?"hidden":null},g)},y)),a.createElement("textarea",{"aria-hidden":!0,className:e.className,readOnly:!0,ref:S,tabIndex:-1,style:Object(o.a)({},v,g)}))})),b=n(60),y="undefined"===typeof window?a.useEffect:a.useLayoutEffect,x=a.forwardRef((function(e,t){var n=e["aria-describedby"],c=e.autoComplete,p=e.autoFocus,h=e.classes,m=e.className,v=(e.color,e.defaultValue),x=e.disabled,w=e.endAdornment,O=(e.error,e.fullWidth),k=void 0!==O&&O,E=e.id,S=e.inputComponent,j=void 0===S?"input":S,C=e.inputProps,_=void 0===C?{}:C,P=e.inputRef,T=(e.margin,e.multiline),M=void 0!==T&&T,R=e.name,N=e.onBlur,A=e.onChange,L=e.onClick,I=e.onFocus,z=e.onKeyDown,D=e.onKeyUp,F=e.placeholder,W=e.readOnly,B=e.renderSuffix,U=e.rows,$=e.rowsMax,V=e.rowsMin,H=e.maxRows,q=e.minRows,Y=e.startAdornment,X=e.type,G=void 0===X?"text":X,K=e.value,Q=Object(r.a)(e,["aria-describedby","autoComplete","autoFocus","classes","className","color","defaultValue","disabled","endAdornment","error","fullWidth","id","inputComponent","inputProps","inputRef","margin","multiline","name","onBlur","onChange","onClick","onFocus","onKeyDown","onKeyUp","placeholder","readOnly","renderSuffix","rows","rowsMax","rowsMin","maxRows","minRows","startAdornment","type","value"]),Z=null!=_.value?_.value:K,J=a.useRef(null!=Z).current,ee=a.useRef(),te=a.useCallback((function(e){0}),[]),ne=Object(f.a)(_.ref,te),re=Object(f.a)(P,ne),oe=Object(f.a)(ee,re),ie=a.useState(!1),ae=ie[0],le=ie[1],se=Object(u.b)();var ue=Object(s.a)({props:e,muiFormControl:se,states:["color","disabled","error","hiddenLabel","margin","required","filled"]});ue.focused=se?se.focused:ae,a.useEffect((function(){!se&&x&&ae&&(le(!1),N&&N())}),[se,x,ae,N]);var ce=se&&se.onFilled,de=se&&se.onEmpty,fe=a.useCallback((function(e){Object(b.b)(e)?ce&&ce():de&&de()}),[ce,de]);y((function(){J&&fe({value:Z})}),[Z,fe,J]);a.useEffect((function(){fe(ee.current)}),[]);var pe=j,he=Object(o.a)({},_,{ref:oe});"string"!==typeof pe?he=Object(o.a)({inputRef:oe,type:G},he,{ref:null}):M?!U||H||q||$||V?(he=Object(o.a)({minRows:U||q,rowsMax:$,maxRows:H},he),pe=g):pe="textarea":he=Object(o.a)({type:G},he);return a.useEffect((function(){se&&se.setAdornedStart(Boolean(Y))}),[se,Y]),a.createElement("div",Object(o.a)({className:Object(l.a)(h.root,h["color".concat(Object(d.a)(ue.color||"primary"))],m,ue.disabled&&h.disabled,ue.error&&h.error,k&&h.fullWidth,ue.focused&&h.focused,se&&h.formControl,M&&h.multiline,Y&&h.adornedStart,w&&h.adornedEnd,"dense"===ue.margin&&h.marginDense),onClick:function(e){ee.current&&e.currentTarget===e.target&&ee.current.focus(),L&&L(e)},ref:t},Q),Y,a.createElement(u.a.Provider,{value:null},a.createElement(pe,Object(o.a)({"aria-invalid":ue.error,"aria-describedby":n,autoComplete:c,autoFocus:p,defaultValue:v,disabled:ue.disabled,id:E,onAnimationStart:function(e){fe("mui-auto-fill-cancel"===e.animationName?ee.current:{value:"x"})},name:R,placeholder:F,readOnly:W,required:ue.required,rows:U,value:Z,onKeyDown:z,onKeyUp:D},he,{className:Object(l.a)(h.input,_.className,ue.disabled&&h.disabled,M&&h.inputMultiline,ue.hiddenLabel&&h.inputHiddenLabel,Y&&h.inputAdornedStart,w&&h.inputAdornedEnd,"search"===G&&h.inputTypeSearch,"dense"===ue.margin&&h.inputMarginDense),onBlur:function(e){N&&N(e),_.onBlur&&_.onBlur(e),se&&se.onBlur?se.onBlur(e):le(!1)},onChange:function(e){if(!J){var t=e.target||ee.current;if(null==t)throw new Error(Object(i.a)(1));fe({value:t.value})}for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;oT.length&&T.push(e)}function N(e,t,n){return null==e?0:function e(t,n,r,o){var l=typeof t;"undefined"!==l&&"boolean"!==l||(t=null);var s=!1;if(null===t)s=!0;else switch(l){case"string":case"number":s=!0;break;case"object":switch(t.$$typeof){case i:case a:s=!0}}if(s)return r(o,t,""===n?"."+A(t,0):n),1;if(s=0,n=""===n?".":n+":",Array.isArray(t))for(var u=0;u