diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/README.md b/rio/snippets/snippet-files/project-template-Crypto Dashboard/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/__init__.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/assets/cryptos.csv b/rio/snippets/snippet-files/project-template-Crypto Dashboard/assets/cryptos.csv
new file mode 100644
index 00000000..6dbc7edc
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/assets/cryptos.csv
@@ -0,0 +1,181 @@
+date,bitcoin,litecoin,ethereum
+2024-03-08 12:00:00,67599.0,87.74,3948.76
+2024-03-08 16:00:00,68263.0,87.6,3938.98
+2024-03-08 20:00:00,68842.0,86.95,3924.4
+2024-03-09 00:00:00,68315.0,88.36,3893.61
+2024-03-09 04:00:00,68412.0,88.97,3936.56
+2024-03-09 08:00:00,68641.0,88.96,3940.13
+2024-03-09 12:00:00,68389.0,88.0,3918.71
+2024-03-09 16:00:00,68436.0,89.93,3923.73
+2024-03-09 20:00:00,68337.0,88.9,3891.06
+2024-03-10 00:00:00,68508.0,90.95,3916.04
+2024-03-10 04:00:00,69159.0,89.48,3941.75
+2024-03-10 08:00:00,69503.0,89.2,3953.62
+2024-03-10 12:00:00,69752.0,88.79,3940.61
+2024-03-10 16:00:00,69366.0,87.78,3899.33
+2024-03-10 20:00:00,69523.0,88.38,3907.77
+2024-03-11 00:00:00,69076.0,87.52,3887.47
+2024-03-11 04:00:00,68570.0,86.75,3862.07
+2024-03-11 08:00:00,71280.0,90.65,4004.27
+2024-03-11 12:00:00,72115.0,94.93,4046.45
+2024-03-11 16:00:00,72226.0,102.02,4030.35
+2024-03-11 20:00:00,72017.0,104.71,4035.06
+2024-03-12 00:00:00,72131.0,103.82,4070.6
+2024-03-12 04:00:00,71593.0,99.12,4036.72
+2024-03-12 08:00:00,72187.0,98.91,4028.14
+2024-03-12 12:00:00,72089.0,97.66,4023.2
+2024-03-12 16:00:00,71862.0,96.77,3992.88
+2024-03-12 20:00:00,71437.0,97.94,3980.26
+2024-03-13 00:00:00,71467.0,97.44,3978.69
+2024-03-13 04:00:00,72050.0,98.17,4042.21
+2024-03-13 08:00:00,72908.0,98.27,4043.54
+2024-03-13 12:00:00,73107.0,97.48,4049.14
+2024-03-13 16:00:00,72569.0,95.74,3958.16
+2024-03-13 20:00:00,73111.0,96.38,3998.11
+2024-03-14 00:00:00,73098.0,97.21,4007.91
+2024-03-14 04:00:00,73286.0,95.97,3997.86
+2024-03-14 08:00:00,73331.0,95.52,3970.7
+2024-03-14 12:00:00,72876.0,95.85,3954.82
+2024-03-14 16:00:00,70916.0,93.94,3854.01
+2024-03-14 20:00:00,69740.0,91.78,3796.04
+2024-03-15 00:00:00,71420.0,94.04,3879.04
+2024-03-15 04:00:00,68664.0,89.91,3732.7
+2024-03-15 08:00:00,68629.0,89.76,3760.8
+2024-03-15 12:00:00,67677.0,87.53,3680.82
+2024-03-15 16:00:00,68239.0,88.42,3693.14
+2024-03-15 20:00:00,68583.0,88.74,3698.82
+2024-03-16 00:00:00,69498.0,89.76,3738.38
+2024-03-16 04:00:00,69291.0,89.51,3737.05
+2024-03-16 08:00:00,69319.0,89.99,3733.34
+2024-03-16 12:00:00,68363.0,89.76,3697.12
+2024-03-16 16:00:00,68440.0,88.16,3679.65
+2024-03-16 20:00:00,67088.0,84.97,3599.44
+2024-03-17 00:00:00,65292.0,83.99,3514.22
+2024-03-17 04:00:00,66510.0,86.17,3571.89
+2024-03-17 08:00:00,65608.0,82.61,3470.38
+2024-03-17 12:00:00,66914.0,85.94,3576.46
+2024-03-17 16:00:00,68035.0,86.48,3635.12
+2024-03-17 20:00:00,68388.0,86.41,3649.0
+2024-03-18 00:00:00,68425.0,86.0,3643.28
+2024-03-18 04:00:00,67990.0,85.31,3602.72
+2024-03-18 08:00:00,68234.0,85.67,3628.4
+2024-03-18 12:00:00,68166.0,84.97,3596.86
+2024-03-18 16:00:00,67379.0,82.97,3524.82
+2024-03-18 20:00:00,66965.0,82.18,3483.63
+2024-03-19 00:00:00,67709.0,87.45,3525.89
+2024-03-19 04:00:00,66034.0,83.51,3424.52
+2024-03-19 08:00:00,64569.0,80.54,3356.92
+2024-03-19 12:00:00,63098.0,79.22,3264.74
+2024-03-19 16:00:00,63858.0,80.88,3302.27
+2024-03-19 20:00:00,64449.0,81.12,3326.83
+2024-03-20 00:00:00,62133.0,78.69,3171.29
+2024-03-20 04:00:00,62290.0,79.4,3197.54
+2024-03-20 08:00:00,62932.0,81.55,3214.48
+2024-03-20 12:00:00,63262.0,81.1,3272.76
+2024-03-20 16:00:00,63682.0,80.73,3312.18
+2024-03-20 20:00:00,65883.0,83.86,3382.99
+2024-03-21 00:00:00,67819.0,84.8,3515.69
+2024-03-21 04:00:00,66484.0,85.01,3499.07
+2024-03-21 08:00:00,66964.0,84.9,3513.59
+2024-03-21 12:00:00,67065.0,85.36,3530.98
+2024-03-21 16:00:00,66494.0,86.19,3496.02
+2024-03-21 20:00:00,65380.0,86.13,3442.28
+2024-03-22 00:00:00,65536.0,85.81,3493.43
+2024-03-22 04:00:00,65942.0,85.58,3512.73
+2024-03-22 08:00:00,66131.0,85.3,3513.87
+2024-03-22 12:00:00,64260.0,82.78,3410.95
+2024-03-22 16:00:00,64084.0,83.02,3347.99
+2024-03-22 20:00:00,64153.0,83.24,3356.17
+2024-03-23 00:00:00,63509.0,83.16,3322.89
+2024-03-23 04:00:00,63516.0,83.98,3301.83
+2024-03-23 08:00:00,64518.0,84.21,3363.15
+2024-03-23 12:00:00,64555.0,86.15,3359.43
+2024-03-23 16:00:00,65080.0,86.46,3400.15
+2024-03-23 20:00:00,64957.0,87.04,3394.56
+2024-03-24 00:00:00,64286.0,85.63,3353.37
+2024-03-24 04:00:00,64098.0,86.91,3318.68
+2024-03-24 08:00:00,64403.0,86.67,3334.59
+2024-03-24 12:00:00,65247.0,87.78,3382.24
+2024-03-24 16:00:00,65627.0,89.54,3398.86
+2024-03-24 20:00:00,65892.0,89.44,3386.51
+2024-03-25 00:00:00,67311.0,89.79,3454.26
+2024-03-25 04:00:00,66984.0,89.79,3442.76
+2024-03-25 08:00:00,66871.0,89.49,3452.12
+2024-03-25 12:00:00,66827.0,88.77,3435.6
+2024-03-25 16:00:00,69919.0,91.47,3588.17
+2024-03-25 20:00:00,70950.0,91.33,3637.92
+2024-03-26 00:00:00,69939.0,90.23,3588.49
+2024-03-26 04:00:00,70497.0,91.01,3634.89
+2024-03-26 08:00:00,70674.0,91.48,3632.06
+2024-03-26 12:00:00,70719.0,91.32,3635.26
+2024-03-26 16:00:00,70240.0,88.76,3592.04
+2024-03-26 20:00:00,69732.0,95.53,3566.54
+2024-03-27 00:00:00,70082.0,95.79,3591.55
+2024-03-27 04:00:00,70407.0,97.13,3607.21
+2024-03-27 08:00:00,69697.0,95.66,3565.21
+2024-03-27 12:00:00,70166.0,97.34,3582.5
+2024-03-27 16:00:00,69014.0,94.04,3519.94
+2024-03-27 20:00:00,68689.0,93.65,3490.66
+2024-03-28 00:00:00,69436.0,93.68,3505.22
+2024-03-28 04:00:00,69192.0,95.37,3486.11
+2024-03-28 08:00:00,70319.0,96.22,3568.91
+2024-03-28 12:00:00,70643.0,94.8,3577.23
+2024-03-28 16:00:00,71434.0,94.99,3587.31
+2024-03-28 20:00:00,70858.0,93.86,3568.63
+2024-03-29 00:00:00,70710.0,94.16,3560.26
+2024-03-29 04:00:00,70429.0,94.64,3559.74
+2024-03-29 08:00:00,69803.0,93.44,3520.88
+2024-03-29 12:00:00,70179.0,102.77,3548.3
+2024-03-29 16:00:00,69279.0,104.78,3497.78
+2024-03-29 20:00:00,69618.0,105.73,3497.88
+2024-03-30 00:00:00,69919.0,109.27,3516.1
+2024-03-30 04:00:00,69865.0,105.25,3492.65
+2024-03-30 08:00:00,69960.0,104.69,3500.01
+2024-03-30 12:00:00,70203.0,103.47,3560.29
+2024-03-30 16:00:00,70043.0,102.35,3539.64
+2024-03-30 20:00:00,69939.0,102.24,3509.25
+2024-03-31 00:00:00,69702.0,102.9,3507.66
+2024-03-31 04:00:00,69939.0,102.83,3536.77
+2024-03-31 08:00:00,70303.0,102.61,3626.24
+2024-03-31 12:00:00,70389.0,102.22,3608.23
+2024-03-31 16:00:00,70364.0,102.81,3619.01
+2024-03-31 20:00:00,71070.0,104.4,3638.99
+2024-04-01 00:00:00,71247.0,105.14,3644.77
+2024-04-01 04:00:00,70631.0,110.3,3611.68
+2024-04-01 08:00:00,69712.0,108.78,3549.56
+2024-04-01 12:00:00,69535.0,104.47,3540.84
+2024-04-01 16:00:00,68524.0,98.75,3477.05
+2024-04-01 20:00:00,69433.0,99.36,3478.01
+2024-04-02 00:00:00,69786.0,99.57,3508.25
+2024-04-02 04:00:00,66811.0,95.75,3368.96
+2024-04-02 08:00:00,66583.0,98.74,3372.58
+2024-04-02 12:00:00,65429.0,102.03,3305.06
+2024-04-02 16:00:00,65095.0,106.09,3255.86
+2024-04-02 20:00:00,66122.0,107.54,3276.87
+2024-04-03 00:00:00,65440.0,106.93,3274.9
+2024-04-03 04:00:00,66252.0,103.1,3322.29
+2024-04-03 08:00:00,66255.0,101.75,3309.57
+2024-04-03 12:00:00,66185.0,100.53,3322.99
+2024-04-03 16:00:00,65925.0,98.79,3328.94
+2024-04-03 20:00:00,65895.0,97.69,3321.85
+2024-04-04 00:00:00,66124.0,98.78,3316.68
+2024-04-04 04:00:00,65660.0,97.86,3279.5
+2024-04-04 08:00:00,66056.0,101.3,3320.72
+2024-04-04 12:00:00,66407.0,100.3,3346.21
+2024-04-04 16:00:00,67805.0,99.65,3365.87
+2024-04-04 20:00:00,68678.0,98.96,3370.51
+2024-04-05 00:00:00,68542.0,97.86,3332.09
+2024-04-05 04:00:00,67833.0,98.38,3307.82
+2024-04-05 08:00:00,66957.0,98.84,3284.9
+2024-04-05 12:00:00,66485.0,96.62,3249.98
+2024-04-05 16:00:00,67989.0,97.78,3322.74
+2024-04-05 20:00:00,67572.0,99.26,3323.64
+2024-04-06 00:00:00,67979.0,98.17,3320.28
+2024-04-06 04:00:00,67758.0,98.3,3333.11
+2024-04-06 08:00:00,68156.0,99.87,3338.67
+2024-04-06 12:00:00,67745.0,102.03,3335.87
+2024-04-06 16:00:00,68158.0,100.67,3340.78
+2024-04-06 20:00:00,68316.0,100.09,3348.47
+2024-04-07 00:00:00,69001.0,101.24,3362.84
+2024-04-07 04:00:00,69449.0,104.42,3391.29
+2024-04-07 08:00:00,69388.0,103.41,3389.81
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/__init__.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/__init__.py
new file mode 100644
index 00000000..8a152923
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/__init__.py
@@ -0,0 +1,3 @@
+from .balance_card import BalanceCard as BalanceCard
+from .crypto_card import CryptoCard as CryptoCard
+from .crypto_chart import CryptoChart as CryptoChart
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py
new file mode 100644
index 00000000..43909524
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py
@@ -0,0 +1,200 @@
+from typing import * # type:ignore
+
+#
+import pandas as pd
+import plotly.express as px
+
+import rio
+
+from .. import data_models
+
+#
+
+
+#
+class BalanceCard(rio.Component):
+ """
+ The MyBalance class is a component of a dashboard application, designed to handle and
+ display balance-related data.
+
+ The class provides methods to calculate total balance at a given index, calculate the
+ percentual difference in balance between the last and second last balances, and create
+ visual sections for the dashboard.
+ These sections include a balance section displaying the total balance and the percentual
+ difference in balance, and a bar chart section displaying a bar chart with a given color
+ and hidden axes.
+
+ The build method combines these sections into a single rio.Card component, creating a
+ complete balance component for the dashboard.
+
+ Attributes:
+ data: A pandas DataFrame containing the data for the coins in MY_COINS.
+ """
+
+ data: pd.DataFrame
+
+ def total_balance(self, idx: int) -> float:
+ """
+ Calculates the total balance for a given index.
+
+ This function iterates over the coins in MY_COINS, and for each coin,
+ it multiplies the coin's value by the value at the given index in the
+ data for that coin. It then adds these products to a total and returns
+ this total.
+
+ Args:
+ idx (int): The index at which to calculate the total balance.
+
+ Returns:
+ float: The total balance at the given index.
+ """
+
+ total = 0
+ for coin in data_models.MY_COINS:
+ total += data_models.MY_COINS[coin][0] * self.data[coin].iloc[idx]
+ return total
+
+ def percentual_differance_balance(self) -> float:
+ """
+ Calculates the percentual difference in balance between the last and
+ second last balances.
+
+ This function iterates over the coins in MY_COINS, and for each coin,
+ it multiplies the coin's value by the total balance at the last and
+ second last indices. It then calculates the percentual difference between
+ these two totals and returns this value.
+
+ Returns:
+ float: The percentual difference between the last and second last balances.
+ """
+
+ total_last = 0
+ total_second_last = 0
+ epsilon = 0.0000001
+
+ for coin in data_models.MY_COINS:
+ total_last += data_models.MY_COINS[coin][0] * self.total_balance(idx=-1)
+ total_second_last += data_models.MY_COINS[coin][0] * self.total_balance(
+ idx=-2
+ )
+
+ # epsilon ensures that the denominator is never zero
+ return ((total_last - total_second_last) / (total_second_last + epsilon)) * 100
+
+ def balance_section(self) -> rio.Component:
+ """
+ Creates a balance section for the dashboard.
+
+ This function creates a section that displays the total balance and the
+ percentual difference in balance. The total balance is displayed in bold,
+ and the percentual difference is displayed in green if it's positive and
+ in red if it's negative. The section is returned as a Column component
+ from the rio library.
+
+ Returns:
+ rio.Component: A Column component from the rio library, which includes
+ the total balance, the percentual difference in balance, and some
+ text and spacing elements.
+ """
+
+ return rio.Column(
+ rio.Text(
+ "My Balance",
+ style=rio.TextStyle(font_size=1.2, font_weight="bold"),
+ align_x=0,
+ ),
+ rio.Spacer(height=1),
+ rio.Text("Total Balance", style="dim", align_x=0),
+ rio.Row(
+ rio.Text(
+ f"{self.total_balance(idx=-1):,.2f} USD",
+ style=rio.TextStyle(font_size=1.2, font_weight="bold"),
+ align_x=0,
+ ),
+ rio.Text(
+ f"({self.percentual_differance_balance():.2f} %)",
+ style=rio.TextStyle(
+ fill=(
+ rio.Color.GREEN
+ if self.percentual_differance_balance() > 0
+ else rio.Color.RED
+ )
+ ),
+ ),
+ spacing=1,
+ ),
+ spacing=1,
+ align_y=1,
+ )
+
+ def bar_chart_section(self, name: str, color: str) -> rio.Component:
+ """
+ Creates a bar chart section for the dashboard.
+
+ This function creates a bar chart with the given color and hiden axes.
+ The function returns a Column component from the rio library, which includes
+ the Plot, the name of the section, and the total balance in USD.
+
+ Args:
+ name (str): The name of the section.
+ color (str): The color of the bars in the bar chart.
+
+ Returns:
+ rio.Component: A Column component from the rio library, which includes
+ the Plot, the name of the section, and the total balance in USD.
+ """
+
+ fig = px.bar(
+ data_models.BAR_CHART,
+ height=200,
+ width=200,
+ color_discrete_sequence=[color],
+ )
+ # hide and lock down axes
+ fig.update_xaxes(visible=False, fixedrange=True)
+ fig.update_yaxes(visible=False, fixedrange=True)
+ # remove facet/subplot labels
+ fig.update_layout(annotations=[], overwrite=True)
+
+ # strip down the rest of the plot
+ fig.update_layout(
+ showlegend=False,
+ margin=dict(t=10, l=10, b=10, r=10),
+ )
+
+ return rio.Column(
+ rio.Plot(
+ figure=fig,
+ height=5,
+ width=20,
+ background=self.session.theme.neutral_color,
+ ),
+ rio.Text(name, style="dim", align_x=0),
+ rio.Text(
+ f"{self.total_balance(idx=-1):,.2f} USD",
+ style=rio.TextStyle(font_size=1.2, font_weight="bold"),
+ justify="left",
+ ),
+ spacing=1,
+ align_y=1,
+ )
+
+ def build(self) -> rio.Component:
+ return rio.Card(
+ rio.Row(
+ # 1. Section
+ self.balance_section(),
+ rio.Separator(),
+ # 2. Section
+ self.bar_chart_section(name="Income", color="green"),
+ rio.Separator(),
+ self.bar_chart_section(name="Expenses", color="red"),
+ margin=1,
+ spacing=4,
+ align_x=0.5,
+ ),
+ height=13,
+ )
+
+
+#
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py
new file mode 100644
index 00000000..7f652f3d
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py
@@ -0,0 +1,152 @@
+from typing import * # type:ignore
+
+#
+import pandas as pd
+import plotly.express as px
+
+import rio
+
+#
+
+
+#
+class CryptoCard(rio.Component):
+ """
+ The CryptoCard class is a component of a dashboard application, designed to handle
+ and display cryptocurrency-related data. It uses the rio library to create
+ interactive dashboard components and pandas DataFrame to store cryptocurrency data.
+
+ The build method creates a rio.Card component that displays a line plot of the last
+ 50 data points of the cryptocurrency, the cryptocurrency's logo, the name and ticker
+ symbol of the cryptocurrency, the amount of the cryptocurrency, and the amount of
+ the cryptocurrency in USD. The layout of the card is a grid with 4 rows and 2 columns.
+
+ If there is no data available for the cryptocurrency, a message is printed to the console.
+
+
+ Attributes:
+ data: A pandas DataFrame that holds the cryptocurrency data.
+ coin: A string representing the name of the cryptocurrency.
+ coin_amount: A float representing the amount of the cryptocurrency.
+ coin_ticker: A string representing the ticker symbol of the cryptocurrency.
+ logo_url: A string representing the URL of the cryptocurrency's logo.
+ """
+
+ data: pd.DataFrame
+ coin: str
+ coin_amount: float
+ coin_ticker: str
+ color: str
+ logo_url: str
+
+ def build(self) -> rio.Component:
+ fig = px.line(
+ self.data[self.coin].iloc[-50:],
+ color_discrete_sequence=[self.color],
+ height=200,
+ width=200,
+ )
+
+ # hide and lock down axes
+ fig.update_xaxes(visible=False, fixedrange=True)
+ fig.update_yaxes(visible=False, fixedrange=True)
+
+ # remove facet/subplot labels
+ fig.update_layout(annotations=[], overwrite=True)
+
+ # strip down the rest of the plot
+ fig.update_layout(
+ showlegend=False,
+ margin=dict(t=10, l=10, b=10, r=10),
+ )
+
+ grid = rio.Grid(
+ column_spacing=0.5,
+ row_spacing=1,
+ margin=2,
+ )
+
+ # Create a grid layout for the card
+ # The grid will have 4 rows and 2 columns
+ # Because the width of the plot is bigger, the second column will be wider
+ # like shown below:
+
+ ############################################
+ # Icon | Plot #
+ # Icon | Plot #
+ # Coin Ticker | Coin Amount #
+ # Coin Name | Coin Amount in USD #
+ ############################################
+
+ # Image with grid height 2
+ grid.add(
+ rio.Image(
+ rio.URL(
+ self.logo_url
+ ), # logo_url = e.g. "https://cryptologos.cc/logos/bitcoin-btc-logo.svg?v=029"
+ height=2,
+ width=2,
+ align_y=0.5,
+ ),
+ row=0,
+ column=0,
+ )
+
+ # Plot with grid height 2
+ grid.add(
+ rio.Plot(
+ figure=fig,
+ corner_radius=0,
+ height=4,
+ background=self.session.theme.neutral_color,
+ ),
+ row=0,
+ column=1,
+ height=2,
+ )
+
+ # Text with coin name and grid height 1
+ grid.add(
+ rio.Text(
+ self.coin.capitalize(),
+ style=rio.TextStyle(font_size=1.2, font_weight="bold"),
+ align_x=0,
+ ),
+ row=2,
+ column=0,
+ )
+
+ # Text with coin amount and grid height 1
+ grid.add(
+ rio.Text(
+ f"{self.coin_amount:.6f} {self.coin_ticker}",
+ align_x=0,
+ style=rio.TextStyle(font_size=1.2, font_weight="bold"),
+ ),
+ row=2,
+ column=1,
+ )
+
+ # Text with coin ticker and grid height 1
+ grid.add(
+ rio.Row(
+ rio.Text(
+ self.coin_ticker,
+ ),
+ rio.Text(" / USD", style="dim"),
+ align_x=0,
+ ),
+ row=3,
+ column=0,
+ )
+ # Text with coin amount in USD and grid height 1
+ usd_amount = self.coin_amount * self.data[self.coin].iloc[-1]
+ grid.add(rio.Text(f"{usd_amount:,.2f} USD", align_x=0), row=3, column=1)
+
+ return rio.Card(
+ grid,
+ height=13,
+ )
+
+
+#
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py
new file mode 100644
index 00000000..f33aa446
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py
@@ -0,0 +1,106 @@
+from typing import * # type:ignore
+
+#
+import pandas as pd
+import plotly.express as px
+
+import rio
+
+from .. import data_models
+
+#
+
+
+#
+class CryptoChart(rio.Component):
+ data: pd.DataFrame
+ coin: str
+ logo_url: str = data_models.MY_COINS["bitcoin"][3]
+ color: str = data_models.MY_COINS["bitcoin"][2]
+
+ def on_change_coin(self, ev: rio.DropdownChangeEvent) -> None:
+ """
+ Handles the event of changing the selected coin.
+
+ This function updates the coin, color and logo_url attributes based on the selected coin.
+
+ Parameters:
+ ev (rio.DropdownChangeEvent): The event object containing the selected coin value.
+ """
+ self.coin = ev.value
+ self.color = data_models.MY_COINS[self.coin][2]
+ self.logo_url = data_models.MY_COINS[self.coin][3]
+
+ def build(self) -> rio.Component:
+ """
+ Creates a Card component with the selected coin's line plot, logo, name, and dropdown.
+
+ This function creates a line plot of the last 50 data points of the selected coin,
+ using the plotly express library. The plot is displayed in a Plot component from the
+ rio library. The Card component includes the coin's logo, name, and dropdown, as well
+ as the line plot.
+
+ Returns:
+ rio.Card: A Card component with the selected coin's line plot, logo, name, and dropdown.
+ See the layout below:
+
+ ############################################
+ # Logo | Coin Name | Dropdown #
+ # Plot #
+ ############################################
+ """
+
+ fig = px.line(
+ self.data[self.coin].iloc[-50:],
+ color_discrete_sequence=[self.color],
+ height=200,
+ width=200,
+ )
+
+ # Set x-axis labels to horizontal and remove labels
+ fig.update_xaxes(tickangle=0, title_text="")
+ fig.update_yaxes(title_text="")
+
+ # strip down the rest of the plot
+ fig.update_layout(
+ showlegend=False,
+ margin=dict(t=10, l=10, b=10, r=10),
+ )
+
+ return rio.Card(
+ rio.Column(
+ rio.Row(
+ rio.Image(
+ rio.URL(self.logo_url),
+ height=2,
+ width=2,
+ ),
+ rio.Text(
+ self.coin.capitalize(),
+ style=rio.TextStyle(font_size=1.2, font_weight="bold"),
+ ),
+ rio.Dropdown(
+ options={
+ value[1]: key for key, value in data_models.MY_COINS.items()
+ },
+ on_change=self.on_change_coin,
+ ),
+ spacing=1,
+ align_x=0.1,
+ ),
+ rio.Plot(
+ figure=fig,
+ corner_radius=0,
+ height=20,
+ width=10,
+ background=self.session.theme.neutral_color,
+ # margin=1,
+ ),
+ spacing=1,
+ align_y=0.5,
+ margin=2,
+ ),
+ )
+
+
+#
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py
new file mode 100644
index 00000000..dd824dfe
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py
@@ -0,0 +1,26 @@
+from typing import * # type:ignore
+
+import pandas as pd
+
+BAR_CHART: pd.DataFrame = pd.DataFrame([1, 2, 3, 4, 5, 6, 7], columns=["data"])
+
+MY_COINS: dict[str, Tuple[float, str, str, str]] = {
+ "bitcoin": (
+ 13.344546,
+ "BTC",
+ "#f7931a",
+ "https://cryptologos.cc/logos/bitcoin-btc-logo.svg?v=029",
+ ),
+ "litecoin": (
+ 40.21321,
+ "LTC",
+ "#365d99",
+ "https://cryptologos.cc/logos/litecoin-ltc-logo.svg?v=029",
+ ),
+ "ethereum": (
+ 4.239234,
+ "ETH",
+ "#14044d",
+ "https://cryptologos.cc/logos/ethereum-eth-logo.svg?v=029",
+ ),
+}
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/meta.json b/rio/snippets/snippet-files/project-template-Crypto Dashboard/meta.json
new file mode 100644
index 00000000..2435300b
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/meta.json
@@ -0,0 +1,10 @@
+{
+ "level": "intermediate",
+ "summary": "A simple Crypto Dashboard",
+ "dependencies": {
+ "numpy": ">=1.26.4",
+ "plotly": ">=5.20.0",
+ "pycoingecko": ">=3.1.0",
+ "pandas": ">=2.2.1"
+ }
+}
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/__init__.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/__init__.py
new file mode 100644
index 00000000..8adf1690
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/__init__.py
@@ -0,0 +1 @@
+from .dashboard_page import DashboardPage as DashboardPage
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py
new file mode 100644
index 00000000..a06e37c8
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py
@@ -0,0 +1,195 @@
+import dataclasses
+from pathlib import Path
+from typing import * # type:ignore
+
+import numpy as np
+
+#
+import pandas as pd
+from pycoingecko import CoinGeckoAPI
+
+import rio
+
+from .. import components as comps
+from .. import data_models
+
+#
+
+
+DIR = Path(__file__).parent.parent
+ASSET_DIR = DIR / "assets"
+
+CRYPTO_LIST: list[str] = ["bitcoin", "litecoin", "ethereum"]
+
+
+FETCH_DATA_FROM_API = True # Set to False to use local data
+
+
+#
+class DashboardPage(rio.Component):
+ """
+ The DashboardPage class is a component of a dashboard application, designed to display
+ a user's cryptocurrency balance and the historical data of three cryptocurrencies.
+ It uses the rio library to create interactive dashboard components and pandas DataFrame
+ to store cryptocurrency data.
+
+ The class contains a coin_data attribute that stores the historical data of three
+ cryptocurrencies: Bitcoin, Litecoin, and Ethereum. The data is fetched from the CoinGecko
+ API using the fetch_coin_data method. The class also contains an on_populate method that
+ fetches the data when the component is populated.
+
+ The build method creates a grid layout for the dashboard, containing a balance component,
+ three cryptocurrency components, and a cryptocurrency chart component. The layout is
+ structured as follows:
+
+ ###################################################
+ # BalanceCard | CryptoCard(BTC) #
+ # Crypto Chart | CryptoCard(LTC) #
+ # (Crypto Chart) | CryptoCard(ETH) #
+ ###################################################
+
+ Attributes:
+ coin_data: A pandas DataFrame that holds the historical data of three cryptocurrencies:
+ Bitcoin, Litecoin, and Ethereum.
+ """
+
+ coin_data: pd.DataFrame = dataclasses.field(
+ default=pd.DataFrame(
+ {
+ "date": np.zeros(5),
+ **{coin: np.zeros(5) for coin in CRYPTO_LIST},
+ }
+ )
+ )
+
+ @rio.event.on_populate
+ async def on_populate(self) -> None:
+ if FETCH_DATA_FROM_API is True:
+ self.coin_data = self._fetch_coin_data(CRYPTO_LIST)
+ else:
+ self.coin_data = self._read_csv(ASSET_DIR / "cryptos.csv")
+
+ def _read_csv(self, path: Path) -> pd.DataFrame:
+ """
+ Reads csv file and returns a pandas DataFrame.
+
+ Args:
+ path: A string representing the path to the csv file.
+
+ Returns:
+ pd.DataFrame: A pandas DataFrame containing the data from the csv file.
+ """
+ df = pd.read_csv(path)
+ df["date"] = pd.to_datetime(df["date"])
+ df.set_index("date", inplace=True)
+ return df
+
+ def _fetch_coin_data(
+ self, coin_names: list[str], vs_currency: str = "usd", days: str = "30"
+ ) -> pd.DataFrame:
+ """
+ This method fetches historical data for a list of cryptocurrencies from the
+ CoinGecko API and returns it as a pandas DataFrame.
+
+ The method creates an instance of the CoinGeckoAPI and iterates over the list
+ of coin_names. For each coin, it fetches the OHLC (Open, High, Low, Close) data,
+ converts it into a DataFrame, and processes it by converting the date to a
+ datetime object, setting the date as the index, and dropping the "open", "high",
+ and "low" columns. Each processed DataFrame is appended to a list.
+
+ Finally, the method concatenates all the DataFrames in the list into a single
+ DataFrame along the columns axis, sets the column names to the coin_names,
+ and returns the merged DataFrame.
+
+
+ Args:
+ coin_names: A list of strings representing the names of the
+ cryptocurrencies to fetch data for.
+ vs_currency: A string representing the currency to compare
+ against. Defaults to "usd".
+ days: A string representing the number of past days to fetch
+ data for. Defaults to "30".
+
+ Returns:
+ pd.DataFrame: A pandas DataFrame containing the historical (OHL)C data for the
+ specified cryptocurrencies.
+ """
+
+ df_list = []
+ cg = CoinGeckoAPI()
+
+ for i in range(len(coin_names)):
+ ohlc = cg.get_coin_ohlc_by_id(
+ id=coin_names[i], vs_currency=vs_currency, days=days
+ )
+ df = pd.DataFrame(ohlc, columns=["date", "open", "high", "low", "close"])
+ df["date"] = pd.to_datetime(df["date"], unit="ms")
+ df.set_index("date", inplace=True)
+ df = df.drop(columns=["open", "high", "low"])
+ df_list.append(df)
+
+ merged_df = pd.concat(df_list, axis=1)
+ merged_df.columns = coin_names
+
+ return merged_df
+
+ def build(self) -> rio.Component:
+ """
+ Creates a grid layout for the dashboard.
+
+ This function creates a grid layout for the dashboard, using the rio library.
+ The grid contains a balance component, three cryptocurrency components, and
+ a cryptocurrency chart component. The components are added to the grid with
+ specific row and column positions, widths, and heights.
+
+ Returns:
+ rio.Grid: A grid layout for the dashboard, containing the balance, cryptocurrency,
+ and cryptocurrency chart components. See the layout below:
+
+
+ ###################################################
+ # BalanceCard | CryptoCard(BTC) #
+ # Crypto Chart | CryptoCard(LTC) #
+ # (Crypto Chart) | CryptoCard(ETH) #
+ ###################################################
+ """
+
+ grid = rio.Grid(
+ column_spacing=2,
+ row_spacing=2,
+ align_y=0.5,
+ margin_left=20,
+ margin_right=5,
+ )
+
+ grid.add(comps.BalanceCard(data=self.coin_data), row=0, column=0, width=4)
+
+ # use loop to add cards
+
+ for i, coin in enumerate(CRYPTO_LIST):
+ grid.add(
+ comps.CryptoCard(
+ data=self.coin_data,
+ coin=coin,
+ coin_amount=data_models.MY_COINS[coin][0],
+ coin_ticker=data_models.MY_COINS[coin][1],
+ color=data_models.MY_COINS[coin][2],
+ logo_url=data_models.MY_COINS[coin][3],
+ ),
+ row=i,
+ column=4,
+ width=2,
+ )
+
+ grid.add(
+ comps.CryptoChart(data=self.coin_data, coin="bitcoin"),
+ row=1,
+ column=0,
+ width=4,
+ height=2,
+ )
+
+ return grid
+
+
+#
diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/thumbnail.svg b/rio/snippets/snippet-files/project-template-Crypto Dashboard/thumbnail.svg
new file mode 100644
index 00000000..48a4248e
--- /dev/null
+++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/thumbnail.svg
@@ -0,0 +1,97 @@
+
+