# Complex Plotting

If you've chosen to have full-customization on the dashboard you're going to build, you can use our `charming_fork_zephyr` library and follow Apache Echarts' specification.&#x20;

Below is an example of how it can be implemented for the [creating-the-dashboard](https://docs.mercurydata.app/zephyr-full-customization/learn/custom-dashboards/creating-the-dashboard "mention") tutorial:

***

Again as before, we can have all the logic in a single function `build_dashboard`, which takes as argument the `aggregated_data` Map we just built. Let's break down an example:

```rust
use zephyr_sdk::{
    charting::{Dashboard, DashboardEntry, Table}
use charming_fork_zephyr::{component::{Axis, Grid, Legend, Title}, element::{AreaStyle, AxisType, Color, ColorStop, Tooltip, Trigger}, series::{Bar, Line}, Chart};


pub fn build_dashboard<'a>(env: &EnvClient, aggregated_data: HashMap<&'a str, HashMap<&'a str, AggregatedData>>, collaterals: &Vec<Collateral>, borroweds: &Vec<Borrowed>) -> Dashboard {
    let mut dashboard = Dashboard::new().title(&"Blend Porotocol Dashboard").description(&"Explore the Blend protocol's mainnet activity.");
    let categories: Vec<String> = vec!["Supply".into(), "Collateral".into(), "Borrowed".into()];

    for (pool, assets) in aggregated_data {
        let auctions_table = {
            let positions_count = get_from_ledger(env, &pool);
            let table = Table::new().columns(vec!["count".into()]).row(vec![positions_count.to_string()]);
            DashboardEntry::new().title("Current Unique Users With Positions").table(table)
        };

        dashboard = dashboard.entry(auctions_table);

        let val = get_from_instance(env, pool, "Name");
        let ScVal::String(string) = val else {panic!()};
        let pool = string.to_utf8_string().unwrap();
        
        env.log().debug("Iterating over data", None);

        for (asset, data) in assets {
            let meta: StellarAssetContractMetadata = env.from_scval(&get_from_instance(env, asset, "METADATA"));
//            let asset = soroban_string_to_string(env, meta.name);
            let denom = soroban_string_to_string(env, meta.symbol);
            let asset = denom.clone();


            let bar = {
                let chart = Chart::new().legend(Legend::new().show(true).left("150px").top("3%")).tooltip(Tooltip::new().trigger(Trigger::Axis))
                .x_axis(Axis::new().type_(AxisType::Category).data(categories.clone()))
                .y_axis(Axis::new().type_(AxisType::Value)).series(Bar::new()
                .name(format!("Pool: {}, Asset {}", pool, asset))
                .data(vec![data.total_supply as i64 / STROOP as i64, data.total_collateral as i64 / STROOP as i64, data.total_borrowed as i64 / STROOP as i64]));

                DashboardEntry::new().title("Distribution all time").chart(chart)
            };
            
            dashboard = dashboard.entry(bar);
      }
      
      dashboard
}
```

Be aware that to build the charts, we need to import some types from the Zephyr SDK and Charmig. Then we create the dashboard calling `Dashboard::new()`. As you can see we can set a title and a description for the dashboard.&#x20;

Now we start iterating through our map for each pool and asset:

### Building a Table

The first element of our dashboard is going to be a table stating for each liquidity pool the current unique users with a position in it.&#x20;

```rust
for (pool, assets) in aggregated_data {
        let auctions_table = {
            let positions_count = get_from_ledger(env, &pool);
            let table = Table::new().columns(vec!["count".into()]).row(vec![positions_count.to_string()]);
            DashboardEntry::new().title("Current Unique Users With Positions").table(table)
        };

        dashboard = dashboard.entry(auctions_table);
}
```

For each pool in our `aggregated_data` Map, we count how many unique users’ positions there are with the `get_from_ledger(env, &pool)` method, we create a table with `Table::new()` and define the column `count`, while the row will be the count. To add the table to the dashboard, we create a new Dashboard Entry with `DashboardEntry::new()`, set its title, and insert our table. Finally, we can add our entry (the table) to the previously created dashboard.

### Building a Chart

Let's look at an example of how to plot a chart. Specifically, we will create multiple bar charts showing the distribution of supply, collateral, and borrowed amounts over time for each pool and asset. The chart can be defined as follows:

```rust
for (asset, data) in assets {
            let meta: StellarAssetContractMetadata = env.from_scval(&get_from_instance(env, asset, "METADATA"));
            let denom = soroban_string_to_string(env, meta.symbol);
            let asset = denom.clone();


            let bar = {
                let chart = Chart::new().legend(Legend::new().show(true).left("150px").top("3%")).tooltip(Tooltip::new().trigger(Trigger::Axis))
                .x_axis(Axis::new().type_(AxisType::Category).data(categories.clone()))
                .y_axis(Axis::new().type_(AxisType::Value)).series(Bar::new()
                .name(format!("Pool: {}, Asset {}", pool, asset))
                .data(vec![data.total_supply as i64 / STROOP as i64, data.total_collateral as i64 / STROOP as i64, data.total_borrowed as i64 / STROOP as i64]));

                DashboardEntry::new().title("Distribution all time").chart(chart)
            };
```

For each asset and each pool, we obtain the asset name as a string (from a Soroban String—more details in [Working with Data](https://docs.mercurydata.app/zephyr-full-customization/learn/custom-dashboards/broken-reference)). We then create the chart using `Chart::new()`, define its dimensions and basic parameters, and set up the axes with `.x_axis(Axis::new())` and `.y_axis(Axis::new())`. On the x-axis, we want to display the categories: supply, collateral, and borrowed amounts. We specify that the axis type will be 'Category' using `.type_(AxisType::Category)`, and set our categories with `.data(categories.clone())`, defined as follows:

```rust
let categories: Vec<String> = vec!["Supply".into(), "Collateral".into(), "Borrowed".into()];
```

On y instead, we will have the values of the respective categories `.type_(AxisType::Value))` expressed in a bar chart: `.series(Bar::new())`.&#x20;

We then use `.name()` to specify the chart's name, including the specific pool and asset being plotted. Finally, we provide the data to be plotted using `.data()`, accessing the `total_supply`, `total_collateral`, and `total_borrowed` fields of the `AggregatedData` instances. Note that previously, we only aggregated data for `total_supply` (refer to the complete example for details on other aggregations).&#x20;

As before, we create a new entry with `DashboardEntry::new()`, set its title, and add the entry to the dashboard element. Now we are all set and set `dashboard` as the return of our function.&#x20;

***

To continue the tutorial, go back to [creating-the-dashboard](https://docs.mercurydata.app/zephyr-full-customization/learn/custom-dashboards/creating-the-dashboard "mention").
