diff --git a/package.json b/package.json
index 2046fd478..8d723bbc3 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"react-dom": "^16.13.1",
"react-helmet": "^6.1.0",
"react-json-view": "^1.20.0",
+ "react-markdown": "^6.0.2",
"react-redux": "^7.2.0",
"react-router-dom": "^5.1.2",
"reactstrap": "^8.4.1",
diff --git a/src/abc/Module.js b/src/abc/Module.js
index b0519eaec..06370107d 100644
--- a/src/abc/Module.js
+++ b/src/abc/Module.js
@@ -1,6 +1,10 @@
export default class Module {
constructor(app, name) {
this.App = app;
+ this.Name = name;
+ this.Config = app?.config;
+ this.Navigation = app?.Navigation;
+ this.Router = app?.Router;
}
initialize() {
diff --git a/src/modules/wizard/Wizard.js b/src/modules/wizard/Wizard.js
new file mode 100644
index 000000000..0edc8ecb7
--- /dev/null
+++ b/src/modules/wizard/Wizard.js
@@ -0,0 +1,166 @@
+import React, { useState, useEffect } from "react";
+import { useForm } from "react-hook-form";
+import ReactMarkdown from 'react-markdown'
+import { Card, CardHeader, CardBody, CardFooter, FormGroup, FormText, Input, Label, Pagination, PaginationItem, PaginationLink } from "reactstrap";
+
+
+export function Wizard({
+ name, wizardPages
+}) {
+
+ const [rows, setRows] = useState();
+ const [page, setPage] = useState(1);
+ const [totalCount, setTotalCount] = useState(0); //TODO: calculate
+ const [progressStep, setProgressStep] = useState(20);
+ const [pageTitle, setPageTitle] = useState("");
+ const { register, handleSubmit, setValue, getValues, errors, reset } = useForm();
+
+ useEffect(() => {
+ setTotalCount(wizardPages.length);
+ }, []);
+
+ useEffect(() => {
+ getRows();
+ setPageTitle(wizardPages[page - 1].title ? wizardPages[page - 1].title : page - 1); // pagination counts index from 1, not 0
+ }, [page]);
+
+ const getRows = () => {
+ var view = []
+ if (wizardPages != undefined) {
+ var pageView = wizardPages[page - 1]
+ if (pageView.rows != undefined) {
+ pageView.rows.forEach(row => {
+ if (row.type == "markdown") {
+ view.push(
+
+ {row.contents}
+
+ )
+ } else if (row.type == "object") {
+ var line = row.properties
+ Object.keys(line).map((key, idx) => {
+ view.push()
+ })
+ };
+ });
+ };
+ setRows(view)
+ };
+ };
+
+
+ return (
+
+
+ {pageTitle}
+
+
+
+ {rows}
+
+
+
+ {/* Here should be some progress indicator */}
+
+
+ );
+}
+
+
+
+
+function ListPagination(props) {
+
+ const slots = Math.min(5, props.max);
+
+ var start = props.page - Math.floor(slots / 2);
+ var end = props.page + Math.floor(slots / 2);
+
+ if (start < 1) {
+ start = 1;
+ end = Math.min(slots, props.max);
+ }
+
+ else if (end > props.max) {
+ start = Math.max(props.max - slots + 1, 1);
+ end = props.max;
+ }
+
+ let pages = [];
+ var i;
+ for (i = start; i <= end; i++) {
+ pages.push(i);
+ }
+
+ return (
+
+
+ props.onPageChange(Math.max(1, props.page - 1))}
+ >
+
+
+
+ {pages.map((i, x) =>
+
+ {
+ props.onPageChange(i);
+ }}
+ >
+ {i}
+
+
+ )}
+
+
+ = props.max}
+ onClick={(e) => {
+ props.onPageChange(props.page + 1)
+ }}
+ >
+
+
+
+ );
+}
+
+
+// TODO: Different types of Item to cover formats such as "number", "boolean", checkbox, radiobox
+export function InputStringItem(props) {
+ return (
+
+
+
+
+ {props.description}
+
+
+ );
+}
diff --git a/src/modules/wizard/WizardContainer.js b/src/modules/wizard/WizardContainer.js
new file mode 100644
index 000000000..9cc81d391
--- /dev/null
+++ b/src/modules/wizard/WizardContainer.js
@@ -0,0 +1,23 @@
+import React from 'react';
+
+import { Wizard } from './Wizard';
+import { Container } from 'reactstrap';
+
+import json from './mock-data.json';
+
+const WizardContainer = (props) => {
+ console.log(json)
+
+ return (
+
+
+
+ )
+}
+
+export default WizardContainer;
+
diff --git a/src/modules/wizard/index.js b/src/modules/wizard/index.js
new file mode 100644
index 000000000..6c16ab8ac
--- /dev/null
+++ b/src/modules/wizard/index.js
@@ -0,0 +1,23 @@
+import Module from 'asab-webui/abc/Module';
+import WizardContainer from './WizardContainer';
+
+export default class WizardModule extends Module {
+ constructor (app) {
+ super(app, "WizardModule");
+
+ console.log("WizardModule: ", this);
+
+ this.Router.addRoute({
+ path: "/wizard/",
+ exact: true,
+ name: "Wizard",
+ component: WizardContainer
+ })
+
+ this.Navigation.addItem({
+ name: "Wizard",
+ icon: "cil-gem",
+ url: "/wizard/"
+ })
+ }
+}
\ No newline at end of file
diff --git a/src/modules/wizard/mock-data.json b/src/modules/wizard/mock-data.json
new file mode 100644
index 000000000..806efd026
--- /dev/null
+++ b/src/modules/wizard/mock-data.json
@@ -0,0 +1,119 @@
+{
+ "name": "Wizard Name",
+ "wizardPages": [
+ {
+ "title": "a 1",
+ "rows":
+ [
+ {
+ "type": "markdown",
+ "contents": "# headline \n ## second line \n normal text \n\n second line of normal text"
+ },
+ {
+ "$id": "some_id",
+ "type": "object",
+ "description": "description",
+ "examples": [
+ {
+ "servers": "lm1:12181,lm2:22181,lm3:32181",
+ "path": "/lmio"
+ }
+ ],
+ "required": [
+ "servers",
+ "path"
+ ],
+ "properties": {
+ "servers": {
+ "$id": "#/properties/asab%3Azookeeper/properties/servers",
+ "type": "text",
+ "title": "The servers schema",
+ "description": "An explanation about the purpose of this instance.",
+ "default": "",
+ "examples": [
+ "lm1:12181,lm2:22181,lm3:32181"
+ ]
+ },
+ "path": {
+ "$id": "#/properties/asab%3Azookeeper/properties/path",
+ "type": "text",
+ "title": "The path schema",
+ "description": "An explanation about the purpose of this instance.",
+ "default": "",
+ "examples": [
+ "/lmio"
+ ]
+ }
+ }
+ },
+ {
+ "type": "markdown",
+ "contents": "## headline 2"
+ }
+ ]
+ },
+ {
+ "title": "b 2",
+ "rows":
+ [
+ {
+ "type": "markdown",
+ "contents": "# headline \n ## second line \n normal text \n\n second line of normal text"
+ },
+ {
+ "type": "markdown",
+ "contents": "## headline 2"
+ },
+ {
+ "type": "markdown",
+ "contents": "### headline 3"
+ },
+ {
+ "type": "markdown",
+ "contents": "#### headline 4"
+ },
+ {
+ "$id": "some_id2",
+ "type": "object",
+ "description": "description",
+ "examples": [
+ {
+ "servers": "lm1:12181,lm2:22181,lm3:32181",
+ "path": "/lmio"
+ }
+ ],
+ "required": [
+ "servers",
+ "path"
+ ],
+ "properties": {
+ "servers2": {
+ "$id": "#/properties/asab%3Azookeeper/properties/servers2",
+ "type": "string",
+ "title": "The servers schema2",
+ "description": "An explanation about the purpose of servers2.",
+ "default": "",
+ "examples": [
+ "lm1:12181,lm2:22181,lm3:32181"
+ ]
+ },
+ "path2": {
+ "$id": "#/properties/asab%3Azookeeper/properties/path2",
+ "type": "string",
+ "title": "The path schema2",
+ "description": "An explanation about the purpose of path2.",
+ "default": "some default value",
+ "examples": [
+ "/lmio"
+ ]
+ }
+ }
+ },
+ {
+ "type": "markdown",
+ "contents": "last markdown under input"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/style.scss b/src/style.scss
index 0a2bdbca9..f1a53eb92 100644
--- a/src/style.scss
+++ b/src/style.scss
@@ -26,3 +26,7 @@ main > .container-fluid, main > .container {
.breadcrumb {
margin: 0 !important;
}
+
+.height-100 {
+ height: 100% !important;
+}