diff --git a/docs/tutorials/articles/sales_dashboard/images/final_app.png b/docs/tutorials/articles/sales_dashboard/images/final_app.png new file mode 100644 index 000000000..536c586a6 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/images/final_app.png differ diff --git a/docs/tutorials/articles/sales_dashboard/images/thumbnail.png b/docs/tutorials/articles/sales_dashboard/images/thumbnail.png new file mode 100644 index 000000000..d6ca7502e Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/images/thumbnail.png differ diff --git a/docs/tutorials/articles/sales_dashboard/images/yt-thumbnail.png b/docs/tutorials/articles/sales_dashboard/images/yt-thumbnail.png new file mode 100644 index 000000000..e17c0778a Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/images/yt-thumbnail.png differ diff --git a/docs/tutorials/articles/sales_dashboard/index.md b/docs/tutorials/articles/sales_dashboard/index.md new file mode 100644 index 000000000..0bc162b12 --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/index.md @@ -0,0 +1,56 @@ +--- +title: Creating a Sales Dashboard +category: fundamentals +data-keywords: gui vizelement chart navbar table layout part menu state multi-page callback +short-description: Understand basic knowledge of Taipy by creating a multi-page sales dashboard. +order: 1.5 +img: sales_dashboard/images/thumbnail.png +--- + +!!! note "Supported Python versions" + Taipy requires **Python 3.9** or newer. + +This tutorial focuses on creating a simple sales dashboard application. You'll learn about visual elements, +interaction, styling, and multi-page applications. + +![Final Application](images/final_app.png){width=90% .tp-image-border} + +### Why Taipy? + +- **Speed:** Quickly develop robust applications. +- **Simplicity:** Easy management of variables and events. +- **Visualization:** Intuitive and clear visual elements. + +Each step in this **Tutorial** builds on the previous one. By the end, you'll be ready to +create your own Taipy applications. + +This tutorial is also available in video format: + +

+ + Youtube Tutorial + +

+ +### Installation + +Ensure you have Python 3.9 or newer, then install Taipy and Plotly: + +```bash +pip install taipy plotly +``` + +!!! info + Use `pip install taipy` for the latest stable version. Need help with pip? Check out + the [installation guide](http://docs.python-guide.org/en/latest/starting/installation/). + +The dataset used in this tutorial is the +[SuperStore Sales dataset](https://www.kaggle.com/datasets/rohitsahoo/sales-forecasting) +available [here](https://github.com/Avaiga/taipy-course-gui/blob/develop/data.csv). + +## Tutorial Steps + +1. [Visual Elements](step_01/step_01.md) +2. [Styling](step_02/step_02.md) +3. [Charts](step_03/step_03.md) +4. [Multipage](step_04/step_04.md) diff --git a/docs/tutorials/articles/sales_dashboard/step_01/images/simple_app.png b/docs/tutorials/articles/sales_dashboard/step_01/images/simple_app.png new file mode 100644 index 000000000..fac2d939b Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_01/images/simple_app.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_01/step_01.md b/docs/tutorials/articles/sales_dashboard/step_01/step_01.md new file mode 100644 index 000000000..fe4a37efa --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/step_01/step_01.md @@ -0,0 +1,163 @@ +--- +hide: + - toc +--- + +The full code for this step is available +[here](https://github.com/Avaiga/taipy-course-gui/blob/develop/2_visual_elements/main.py){: .tp-btn target='blank' } + +Let's start by creating a simple page with 3 components: a selector to select a category of items, +a bar chart which displays the sales of the top 10 countries for this category and +a table which displays data for the selected category + +![Step 1 Application](images/simple_app.png){ width=90% : .tp-image-border } + +Let's start by importing the necessary libraries: + +=== "Python" + ```python + from taipy.gui import Gui + import taipy.gui.builder as tgb + import pandas as pd + ``` +=== "Markdown" + ```python + from taipy.gui import Gui + import pandas as pd + ``` + +We can now start creating the page. We will first add a [selector](../../../../refmans/gui/viselements/generic/selector.md). + +=== "Python" + ```python + with tgb.Page() as page: + tgb.selector(value="{selected_category}", lov="{categories}", on_change=change_category) + ``` +=== "Markdown" + ```python + page = """ + <|{selected_category}|selector|lov={categories}|on_change=change_category|> + """ + ``` + +Taipy [visual elements](../../../../refmans/gui/viselements/index.md) take many properties. +Note that dynamic properties use a quote and brackets syntax. We use `value="{selected_category}"` +to signal to Taipy that `selected_category` should change when the user uses the selector. +Likewise, if `categories` changes, the selector will get updated with the new values. + +Here, selector needs an associated string variable which will change when a user selects a value, +a list of values (lov) to choose from, and a callback function to call when the value changes. +We can define them above: + +```python +data = pd.read_csv("data.csv") +selected_category = "Furniture" +categories = list(data["Category"].unique()) + +def change_category(state): + # Do nothing for now, we will implement this later + return None +``` + +We can now add a chart to display the sales of the top 10 countries for the selected category. + +=== "Python" + ```python + tgb.chart( + data="{chart_data}", + x="State", + y="Sales", + type="bar", + layout="{layout}", + ) + ``` +=== "Markdown" + ``` + <|{chart_data}|chart|x=State|y=Sales|type=bar|layout={layout}|> + ``` + +Taipy charts have many properties. You can create multiple traces, add styling, change the type of chart, etc. + +=== "Python" + ```python + data = {"x_col": [0, 1, 2], "y_col1": [4, 1, 2], "y_col_2": [3, 1, 2]} + with tgb.Page() as page: + tgb.chart("{data}", x="x_col", y__1="y_col1", y__2="y_col_2", type__1="bar", color__2="red") + ``` +=== "Markdown" + ```python + data = {"x_col": [0, 1, 2], "y_col_1": [4, 2, 1], "y_col_2":[3, 1, 2]} + Gui("<|{data}|chart|x=x_col|y[1]=y_col_1|y[2]=y_col_2|type[1]=bar|color[2]=red|>").run() + ``` + + +You can check the syntax for charts [here](../../../../refmans/gui/viselements/generic/chart.md). + +You +can also directly embed Plotly charts using the `figure` property as we will do in [Step 3](../step_03/step_03.md). + +Here we need to provide a Pandas Dataframe with the data to display, the x and y columns to use, the type of chart, +and a layout dictionary with additional properties. + +```python +chart_data = ( + data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() +) + +layout = {"yaxis": {"title": "Revenue (USD)"}, "title": "Sales by State"} +``` + +Lastly, we can add a table to display the data for the selected category. + +=== "Python" + ```python + tgb.table(data="{data}") + ``` +=== "Markdown" + ``` + <|{data}|table|> + ``` + +We can now run the application using: + +```python +if __name__ == "__main__": + Gui(page=page).run(title="Sales", dark_mode=False, debug=True) +``` + +`debug=True` will display a stack trace of the errors if any occur. +You can also set `use_reloader=True` to automatically reload the page +when you save changes to the code and `port=XXXX` to change the server port. + +The application runs but has no interaction. We need to code the callback function +to update the chart and table when the user selects a category. + +```python +def change_category(state): + state.data = data[data["Category"] == state.selected_category] + state.chart_data = ( + state.data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() + ) + state.layout = { + "yaxis": {"title": "Revenue (USD)"}, + "title": f"Sales by State for {state.selected_category}", + } +``` + +## State + +Taipy uses a `state` object to store the variables per client. +The syntax to update a variable will always be `state.variable = new_value`. + +State holds the value of all the variables used in the user interface for one specific connection. + +Modifying `state.data` will update data for one specific user, without modifying `state.data` for other users +or the global `data` variable. You can test this by opening the application in a separate incognito window. \ No newline at end of file diff --git a/docs/tutorials/articles/sales_dashboard/step_02/images/filters.png b/docs/tutorials/articles/sales_dashboard/step_02/images/filters.png new file mode 100644 index 000000000..1b19ffe00 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_02/images/filters.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_02/images/layout.png b/docs/tutorials/articles/sales_dashboard/step_02/images/layout.png new file mode 100644 index 000000000..6d2397039 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_02/images/layout.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_02/images/styling_app.png b/docs/tutorials/articles/sales_dashboard/step_02/images/styling_app.png new file mode 100644 index 000000000..b6f32eee7 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_02/images/styling_app.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_02/step_02.md b/docs/tutorials/articles/sales_dashboard/step_02/step_02.md new file mode 100644 index 000000000..fe448dda1 --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/step_02/step_02.md @@ -0,0 +1,266 @@ +--- +hide: + - toc +--- + +The full code for this step is available +[here](https://github.com/Avaiga/taipy-course-gui/tree/develop/3_styling){: .tp-btn target='blank' } + +This step will be about styling the application. We will add more filters, layout the visual element and +regroup them in parts. + +![Styling Application](images/styling_app.png){ width=90% : .tp-image-border } + +We can still use the same code as the previous step, but let's recreate the page from scratch: + +=== "Python" + ```python + with tgb.Page() as page: + with tgb.part(class_name="container"): + tgb.text("# Sales by **State**", mode="md") + ``` +=== "Markdown" + ```python + page = """ + <|part|class_name=container| + # Sales by **State** + |> + """ + ``` + +[Part](../../../../refmans/gui/viselements/generic/part.md) allows you to group and style visual elements together. +Here, the [container](../../../../userman/gui/styling/stylekit.md) class is a predefined style +that will limit the width of visual elements contained in the part. + +[Text](../../../../refmans/gui/viselements/generic/text.md) is a simple text visual element. Here we use the +Markdown (md) mode to display a title and make the word "State". We can also color bold text in orange +by using CSS. Create a new CSS file with the same name as the Python file and add the following code: + +```css +strong, +b { + font-weight: bold; + color: var(--color-primary); +} +``` + +You can also connect visual elements to lambda functions. +For example, the following text visual element updates the displayed total sales when the data changes: + +```python +tgb.text(value=lambda data: f"Total Sales: {data['Sales'].sum():,.2f}") +``` + +Let's now add a new container for the filters: + +=== "Python" + ```python + with tgb.part(class_name="card"): + with tgb.layout(columns="1 2 1"): + with tgb.part(): + tgb.text("Filter **From**", mode="md") + with tgb.part(): + tgb.text("Filter Product **Category**", mode="md") + with tgb.part(class_name="text-center"): + tgb.button( + "Apply", + ) + ``` +=== "Markdown" + ``` + <|part|class_name=card| + <|layout|columns=1 2 1| + Filter **From** + + Filter Product **Category** + + <|part|class_name=text-center| + <|Apply|button|> + |> + |> + |> + ``` + + +[Card](../../../../userman/gui/styling/stylekit.md) is a predefined style that will regroup visual elements in +a white box. [layout](../../../../refmans/gui/viselements/generic/layout.md) allows you to create columns +for visual elements. Here we create 3 columns with a ratio of 1:2:1, the second column will be twice as wide as the +first and last columns. The contents of each column then needs to be separated in parts. + +![Layout](images/layout.png){ width=90% : .tp-image-border } + +We can now add [date selectors](../../../../refmans/gui/viselements/generic/date.md), +[selectors](../../../../refmans/gui/viselements/generic/selector.md) and a +[button](../../../../refmans/gui/viselements/generic/button.md) to apply the filters: + + +=== "Python" + ```python + with tgb.part(class_name="card"): + with tgb.layout(columns="1 2 1"): + with tgb.part(): + tgb.text("Filter **From**", mode="md") + tgb.date("{start_date}") + tgb.text("To") + tgb.date("{end_date}") + with tgb.part(): + tgb.text("Filter Product **Category**", mode="md") + tgb.selector( + value="{selected_category}", + lov="{categories}", + on_change=change_category, + dropdown=True, + ) + tgb.text("Filter Product **Subcategory**", mode="md") + tgb.selector( + value="{selected_subcategory}", + lov="{subcategories}", + dropdown=True, + ) + with tgb.part(class_name="text-center"): + tgb.button( + "Apply", + class_name="plain apply_button", + on_action=apply_changes, + ) + ``` +=== "Markdown" + ``` + <|part|class_name=card| + <|layout|columns=1 2 1| + Filter **From** + <|{start_date}|date|> + To + <|{end_date}|date|> + + Filter Product **Category** + <|{selected_category}|selector|lov={categories}|on_change=change_category|dropdown=True|> + Filter Product **Subcategory** + <|{selected_subcategory}|selector|lov={subcategories}|dropdown=True|> + + <|Apply|button|class_name=plain apply_button|on_action=apply_changes|> + |> + |> + ``` + +You'll notice we converted our selectors to dropdowns by setting the `dropdown` property to `True`. +We also applied styling to the button: `plain` is a predefined style that colors the button in orange. +Predefined styles are available in visual elements documentation. +Check out the styling part of [button](../../../../refmans/gui/viselements/generic/button.md/). +`apply_button` is a custom style that we can add using our CSS file: + +```css +.apply_button { + margin-top: 158px; +} +``` + +This will add a margin to the top of the button to align it with the filters. +We can also add properties to all Taipy buttons by applying properties to the `taipy-button` class +(You can find these class names by inspecting the page on a visual element) + +```css +.taipy-button { + width: 60% +} +``` + +![Filters](images/filters.png){ width=90% : .tp-image-border } + +We can now add the chart and the table: + + +=== "Python" + ```python + tgb.html("br") + tgb.chart( + data="{chart_data}", + x="State", + y="Sales", + type="bar", + layout="{layout}", + ) + tgb.html("br") + tgb.table(data="{data}") + + Gui(page=page).run(title="Sales", dark_mode=False, debug=True) + ``` +=== "Markdown" + ```python + page = """ + ... +
+ <|{chart_data}|chart|x=State|y=Sales|type=bar|layout={layout}|> +
+ <|{data}|table|> + """ + + Gui(page=page).run(title="Sales", dark_mode=False, debug=True) + ``` + +We use `tgb.html("br")` to add a line break and create space between elements. + +The last thing we need is to initialize the new variables and create the callback +function to apply the filter: + +```python +data = pd.read_csv("data.csv") +chart_data = ( + data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() +) + +start_date = "2015-01-01" +start_date = pd.to_datetime(start_date) +end_date = "2018-12-31" +end_date = pd.to_datetime(end_date) + +categories = list(data["Category"].unique()) +selected_category = "Furniture" + +selected_subcategory = "Bookcases" +subcategories = list( + data[data["Category"] == selected_category]["Sub-Category"].unique() +) + +layout = {"yaxis": {"title": "Revenue (USD)"}, "title": "Sales by State"} + + +def change_category(state): + state.subcategories = list( + data[data["Category"] == state.selected_category]["Sub-Category"].unique() + ) + state.selected_subcategory = state.subcategories[0] + + +def apply_changes(state): + new_data = data[ + ( + pd.to_datetime(data["Order Date"], format="%d/%m/%Y") + >= pd.to_datetime(state.start_date) + ) + & ( + pd.to_datetime(data["Order Date"], format="%d/%m/%Y") + <= pd.to_datetime(state.end_date) + ) + ] + new_data = new_data[new_data["Category"] == state.selected_category] + new_data = new_data[new_data["Sub-Category"] == state.selected_subcategory] + state.data = new_data + state.chart_data = ( + state.data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() + ) + state.layout = { + "yaxis": {"title": "Revenue (USD)"}, + "title": f"Sales by State for {state.selected_category} - {state.selected_subcategory}", + } +``` + +You can learn more about styling [here](../../../../userman/gui/styling/index.md). \ No newline at end of file diff --git a/docs/tutorials/articles/sales_dashboard/step_03/images/map.png b/docs/tutorials/articles/sales_dashboard/step_03/images/map.png new file mode 100644 index 000000000..bed0ac8a4 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_03/images/map.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_03/images/plotly_map.png b/docs/tutorials/articles/sales_dashboard/step_03/images/plotly_map.png new file mode 100644 index 000000000..cae76d514 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_03/images/plotly_map.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_03/step_03.md b/docs/tutorials/articles/sales_dashboard/step_03/step_03.md new file mode 100644 index 000000000..bbcfdf862 --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/step_03/step_03.md @@ -0,0 +1,86 @@ +--- +hide: + - toc +--- + +The full code for this step is available +[here](https://github.com/Avaiga/taipy-course-gui/tree/develop/4_charts){: .tp-btn target='blank' } + +In this part we will embed a Plotly map figure in our application. + +![Map embedded in application](images/map.png){ width=90% : .tp-image-border } + +For this purpose, we will use the `generate_map` function defined +[here](https://github.com/Avaiga/taipy-course-gui/blob/develop/4_charts/chart.py) +to return a Plotly map figure. + +![Plotly Map](images/plotly_map.png){ width=50% : .tp-image-border } + +Using the same code as the previous steps, we can import the function and initialize the chart: + +```python +from chart import generate_map + +data = pd.read_csv("data.csv") +map_fig = generate_map(data) +``` + +We can now add the map to the page by replacing our previous chart and table with: + +=== "Python" + ```python + tgb.html("br") + with tgb.layout(columns="2 3"): + tgb.chart( + data="{chart_data}", + x="State", + y="Sales", + type="bar", + layout="{layout}", + ) + tgb.chart(figure="{map_fig}") + tgb.html("br") + tgb.table(data="{data}") + ``` +=== "Markdown" + ``` +
+ <|layout|columns=2 3| + <|{chart_data}|chart|x=State|y=Sales|type=bar|layout={layout}|> + + <|chart|figure={map_fig}|> + |> +
+ <|{data}|table|> + ``` + +We should now update the callback function to refresh the map when filters are applied: + +```python +def apply_changes(state): + new_data = data[ + ( + pd.to_datetime(data["Order Date"], format="%d/%m/%Y") + >= pd.to_datetime(state.start_date) + ) + & ( + pd.to_datetime(data["Order Date"], format="%d/%m/%Y") + <= pd.to_datetime(state.end_date) + ) + ] + new_data = new_data[new_data["Category"] == state.selected_category] + new_data = new_data[new_data["Sub-Category"] == state.selected_subcategory] + state.data = new_data + state.chart_data = ( + state.data.groupby("State")["Sales"] + .sum() + .sort_values(ascending=False) + .head(10) + .reset_index() + ) + state.layout = { + "yaxis": {"title": "Revenue (USD)"}, + "title": f"Sales by State for {state.selected_category} - {state.selected_subcategory}", + } + state.map_fig = generate_map(state.data) +``` \ No newline at end of file diff --git a/docs/tutorials/articles/sales_dashboard/step_04/images/menu.png b/docs/tutorials/articles/sales_dashboard/step_04/images/menu.png new file mode 100644 index 000000000..898aa1624 Binary files /dev/null and b/docs/tutorials/articles/sales_dashboard/step_04/images/menu.png differ diff --git a/docs/tutorials/articles/sales_dashboard/step_04/step_04.md b/docs/tutorials/articles/sales_dashboard/step_04/step_04.md new file mode 100644 index 000000000..bb6f76eea --- /dev/null +++ b/docs/tutorials/articles/sales_dashboard/step_04/step_04.md @@ -0,0 +1,105 @@ +--- +hide: + - toc +--- + +The full code for this step is available +[here](https://github.com/Avaiga/taipy-course-gui/tree/develop/5_multipage){: .tp-btn target='blank' } + +In this part we will add a second page to our application and a sidebar menu to navigate between pages. + +![Sidebar Menu](images/menu.png){ width=90% : .tp-image-border } + +Using the same code as the previous steps, let's add a root page which will contain the sidebar menu +and appear on all pages: + +=== "Python" + ```python + from taipy.gui import Gui, Icon, navigate + + with tgb.Page() as root_page: + tgb.menu( + label="Menu", + lov=[ + ("page1", Icon("images/map.png", "Sales")), + ("page2", Icon("images/person.png", "Account")), + ], + on_action=menu_option_selected, + ) + ``` +=== "Markdown" + ```python + from taipy.gui import Gui, Icon, navigate + + menu_lov = [ + ("page1", Icon("images/map.png", "Sales")), + ("page2", Icon("images/person.png", "Account")), + ] + + root_page = """ + <|Menu|menu|lov={menu_lov}|on_action=menu_option_selected|> + """ + ``` + +Here we use the [menu](../../../../refmans/gui/viselements/generic/menu.md) visual element to create a sidebar menu. +`menu` take a label which will be displayed at the top of the menu, a list of values (lov) which need the following format: +`(page_url, Icon(icon_image_path, page_name))`, and a callback function to call when an option is selected. + +The images used are available [here](https://github.com/Avaiga/taipy-course-gui/tree/develop/5_multipage/images) + +Let's add a second page: + +=== "Python" + ```python + with tgb.Page() as page_2: + tgb.text("# Account **Management**", mode="md") + tgb.button("Logout", class_name="plain") + ``` +=== "Markdown" + ```python + page_2 = """ + # Account **Management** + <|Logout|button|class_name=plain|> + """ + ``` + +And run the application: + +```python +pages = {"/": root_page, "page1": page, "page2": page_2} + +if __name__ == "__main__": + Gui(pages=pages).run(title="Sales", dark_mode=False, debug=True) +``` + +For multipage applications, we use `pages` instead of `page` as the argument of the `Gui` class. + +`pages` is a dictionary where the key is the page URL and the value is the page object. + +We now need to create a callback function to navigate between pages: + +```python +def menu_option_selected(state, action, info): + page = info["args"][0] + navigate(state, to=page) +``` + +The callback signature for `menu` visual element is described [here](../../../../refmans/gui/viselements/generic/menu.md). +Here we extract the page URL from the `info` dictionary and use the `navigate` function to change the page. + +You can learn more about multi-page navigation [here](../../../../userman/gui/pages/navigate/index.md) + +Lastly, we can add some CSS to resize the images and make the login button smaller: + +```css +.login-button { + width: 20% !important; +} + +.MuiAvatar-img { + width: 70%; + height: 70%; +} +``` + +You can learn more about multi-page navigation [here](../../../../userman/gui/pages/navigate/index.md) \ No newline at end of file diff --git a/docs/tutorials/getting_started/index.md_template b/docs/tutorials/getting_started/index.md_template index 0e471a905..2905ee4b6 100644 --- a/docs/tutorials/getting_started/index.md_template +++ b/docs/tutorials/getting_started/index.md_template @@ -162,10 +162,10 @@ For more realistic and advanced use cases, check out our
- +
-

Understanding GUI

+

Creating a Sales Dashboard

diff --git a/docs/tutorials/index.md_template b/docs/tutorials/index.md_template index 190b9e2ea..1c72fcd97 100644 --- a/docs/tutorials/index.md_template +++ b/docs/tutorials/index.md_template @@ -81,15 +81,15 @@ Learn how to build Taipy applications through tutorials! ## Favorites

    -
  • - +
  • +
    - +
    -

    Understanding GUI

    +

    Creating a Sales Dashboard

    - Understand basic knowledge of Taipy GUI creating of a multi-page NLP application. + Understand basic knowledge of Taipy by creating of a multi-page sales dashboard.

    diff --git a/mkdocs.yml_template b/mkdocs.yml_template index e27b82ec2..b0ae9fcad 100644 --- a/mkdocs.yml_template +++ b/mkdocs.yml_template @@ -21,15 +21,12 @@ nav: - "Fundamentals": - "Fundamentals": tutorials/fundamentals/index.md - "Getting Started": tutorials/getting_started/index.md - - "Understanding GUI": - - "Understanding GUI": tutorials/articles/understanding_gui/index.md - - "1 - First Web page": tutorials/articles/understanding_gui/step_01/step_01.md - - "2 - Visual elements": tutorials/articles/understanding_gui/step_02/step_02.md - - "3 - Interactions": tutorials/articles/understanding_gui/step_03/step_03.md - - "4 - Charts": tutorials/articles/understanding_gui/step_04/step_04.md - - "5 - Python expressions in properties": tutorials/articles/understanding_gui/step_05/step_05.md - - "6 - Page layouts": tutorials/articles/understanding_gui/step_06/step_06.md - - "7 - Multi-pages, navbars, and menus": tutorials/articles/understanding_gui/step_07/step_07.md + - "Creating a Sales Dashboard": + - "Creating a Sales Dashboard": tutorials/articles/sales_dashboard/index.md + - "1 - Visual Elements": tutorials/articles/sales_dashboard/step_01/step_01.md + - "2 - Styling": tutorials/articles/sales_dashboard/step_02/step_02.md + - "3 - Charts": tutorials/articles/sales_dashboard/step_03/step_03.md + - "4 - Multipage": tutorials/articles/sales_dashboard/step_04/step_04.md - "Scenario management overview": tutorials/articles/scenario_management_overview/index.md - "First Realistic application": - "First Realistic application": tutorials/articles/complete_application/index.md