axiom-coding-agent-setup 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/.agents/skills/agent-browser/SKILL.md +55 -0
  2. package/.agents/skills/developing-with-streamlit/SKILL.md +220 -0
  3. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-chat-ui/SKILL.md +195 -0
  4. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/SKILL.md +233 -0
  5. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/packaged-components.md +212 -0
  6. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/state-sync.md +149 -0
  7. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/theme-css-variables.md +243 -0
  8. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/troubleshooting.md +121 -0
  9. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-dashboards/SKILL.md +147 -0
  10. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-multipage-apps/SKILL.md +218 -0
  11. package/.agents/skills/developing-with-streamlit/skills/choosing-streamlit-selection-widgets/SKILL.md +139 -0
  12. package/.agents/skills/developing-with-streamlit/skills/connecting-streamlit-to-snowflake/SKILL.md +188 -0
  13. package/.agents/skills/developing-with-streamlit/skills/creating-streamlit-themes/SKILL.md +486 -0
  14. package/.agents/skills/developing-with-streamlit/skills/displaying-streamlit-data/SKILL.md +199 -0
  15. package/.agents/skills/developing-with-streamlit/skills/improving-streamlit-design/SKILL.md +191 -0
  16. package/.agents/skills/developing-with-streamlit/skills/optimizing-streamlit-performance/SKILL.md +323 -0
  17. package/.agents/skills/developing-with-streamlit/skills/organizing-streamlit-code/SKILL.md +91 -0
  18. package/.agents/skills/developing-with-streamlit/skills/setting-up-streamlit-environment/SKILL.md +128 -0
  19. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-cli/SKILL.md +165 -0
  20. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-custom-components/SKILL.md +170 -0
  21. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-layouts/SKILL.md +229 -0
  22. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-markdown/SKILL.md +207 -0
  23. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-session-state/SKILL.md +144 -0
  24. package/.agents/skills/developing-with-streamlit/templates/.gitattributes +3 -0
  25. package/.agents/skills/developing-with-streamlit/templates/apps/README.md +188 -0
  26. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/pyproject.toml +12 -0
  27. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/streamlit_app.py +365 -0
  28. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/pyproject.toml +12 -0
  29. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/streamlit_app.py +461 -0
  30. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/.streamlit/secrets.toml.example +20 -0
  31. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/pyproject.toml +11 -0
  32. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/snowflake.yml +18 -0
  33. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/streamlit_app.py +527 -0
  34. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/pyproject.toml +12 -0
  35. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/streamlit_app.py +307 -0
  36. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/pyproject.toml +12 -0
  37. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/streamlit_app.py +426 -0
  38. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/.streamlit/secrets.toml.example +20 -0
  39. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/pyproject.toml +11 -0
  40. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/snowflake.yml +18 -0
  41. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/streamlit_app.py +463 -0
  42. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/pyproject.toml +12 -0
  43. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/streamlit_app.py +252 -0
  44. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/pyproject.toml +12 -0
  45. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/streamlit_app.py +342 -0
  46. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/.streamlit/secrets.toml.example +20 -0
  47. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/pyproject.toml +11 -0
  48. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/snowflake.yml +18 -0
  49. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/streamlit_app.py +384 -0
  50. package/.agents/skills/developing-with-streamlit/templates/themes/README.md +128 -0
  51. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/dracula.toml +39 -0
  52. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/github.toml +37 -0
  53. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/minimal.toml +39 -0
  54. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/nord.toml +39 -0
  55. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/snowflake.toml +42 -0
  56. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/solarized-light.toml +38 -0
  57. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/spotify.toml +34 -0
  58. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/stripe.toml +35 -0
  59. package/.agents/skills/developing-with-streamlit/templates/themes/_shared/streamlit_app.py +336 -0
  60. package/.agents/skills/developing-with-streamlit/templates/themes/_templates/pyproject.toml.tmpl +12 -0
  61. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/.streamlit/config.toml +39 -0
  62. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/pyproject.toml +10 -0
  63. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/streamlit_app.py +337 -0
  64. package/.agents/skills/developing-with-streamlit/templates/themes/github/.streamlit/config.toml +37 -0
  65. package/.agents/skills/developing-with-streamlit/templates/themes/github/pyproject.toml +10 -0
  66. package/.agents/skills/developing-with-streamlit/templates/themes/github/streamlit_app.py +337 -0
  67. package/.agents/skills/developing-with-streamlit/templates/themes/manage.py +332 -0
  68. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/.streamlit/config.toml +39 -0
  69. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/pyproject.toml +10 -0
  70. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/streamlit_app.py +337 -0
  71. package/.agents/skills/developing-with-streamlit/templates/themes/nord/.streamlit/config.toml +39 -0
  72. package/.agents/skills/developing-with-streamlit/templates/themes/nord/pyproject.toml +10 -0
  73. package/.agents/skills/developing-with-streamlit/templates/themes/nord/streamlit_app.py +337 -0
  74. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/.streamlit/config.toml +42 -0
  75. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/pyproject.toml +10 -0
  76. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/streamlit_app.py +337 -0
  77. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/.streamlit/config.toml +38 -0
  78. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/pyproject.toml +10 -0
  79. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/streamlit_app.py +337 -0
  80. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/.streamlit/config.toml +34 -0
  81. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/pyproject.toml +10 -0
  82. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/streamlit_app.py +337 -0
  83. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/.streamlit/config.toml +35 -0
  84. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/pyproject.toml +10 -0
  85. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/streamlit_app.py +337 -0
  86. package/.agents/skills/fastapi/SKILL.md +436 -0
  87. package/.agents/skills/fastapi/references/dependencies.md +142 -0
  88. package/.agents/skills/fastapi/references/other-tools.md +76 -0
  89. package/.agents/skills/fastapi/references/streaming.md +105 -0
  90. package/.agents/skills/frontend-design/LICENSE.txt +177 -0
  91. package/.agents/skills/frontend-design/SKILL.md +42 -0
  92. package/.agents/skills/gradio/SKILL.md +84 -0
  93. package/.agents/skills/gradio/references/api-signatures.md +115 -0
  94. package/.agents/skills/gradio/references/event-listeners.md +130 -0
  95. package/.agents/skills/gradio/references/examples.md +613 -0
  96. package/.agents/skills/ui-ux-pro-max/SKILL.md +659 -0
  97. package/.agents/skills/ui-ux-pro-max/data +1 -0
  98. package/.agents/skills/ui-ux-pro-max/scripts +1 -0
  99. package/.agents/stack.md +187 -187
  100. package/.env.axiom +8 -0
  101. package/AGENTS.md +2 -1
  102. package/README.md +19 -3
  103. package/bin/cli.js +11 -1
  104. package/opencode.json +12 -4
  105. package/package.json +1 -1
  106. package/skills-lock.json +36 -0
  107. package/.agents/skills/n8n-patterns/SKILL.md +0 -272
@@ -0,0 +1,463 @@
1
+ """
2
+ Metrics Dashboard Template (Snowflake Edition)
3
+
4
+ A comprehensive metrics dashboard demonstrating:
5
+ - Snowflake connection via st.connection("snowflake")
6
+ - Parameterized queries for safe data loading
7
+ - Time series visualization with Altair (line, area, bar, point charts)
8
+ - Metric cards with chart/table toggle and popover filters
9
+ - Time range filtering (1M, 6M, 1Y, QTD, YTD, All)
10
+ - Line options (Daily, 7-day MA)
11
+
12
+ This template creates synthetic data in Snowflake. You can:
13
+ 1. Replace the synthetic data generation with your actual tables
14
+ 2. Modify the queries to match your schema (using parameterized queries)
15
+ """
16
+
17
+ from datetime import date, timedelta
18
+ import pandas as pd
19
+ import streamlit as st
20
+ import altair as alt
21
+
22
+ st.set_page_config(
23
+ page_title="Metrics Dashboard (Snowflake)",
24
+ page_icon=":material/monitoring:",
25
+ layout="wide",
26
+ )
27
+
28
+
29
+ # =============================================================================
30
+ # Constants
31
+ # =============================================================================
32
+
33
+ TIME_RANGES = ["1M", "6M", "1Y", "QTD", "YTD", "All"]
34
+ CHART_HEIGHT = 300
35
+
36
+ # Metric configurations (used for synthetic data generation)
37
+ METRIC_CONFIGS = {
38
+ "users": {"base_value": 5000, "growth_rate": 0.002},
39
+ "sessions": {"base_value": 15000, "growth_rate": 0.003},
40
+ "revenue": {"base_value": 50000, "growth_rate": 0.001},
41
+ "conversions": {"base_value": 500, "growth_rate": 0.0015},
42
+ }
43
+
44
+
45
+ # =============================================================================
46
+ # Snowflake Connection and Data Loading
47
+ # =============================================================================
48
+
49
+
50
+ def get_snowflake_connection():
51
+ """Get Snowflake connection via st.connection.
52
+
53
+ Displays an error and stops the app if the connection fails.
54
+ """
55
+ try:
56
+ return st.connection("snowflake")
57
+ except Exception as e:
58
+ st.error(f"Failed to connect to Snowflake: {e}")
59
+ st.info(
60
+ "Make sure you have configured your Snowflake connection in "
61
+ "`.streamlit/secrets.toml` or via environment variables."
62
+ )
63
+ st.stop()
64
+
65
+
66
+ # SQL query template for synthetic data generation.
67
+ # Uses positional parameters (?) for Snowflake connector compatibility.
68
+ SYNTHETIC_DATA_QUERY = """
69
+ WITH date_series AS (
70
+ SELECT
71
+ DATEADD(day, -seq4(), CURRENT_DATE() - 1) AS ds
72
+ FROM TABLE(GENERATOR(ROWCOUNT => 730))
73
+ ),
74
+ base_data AS (
75
+ SELECT
76
+ ds,
77
+ ? * POWER(1 + ?, DATEDIFF(day, '2023-01-01', ds)) AS base_trend,
78
+ CASE WHEN DAYOFWEEK(ds) IN (0, 6) THEN 0.7 ELSE 1.0 END AS seasonality,
79
+ 1 + (RANDOM() / 10000000000000000000.0 - 0.5) * 0.2 AS noise
80
+ FROM date_series
81
+ WHERE ds >= '2023-01-01'
82
+ )
83
+ SELECT
84
+ ds,
85
+ ROUND(base_trend * seasonality * noise, 2) AS daily_value,
86
+ ROUND(AVG(base_trend * seasonality * noise) OVER (
87
+ ORDER BY ds ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
88
+ ), 2) AS value_7d_ma
89
+ FROM base_data
90
+ ORDER BY ds
91
+ """
92
+
93
+
94
+ @st.cache_data(ttl=3600, show_spinner="Loading metrics from Snowflake...")
95
+ def load_metric_from_snowflake(metric_name: str) -> pd.DataFrame:
96
+ """Load metric data from Snowflake using parameterized queries.
97
+
98
+ In production, replace the synthetic query with your actual table query:
99
+
100
+ PRODUCTION_QUERY = '''
101
+ SELECT ds, daily_value, value_7d_ma
102
+ FROM your_schema.your_metrics_table
103
+ WHERE metric_name = ?
104
+ ORDER BY ds
105
+ '''
106
+
107
+ df = conn.query(PRODUCTION_QUERY, params=[metric_name])
108
+ """
109
+ conn = get_snowflake_connection()
110
+ config = METRIC_CONFIGS[metric_name]
111
+
112
+ # Use parameterized query with positional parameters (list)
113
+ df = conn.query(
114
+ SYNTHETIC_DATA_QUERY,
115
+ params=[config["base_value"], config["growth_rate"]],
116
+ )
117
+ df.columns = df.columns.str.lower() # Normalize column names
118
+ return df
119
+
120
+
121
+ @st.cache_data(ttl=3600)
122
+ def load_all_metrics() -> dict[str, pd.DataFrame]:
123
+ """Load all metrics from Snowflake."""
124
+ return {
125
+ "users": load_metric_from_snowflake("users"),
126
+ "sessions": load_metric_from_snowflake("sessions"),
127
+ "revenue": load_metric_from_snowflake("revenue"),
128
+ "conversions": load_metric_from_snowflake("conversions"),
129
+ }
130
+
131
+
132
+ # =============================================================================
133
+ # Chart Utilities
134
+ # =============================================================================
135
+
136
+
137
+ def filter_by_time_range(df: pd.DataFrame, x_col: str, time_range: str) -> pd.DataFrame:
138
+ """Filter dataframe by time range."""
139
+ if time_range == "All" or df.empty:
140
+ return df
141
+
142
+ df = df.copy()
143
+ df[x_col] = pd.to_datetime(df[x_col])
144
+ max_date = df[x_col].max()
145
+
146
+ if time_range == "1M":
147
+ min_date = max_date - timedelta(days=30)
148
+ elif time_range == "6M":
149
+ min_date = max_date - timedelta(days=180)
150
+ elif time_range == "1Y":
151
+ min_date = max_date - timedelta(days=365)
152
+ elif time_range == "QTD":
153
+ quarter_month = ((max_date.month - 1) // 3) * 3 + 1
154
+ min_date = pd.Timestamp(date(max_date.year, quarter_month, 1))
155
+ elif time_range == "YTD":
156
+ min_date = pd.Timestamp(date(max_date.year, 1, 1))
157
+ else:
158
+ return df
159
+
160
+ return df[df[x_col] >= min_date]
161
+
162
+
163
+ def render_line_chart(
164
+ df: pd.DataFrame,
165
+ x_col: str,
166
+ y_cols: list[str],
167
+ labels: list[str],
168
+ height: int = CHART_HEIGHT,
169
+ ) -> alt.Chart:
170
+ """Render a multi-line chart."""
171
+ # Melt for Altair
172
+ melted = df.melt(
173
+ id_vars=[x_col],
174
+ value_vars=y_cols,
175
+ var_name="series",
176
+ value_name="value",
177
+ )
178
+
179
+ # Map to labels
180
+ label_map = dict(zip(y_cols, labels))
181
+ melted["series"] = melted["series"].map(label_map)
182
+
183
+ chart = (
184
+ alt.Chart(melted)
185
+ .mark_line()
186
+ .encode(
187
+ x=alt.X(f"{x_col}:T", title=None),
188
+ y=alt.Y("value:Q", title=None, scale=alt.Scale(zero=False)),
189
+ color=alt.Color("series:N", title=None, legend=alt.Legend(orient="bottom")),
190
+ strokeDash=alt.condition(
191
+ alt.datum.series == "7-day MA",
192
+ alt.value([5, 5]),
193
+ alt.value([0]),
194
+ ),
195
+ tooltip=[
196
+ alt.Tooltip(f"{x_col}:T", title="Date", format="%Y-%m-%d"),
197
+ alt.Tooltip("series:N", title="Series"),
198
+ alt.Tooltip("value:Q", title="Value", format=",.0f"),
199
+ ],
200
+ )
201
+ .properties(height=height)
202
+ )
203
+
204
+ return chart
205
+
206
+
207
+ def render_area_chart(
208
+ df: pd.DataFrame,
209
+ x_col: str,
210
+ y_cols: list[str],
211
+ labels: list[str],
212
+ height: int = CHART_HEIGHT,
213
+ ) -> alt.Chart:
214
+ """Render a stacked area chart."""
215
+ melted = df.melt(
216
+ id_vars=[x_col],
217
+ value_vars=y_cols,
218
+ var_name="series",
219
+ value_name="value",
220
+ )
221
+ label_map = dict(zip(y_cols, labels))
222
+ melted["series"] = melted["series"].map(label_map)
223
+
224
+ chart = (
225
+ alt.Chart(melted)
226
+ .mark_area(opacity=0.6, line=True)
227
+ .encode(
228
+ x=alt.X(f"{x_col}:T", title=None),
229
+ y=alt.Y("value:Q", title=None, scale=alt.Scale(zero=False)),
230
+ color=alt.Color("series:N", title=None, legend=alt.Legend(orient="bottom")),
231
+ tooltip=[
232
+ alt.Tooltip(f"{x_col}:T", title="Date", format="%Y-%m-%d"),
233
+ alt.Tooltip("series:N", title="Series"),
234
+ alt.Tooltip("value:Q", title="Value", format=",.0f"),
235
+ ],
236
+ )
237
+ .properties(height=height)
238
+ )
239
+ return chart
240
+
241
+
242
+ def render_bar_chart(
243
+ df: pd.DataFrame,
244
+ x_col: str,
245
+ y_cols: list[str],
246
+ labels: list[str],
247
+ height: int = CHART_HEIGHT,
248
+ ) -> alt.Chart:
249
+ """Render a bar chart (weekly aggregation for readability)."""
250
+ df = df.copy()
251
+ df[x_col] = pd.to_datetime(df[x_col])
252
+ df["week"] = df[x_col].dt.to_period("W").dt.start_time
253
+
254
+ # Aggregate by week
255
+ agg_df = df.groupby("week")[y_cols].mean().reset_index()
256
+
257
+ melted = agg_df.melt(
258
+ id_vars=["week"],
259
+ value_vars=y_cols,
260
+ var_name="series",
261
+ value_name="value",
262
+ )
263
+ label_map = dict(zip(y_cols, labels))
264
+ melted["series"] = melted["series"].map(label_map)
265
+
266
+ chart = (
267
+ alt.Chart(melted)
268
+ .mark_bar(opacity=0.8)
269
+ .encode(
270
+ x=alt.X("week:T", title=None),
271
+ y=alt.Y("value:Q", title=None, scale=alt.Scale(zero=False)),
272
+ color=alt.Color("series:N", title=None, legend=alt.Legend(orient="bottom")),
273
+ xOffset="series:N",
274
+ tooltip=[
275
+ alt.Tooltip("week:T", title="Week", format="%Y-%m-%d"),
276
+ alt.Tooltip("series:N", title="Series"),
277
+ alt.Tooltip("value:Q", title="Value", format=",.0f"),
278
+ ],
279
+ )
280
+ .properties(height=height)
281
+ )
282
+ return chart
283
+
284
+
285
+ def render_point_chart(
286
+ df: pd.DataFrame,
287
+ x_col: str,
288
+ y_cols: list[str],
289
+ labels: list[str],
290
+ height: int = CHART_HEIGHT,
291
+ ) -> alt.Chart:
292
+ """Render a scatter/point chart with trend line."""
293
+ melted = df.melt(
294
+ id_vars=[x_col],
295
+ value_vars=y_cols,
296
+ var_name="series",
297
+ value_name="value",
298
+ )
299
+ label_map = dict(zip(y_cols, labels))
300
+ melted["series"] = melted["series"].map(label_map)
301
+
302
+ points = (
303
+ alt.Chart(melted)
304
+ .mark_point(opacity=0.5, size=20)
305
+ .encode(
306
+ x=alt.X(f"{x_col}:T", title=None),
307
+ y=alt.Y("value:Q", title=None, scale=alt.Scale(zero=False)),
308
+ color=alt.Color("series:N", title=None, legend=alt.Legend(orient="bottom")),
309
+ tooltip=[
310
+ alt.Tooltip(f"{x_col}:T", title="Date", format="%Y-%m-%d"),
311
+ alt.Tooltip("series:N", title="Series"),
312
+ alt.Tooltip("value:Q", title="Value", format=",.0f"),
313
+ ],
314
+ )
315
+ )
316
+
317
+ # Add trend line for 7-day MA only
318
+ trend = (
319
+ alt.Chart(melted[melted["series"] == "7-day MA"])
320
+ .mark_line(strokeDash=[5, 5], strokeWidth=2)
321
+ .encode(
322
+ x=alt.X(f"{x_col}:T"),
323
+ y=alt.Y("value:Q"),
324
+ color=alt.Color("series:N"),
325
+ )
326
+ )
327
+
328
+ return (points + trend).properties(height=height)
329
+
330
+
331
+ # =============================================================================
332
+ # Metric Card Component
333
+ # =============================================================================
334
+
335
+
336
+ def metric_card(
337
+ title: str,
338
+ df: pd.DataFrame,
339
+ key_prefix: str,
340
+ chart_type: str = "line",
341
+ ):
342
+ """Display a metric card with chart/table toggle and popover filters.
343
+
344
+ Args:
345
+ title: Card title
346
+ df: DataFrame with ds, daily_value, value_7d_ma columns
347
+ key_prefix: Unique prefix for widget keys
348
+ chart_type: One of "line", "area", "bar", "point"
349
+ """
350
+ chart_renderers = {
351
+ "line": render_line_chart,
352
+ "area": render_area_chart,
353
+ "bar": render_bar_chart,
354
+ "point": render_point_chart,
355
+ }
356
+ render_chart = chart_renderers.get(chart_type, render_line_chart)
357
+
358
+ with st.container(border=True):
359
+ # Header row with title, view toggle, and filters
360
+ with st.container(
361
+ horizontal=True,
362
+ horizontal_alignment="distribute",
363
+ vertical_alignment="center",
364
+ ):
365
+ st.markdown(f"**{title}**")
366
+
367
+ view_mode = st.segmented_control(
368
+ "View",
369
+ options=[":material/show_chart:", ":material/table:"],
370
+ default=":material/show_chart:",
371
+ key=f"{key_prefix}_view",
372
+ label_visibility="collapsed",
373
+ )
374
+
375
+ with st.popover("Filters", type="tertiary"):
376
+ line_options = st.pills(
377
+ "Lines",
378
+ options=["Daily", "7-day MA"],
379
+ default=["Daily", "7-day MA"],
380
+ selection_mode="multi",
381
+ key=f"{key_prefix}_lines",
382
+ )
383
+ time_range = st.segmented_control(
384
+ "Time range",
385
+ options=TIME_RANGES,
386
+ default="All",
387
+ key=f"{key_prefix}_time",
388
+ )
389
+
390
+ # Apply filters
391
+ line_options = line_options or ["7-day MA"]
392
+ filtered_df = filter_by_time_range(df, "ds", time_range)
393
+
394
+ # Determine which columns to show
395
+ y_cols = []
396
+ labels = []
397
+ if "Daily" in line_options:
398
+ y_cols.append("daily_value")
399
+ labels.append("Daily")
400
+ if "7-day MA" in line_options:
401
+ y_cols.append("value_7d_ma")
402
+ labels.append("7-day MA")
403
+
404
+ # Render view
405
+ if "table" in (view_mode or ""):
406
+ st.dataframe(
407
+ filtered_df,
408
+ height=CHART_HEIGHT,
409
+ hide_index=True,
410
+ )
411
+ else:
412
+ if y_cols:
413
+ st.altair_chart(
414
+ render_chart(filtered_df, "ds", y_cols, labels),
415
+ )
416
+ else:
417
+ st.info("Select at least one line option.")
418
+
419
+
420
+ # =============================================================================
421
+ # Page Header Component
422
+ # =============================================================================
423
+
424
+
425
+ def render_page_header(title: str):
426
+ """Render page header with title and reset button."""
427
+ with st.container(
428
+ horizontal=True, horizontal_alignment="distribute", vertical_alignment="center"
429
+ ):
430
+ st.markdown(title)
431
+ if st.button(":material/restart_alt: Reset", type="tertiary"):
432
+ st.session_state.clear()
433
+ st.rerun()
434
+
435
+
436
+ # =============================================================================
437
+ # Page Layout
438
+ # =============================================================================
439
+
440
+ # Load data from Snowflake
441
+ metrics_data = load_all_metrics()
442
+
443
+ # Page header
444
+ render_page_header("# :material/monitoring: Metrics Dashboard")
445
+ st.caption(":material/cloud: Powered by Snowflake")
446
+
447
+ # Row 1: Users and Sessions
448
+ row1 = st.columns(2)
449
+
450
+ with row1[0]:
451
+ metric_card("Active Users", metrics_data["users"], "users", chart_type="line")
452
+
453
+ with row1[1]:
454
+ metric_card("Sessions", metrics_data["sessions"], "sessions", chart_type="area")
455
+
456
+ # Row 2: Revenue and Conversions
457
+ row2 = st.columns(2)
458
+
459
+ with row2[0]:
460
+ metric_card("Revenue", metrics_data["revenue"], "revenue", chart_type="bar")
461
+
462
+ with row2[1]:
463
+ metric_card("Conversions", metrics_data["conversions"], "conversions", chart_type="point")
@@ -0,0 +1,12 @@
1
+ [project]
2
+ name = "dashboard-seattle-weather"
3
+ version = "1.0.0"
4
+ description = "An example dashboard exploring the Seattle Weather dataset"
5
+ requires-python = ">=3.11"
6
+ dependencies = [
7
+ "altair>=5.5.0",
8
+ "pandas>=2.2.3",
9
+ "snowflake-connector-python>=3.3.0",
10
+ "streamlit[snowflake]>=1.54.0",
11
+ "vega-datasets>=0.9.0",
12
+ ]