{"id":21430152,"url":"https://github.com/cuba-platform/interactive-tour-addon","last_synced_at":"2025-03-16T21:44:49.925Z","repository":{"id":85315547,"uuid":"147793805","full_name":"cuba-platform/interactive-tour-addon","owner":"cuba-platform","description":null,"archived":false,"fork":false,"pushed_at":"2020-03-23T09:41:21.000Z","size":637,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-01-23T08:19:37.198Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cuba-platform.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-07T08:22:40.000Z","updated_at":"2022-05-07T10:56:43.000Z","dependencies_parsed_at":"2023-04-13T07:15:08.280Z","dependency_job_id":null,"html_url":"https://github.com/cuba-platform/interactive-tour-addon","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Finteractive-tour-addon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Finteractive-tour-addon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Finteractive-tour-addon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Finteractive-tour-addon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cuba-platform","download_url":"https://codeload.github.com/cuba-platform/interactive-tour-addon/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243940063,"owners_count":20372044,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-22T22:21:01.699Z","updated_at":"2025-03-16T21:44:49.893Z","avatar_url":"https://github.com/cuba-platform.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Interactive Tour\n\n## Overview\nThis add-on is designed to create **product tour** for screens in the projects based on\n[CUBA platform](https://www.cuba-platform.com).\n\n### Main features\nThe following components are used to create and run tours:\n- **Tour** (A tour consisting of one or multiple steps)\n- **Step** (A single step of a tour)\n- **StepButton** (A button of a step that can be used to provide different actions if clicked)\n- **TourStartAction** (Standard action to start a tour)\n- **TourParser** (Parser of Tour objects)\n\n## Installation\n\nThe add-on can be added to your project in one of the ways described below. Installation from the Marketplace is the simplest way. The last version of the add-on compatible with the used version of the platform will be installed.\nAlso, you can install the add-on by coordinates choosing the required version of the add-on from the table.\n\nIn case you want to install the add-on by manual editing or by building from sources see the complete add-ons installation guide in [CUBA Platform documentation](https://doc.cuba-platform.com/manual-latest/manual.html#app_components_usage).\n\n### From the Marketplace\n\n1. Open your application in CUBA Studio. Check the latest version of CUBA Studio on the [CUBA Platform site](https://www.cuba-platform.com/download/previous-studio/).\n2. Go to *CUBA -\u003e Marketplace* in the main menu.\n\n ![marketplace](screenshots/marketplace.png)\n\n3. Find the *Interactive Tour* add-on there.\n\n ![addons](screenshots/addons.png)\n\n4. Click *Install* and apply the changes.\nThe add-on corresponding to the used platform version will be installed.\n\n### By Coordinates\n\n1. Open your application in CUBA Studio. Check the latest version of CUBA Studio on the [CUBA Platform site](https://www.cuba-platform.com/download/previous-studio/).\n2. Go to *CUBA -\u003e Marketplace* in the main menu.\n3. Click the icon in the upper-right corner.\n\n ![by-coordinates](screenshots/by-coordinates.png)\n\n4. Paste the add-on coordinates in the corresponding field as follows:\n\n `com.haulmont.addon.tour:tour-global:\u003cadd-on version\u003e`\n\n where `\u003cadd-on version\u003e` is compatible with the used version of the CUBA platform.\n\n | Platform Version | Add-on Version |\n|------------------|----------------|\n| 7.2.x           | 1.3.0         |\n| 7.1.x           | 1.2.0         |\n| 7.0.x           | 1.1.0         |\n| 6.10.x          | 1.0.2         |\n\n5. Click *Install* and apply the changes. The add-on will be installed to your project.\n\n## Usage\n\nThis add-on enables creating a product tour on your application's screens. Each step of the tour can be bound to any UI component, thus helping you to show and describe UI components.\n\nThe tour itself consists of steps, each having its own title, value (text), binding to a component, and some other configuration attributes. In order to interact between steps, the special `StepButton` buttons are used. These buttons may have either predefined actions from `TourActionType` and `StepActionType` or their own actions. The `Tour` interface is used for tours, while `Step` is used for steps.\n\nA tour can also be created with the help of the `parseTour()` method of `TourParser`, which receives a JSON, messagesPack, and window to extend.\n\nTo start a tour, call the `TourStartAction`. The `setSettingsEnabled()` method allows you to define whether the tour should start each time on the screen opening or only once at the first time.\n\n### Step-by-Step Guide\n\nAs a hands-on example, let's create a small demo app containing two screens with tours.\n\nIn this app, we will:\n- create tours on application screens,\n- define different options for the tours steps,\n- define the parameters of the tours start.\n\nTo do that:\n\nCreate a new CUBA project.\n\n![](screenshots/1-create-new-project.png)\n\nAdd the given add-on to it.\n\n![](screenshots/2-add-custom-component.png)\n\nCreate a new entity \"Product\" with two attributes.\n\n![](screenshots/3-create-product-entity.png)\n\n```java\npackage com.company.demo.entity;\n\nimport com.haulmont.chile.core.annotations.NamePattern;\nimport com.haulmont.cuba.core.entity.StandardEntity;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\nimport java.math.BigDecimal;\n\n@NamePattern(\"%s|name\")\n@Table(name = \"DEMO_PRODUCT\")\n@Entity(name = \"demo_Product\")\npublic class Product extends StandardEntity {\n\n    @Column(name = \"NAME\")\n    protected String name;\n\n    @Column(name = \"PRICE\")\n    protected BigDecimal price;\n\n    public BigDecimal getPrice() {\n        return price;\n    }\n\n    public void setPrice(BigDecimal price) {\n        this.price = price;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n}\n```\n\nCreate entity browser and entity editor screens.\n\n![](screenshots/4-create-product-browser-and-editor.png)\n\nLet's add a button which will start a tour in `ProductBrowse`, as well as implement the tours start on the screen opening.\n\nTo create the button, add the component below to the `buttonsPanel` component in `product-browse.xml`:\n\n```xml\n\u003cbutton id=\"startTourBtn\" caption=\"msg://browse.startTour\" invoke=\"startTour\"/\u003e\n```\n\nOn the `ProductBrowse` screen initialization, we will parse the tour from the JSON file which is located in the same directory with screens.\nThis JSON file describes the list of steps for the tour. Each step is provided with several parameters and an array of buttons.\nFor each button we describe its attributes and the action performed on the button's click.\n\n`productBrowseTour.json`:\n```JSON\n[\n  {\n    \"id\": \"browseStepOne\",\n    \"text\": \"tour.tutorialStartedText\",\n    \"title\": \"tour.tutorialStartedTitle\",\n    \"width\": \"400\",\n    \"textContentMode\": \"html\",\n    \"titleContentMode\": \"html\",\n    \"cancellable\": \"true\",\n    \"buttons\": [\n      {\n        \"caption\": \"tour.cancel\",\n        \"style\": \"danger\",\n        \"action\": \"tour:cancel\",\n        \"enabled\": \"true\"\n      },\n      {\n        \"caption\": \"tour.next\",\n        \"style\": \"friendly\",\n        \"action\": \"tour:next\",\n        \"enabled\": \"true\"\n      }\n    ]\n  },\n  {\n    \"id\": \"browseStepTwo\",\n    \"text\": \"tour.createButtonText\",\n    \"title\": \"tour.createButtonTitle\",\n    \"width\": \"400\",\n    \"textContentMode\": \"html\",\n    \"titleContentMode\": \"html\",\n    \"attachTo\": \"createBtn\",\n    \"anchor\": \"right\",\n    \"buttons\": [\n      {\n        \"caption\": \"tour.back\",\n        \"style\": \"primary\",\n        \"action\": \"tour:back\",\n        \"enabled\": \"true\"\n      },\n      {\n        \"caption\": \"tour.next\",\n        \"style\": \"friendly\",\n        \"action\": \"tour:next\",\n        \"enabled\": \"true\"\n      }\n    ]\n  },\n  {\n    \"id\": \"browseStepThree\",\n    \"text\": \"tour.editButtonText\",\n    \"title\": \"tour.editButtonTitle\",\n    \"width\": \"400\",\n    \"textContentMode\": \"html\",\n    \"titleContentMode\": \"html\",\n    \"attachTo\": \"editBtn\",\n    \"anchor\": \"right\",\n    \"buttons\": [\n      {\n        \"caption\": \"tour.back\",\n        \"style\": \"primary\",\n        \"action\": \"tour:back\",\n        \"enabled\": \"true\"\n      },\n      {\n        \"caption\": \"tour.next\",\n        \"style\": \"friendly\",\n        \"action\": \"tour:next\",\n        \"enabled\": \"true\"\n      }\n    ]\n  },\n  {\n    \"id\": \"browseStepFour\",\n    \"text\": \"tour.removeButtonText\",\n    \"title\": \"tour.removeButtonTitle\",\n    \"width\": \"400\",\n    \"textContentMode\": \"html\",\n    \"titleContentMode\": \"html\",\n    \"attachTo\": \"removeBtn\",\n    \"anchor\": \"bottom\",\n    \"buttons\": [\n      {\n        \"caption\": \"tour.back\",\n        \"style\": \"primary\",\n        \"action\": \"tour:back\",\n        \"enabled\": \"true\"\n      },\n      {\n        \"caption\": \"tour.next\",\n        \"style\": \"friendly\",\n        \"action\": \"tour:next\",\n        \"enabled\": \"true\"\n      }\n    ]\n  },\n  {\n    \"id\": \"browseStepFive\",\n    \"text\": \"tour.filterPanelText\",\n    \"title\": \"tour.filterPanelTitle\",\n    \"width\": \"400\",\n    \"textContentMode\": \"html\",\n    \"titleContentMode\": \"html\",\n    \"attachTo\": \"filter\",\n    \"anchor\": \"bottom\",\n    \"modal\": \"true\",\n    \"buttons\": [\n      {\n        \"caption\": \"tour.back\",\n        \"style\": \"primary\",\n        \"action\": \"tour:back\",\n        \"enabled\": \"true\"\n      },\n      {\n        \"caption\": \"tour.finish\",\n        \"style\": \"friendly\",\n        \"action\": \"tour:next\",\n        \"enabled\": \"true\"\n      }\n    ]\n  }\n]\n```\n\nTo start the tour let's create a method `startTour()`. We'll disable the setting of starting a tour only one on the first screen opening.\nThis method will be invoked on the `tourButton` click. Additionally,let's call this method at the screen initialization.\n\n`ProductBrowse.java` should look like this:\n```Java\npackage com.company.demo.web.product;\n\nimport com.haulmont.cuba.gui.screen.*;\nimport com.company.demo.entity.Product;\nimport com.haulmont.addon.tour.web.gui.components.Tour;\nimport com.haulmont.addon.tour.web.gui.components.TourStartAction;\nimport com.haulmont.addon.tour.web.gui.utils.TourParser;\nimport com.haulmont.cuba.core.global.Resources;\n\nimport javax.inject.Inject;\n\n@UiController(\"demo_Product.browse\")\n@UiDescriptor(\"product-browse.xml\")\n@LookupComponent(\"productsTable\")\n@LoadDataBeforeShow\npublic class ProductBrowse extends StandardLookup\u003cProduct\u003e {\n\n    @Inject\n    protected Resources resources;\n    @Inject\n    protected TourParser tourParser;\n    @Inject\n    private MessageBundle messageBundle;\n\n    protected Tour tour;\n    protected TourStartAction tourStartAction;\n\n    @Subscribe\n    private void onInit(InitEvent event) {\n        createTour();\n        createTourStartAction();\n        startTour();\n    }\n\n    protected void createTour() {\n        String jsonTourDescriptionLocation = \"com/company/demo/web/product/productBrowseTour.json\";\n        String jsonTourDescription = resources.getResourceAsString(jsonTourDescriptionLocation);\n        tour = tourParser.parseTour(jsonTourDescription, messageBundle.getMessagesPack(), getWindow());\n    }\n\n    protected void createTourStartAction() {\n        tourStartAction = TourStartAction.create(tour);\n        // The Tour will start each time if the settings disabled\n        tourStartAction.setSettingsEnabled(false);\n    }\n\n    public void startTour() {\n        tourStartAction.actionPerform(getWindow());\n    }\n}\n```\n\nAt the `ProductEdit` screen in the `onInit` method we will call the `createTour()` method where we create a tour instance,\nadd the steps, their parameters, and buttons. Also, in the `onInit` method we create an action that will start a tour\nwithout disabling additional settings.\n\n`ProductEdit.java` should look like this:\n```Java\npackage com.company.demo.web.product;\n\nimport com.company.demo.entity.Product;\nimport com.haulmont.addon.tour.web.gui.components.*;\nimport com.haulmont.cuba.gui.components.Button;\nimport com.haulmont.cuba.gui.components.Form;\nimport com.haulmont.cuba.gui.screen.*;\n\nimport javax.inject.Inject;\n\n@UiController(\"demo_Product.edit\")\n@UiDescriptor(\"product-edit.xml\")\n@EditedEntityContainer(\"productDc\")\n@LoadDataBeforeShow\npublic class ProductEdit extends StandardEditor\u003cProduct\u003e {\n\n    @Inject\n    private Form form;\n    @Inject\n    private Button windowClose;\n    @Inject\n    private MessageBundle messageBundle;\n\n    protected Tour tour;\n    protected TourStartAction tourStartAction;\n\n    @Subscribe\n    private void onInit(InitEvent event) {\n        createTour();\n        tourStartAction = TourStartAction.create(tour);\n        tourStartAction.actionPerform(getWindow());\n    }\n\n    protected void createTour() {\n        tour = new Tour(getWindow());\n        tour.addStep(createStepOne());\n        tour.addStep(createStepTwo());\n        tour.addStep(createStepThree());\n    }\n\n    protected Step createStepOne() {\n        Step step = new Step(\"editStepOne\");\n\n        step.setText(messageBundle.getMessage(\"tour.editStartedText\"));\n        step.setTitle(messageBundle.getMessage(\"tour.editStartedTitle\"));\n        step.setWidth(\"400\");\n        step.setTextContentMode(ContentMode.HTML);\n        step.setTitleContentMode(ContentMode.HTML);\n        step.setCancellable(true);\n\n        StepButton stepButton = new StepButton(messageBundle.getMessage(\"tour.cancel\"));\n        stepButton.setStyleName(\"danger\");\n        stepButton.setEnabled(true);\n        stepButton.addStepButtonClickListener(TourActionType.CANCEL::execute);\n\n        step.addButton(stepButton);\n\n        stepButton = new StepButton(messageBundle.getMessage(\"tour.next\"));\n        stepButton.setStyleName(\"friendly\");\n        stepButton.setEnabled(true);\n        stepButton.addStepButtonClickListener(TourActionType.NEXT::execute);\n\n        step.addButton(stepButton);\n\n        return step;\n    }\n\n    protected Step createStepTwo() {\n        Step step = new Step(\"editStepTwo\");\n\n        step.setText(messageBundle.getMessage(\"tour.formText\"));\n        step.setTitle(messageBundle.getMessage(\"tour.formTitle\"));\n        step.setWidth(\"400\");\n        step.setTextContentMode(ContentMode.HTML);\n        step.setTitleContentMode(ContentMode.HTML);\n        step.setAttachedTo(form);\n        step.setAnchor(StepAnchor.RIGHT);\n\n        StepButton stepButton = new StepButton(messageBundle.getMessage(\"tour.back\"));\n        stepButton.setStyleName(\"primary\");\n        stepButton.setEnabled(true);\n        stepButton.addStepButtonClickListener(TourActionType.BACK::execute);\n\n        step.addButton(stepButton);\n\n        stepButton = new StepButton(messageBundle.getMessage(\"tour.next\"));\n        stepButton.setStyleName(\"friendly\");\n        stepButton.setEnabled(true);\n        stepButton.addStepButtonClickListener(TourActionType.NEXT::execute);\n\n        step.addButton(stepButton);\n\n        return step;\n    }\n\n    protected Step createStepThree() {\n        Step step = new Step(\"editStepThree\");\n\n        step.setText(messageBundle.getMessage(\"tour.windowActionsText\"));\n        step.setTitle(messageBundle.getMessage(\"tour.windowActionsTitle\"));\n        step.setWidth(\"400\");\n        step.setTextContentMode(ContentMode.HTML);\n        step.setTitleContentMode(ContentMode.HTML);\n        step.setAttachedTo(windowClose);\n        step.setAnchor(StepAnchor.RIGHT);\n\n        StepButton stepButton = new StepButton(messageBundle.getMessage(\"tour.back\"));\n        stepButton.setStyleName(\"primary\");\n        stepButton.setEnabled(true);\n        stepButton.addStepButtonClickListener(TourActionType.BACK::execute);\n\n        step.addButton(stepButton);\n\n        stepButton = new StepButton(messageBundle.getMessage(\"tour.finish\"));\n        stepButton.setStyleName(\"friendly\");\n        stepButton.setEnabled(true);\n        stepButton.addStepButtonClickListener(TourActionType.NEXT::execute);\n\n        step.addButton(stepButton);\n\n        return step;\n    }\n}\n```\n\nIn order to display localized messages, put them in `messages.properties`:\n\n```properties\nbrowseCaption=Products\neditorCaption=Product\nbrowse.startTour=Toturial\ntour.createButtonText = \u003cp\u003eThis is a \u003cb\u003ecreate button\u003c/b\u003e.\u003c/p\u003e \\\n  \u003cp\u003ePress the button to open an editor screen to \u003cb\u003ecreate\u003c/b\u003e a new entity.\u003c/p\u003e\ntour.editButtonText = \u003cp\u003eThis is an \u003cb\u003eedit button\u003c/b\u003e.\u003c/p\u003e \\\n  \u003cp\u003eSelect an entity and press the button to open an editor screen to \u003cb\u003eedit\u003c/b\u003e the entity.\u003c/p\u003e\ntour.removeButtonText = \u003cp\u003eThis is a \u003cb\u003eremove button\u003c/b\u003e.\u003c/p\u003e \\\n  \u003cp\u003eSelect an entity and press the button to remove the entity.\u003c/p\u003e\ntour.filterPanelText = \u003cp\u003eThis is a \u003cb\u003efilter panel\u003c/b\u003e.\u003c/p\u003e \\\n  \u003cp\u003eYou may choose conditions to filter entities.\u003c/p\u003e\ntour.formText = \u003cp\u003eThis is a \u003cb\u003eform\u003c/b\u003e consisting of text fields.\u003c/p\u003e \\\n  \u003cp\u003eYou may fill the fields by your own data.\u003c/p\u003e\ntour.windowActionsText = \u003cp\u003eThese are window \u003cb\u003eactions\u003c/b\u003e.\u003c/p\u003e \\\n  \u003cp\u003eYou are able to \u003cb\u003econfirm\u003c/b\u003e or \u003cb\u003edecline\u003c/b\u003e changes.\u003c/p\u003e\ntour.tutorialStartedText = \u003cp\u003eThis tutorial will show you and describe some UI components.\u003c/p\u003e \\\n  \u003cp\u003eThis tour starts every time.\u003c/p\u003e\ntour.editStartedText = \u003cp\u003eThis is an \u003cb\u003eeditor screen\u003c/b\u003e.\u003c/p\u003e \\\n  \u003cp\u003eHere you may \u003cb\u003ecreate\u003c/b\u003e or \u003cb\u003eedit\u003c/b\u003e entities.\u003c/p\u003e \\\n  \u003cp\u003eThis tour starts only once.\u003c/p\u003e\ntour.editStartedTitle = \u003cb\u003eEditor\u003c/b\u003e screen\ntour.tutorialStartedTitle = Tutorial has \u003cb\u003estarted\u003c/b\u003e!\ntour.createButtonTitle = \u003cb\u003eCreate button\u003c/b\u003e\ntour.editButtonTitle = \u003cb\u003eEdit button\u003c/b\u003e\ntour.removeButtonTitle = \u003cb\u003eRemove button\u003c/b\u003e\ntour.filterPanelTitle = \u003cb\u003eFilter Panel\u003c/b\u003e\ntour.formTitle = \u003cb\u003eField group\u003c/b\u003e\ntour.windowActionsTitle = \u003cb\u003eWindow actions\u003c/b\u003e\ntour.back = Back\ntour.next = Next\ntour.cancel = Cancel\ntour.finish = Finish\n```\n\nNow, when the user opens the `Products` screen, each time they will see the tour.\nMoreover, they will be able to start the tour by clicking the \"Tutorial\" button. As for `Product` screen, the tour will start only at the first screen opening for each user.\n\nThis is how the `Products` screen looks in the running app:\n\n![](screenshots/5-products-first.png)\n\nThis is how the `Products` screen looks with the step bound to the component:\n\n![](screenshots/6-products-second.png)\n\nThis is how the `Product` screen looks in the running app:\n\n![](screenshots/7-product-first.png)\n\nThis is how the `Product` screen looks with the step bound to the component:\n\n![](screenshots/8-product-second.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuba-platform%2Finteractive-tour-addon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcuba-platform%2Finteractive-tour-addon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuba-platform%2Finteractive-tour-addon/lists"}