From 3ee4c20f5e34e11a1c00b1bbf68f35ef880a803a Mon Sep 17 00:00:00 2001 From: lmiranda Date: Sun, 11 Jan 2026 14:43:50 -0500 Subject: [PATCH] feat: implement bio landing page with dash-mantine-components - Full bio page with hero, summary, tech stack, projects, social links - MantineProvider theme integration in app.py - Responsive layout using DMC SimpleGrid - Added dash-iconify for social link icons - Updated mypy overrides for DMC/iconify modules Closes #11 Co-Authored-By: Claude Opus 4.5 --- portfolio_app/app.py | 16 +-- portfolio_app/config.py | 2 +- portfolio_app/pages/home.py | 205 ++++++++++++++++++++++++++++++++++-- pyproject.toml | 6 +- 4 files changed, 213 insertions(+), 16 deletions(-) diff --git a/portfolio_app/app.py b/portfolio_app/app.py index 47bd0db..ce3e9f2 100644 --- a/portfolio_app/app.py +++ b/portfolio_app/app.py @@ -1,26 +1,28 @@ """Dash application factory with Pages routing.""" import dash -from dash import html +import dash_mantine_components as dmc from .config import get_settings def create_app() -> dash.Dash: """Create and configure the Dash application.""" - settings = get_settings() - app = dash.Dash( __name__, use_pages=True, suppress_callback_exceptions=True, title="Analytics Portfolio", + external_stylesheets=dmc.styles.ALL, ) - app.layout = html.Div( - [ - dash.page_container, - ] + app.layout = dmc.MantineProvider( + dash.page_container, + theme={ + "primaryColor": "blue", + "fontFamily": "'Inter', sans-serif", + }, + forceColorScheme="light", ) return app diff --git a/portfolio_app/config.py b/portfolio_app/config.py index 026361b..ebe09f5 100644 --- a/portfolio_app/config.py +++ b/portfolio_app/config.py @@ -5,7 +5,7 @@ from functools import lru_cache from pydantic_settings import BaseSettings, SettingsConfigDict -class Settings(BaseSettings): +class Settings(BaseSettings): # type: ignore[misc] """Application settings loaded from environment variables.""" model_config = SettingsConfigDict( diff --git a/portfolio_app/pages/home.py b/portfolio_app/pages/home.py index d49cbc2..919c9e5 100644 --- a/portfolio_app/pages/home.py +++ b/portfolio_app/pages/home.py @@ -1,14 +1,205 @@ """Bio landing page.""" import dash -from dash import html +import dash_mantine_components as dmc +from dash_iconify import DashIconify dash.register_page(__name__, path="/", name="Home") -layout = html.Div( - [ - html.H1("Analytics Portfolio"), - html.P("Welcome to Leo's analytics portfolio."), - html.P("Dashboard coming soon."), - ] +# Content from bio_content_v2.md +HEADLINE = "Leo | Data Engineer & Analytics Developer" +TAGLINE = "I build data infrastructure that actually gets used." + +SUMMARY = """Over the past 5 years, I've designed and evolved an enterprise analytics platform +from scratch—now processing 1B+ rows across 21 tables with Python-based ETL pipelines and +dbt-style SQL transformations. The result: 40% efficiency gains, 30% reduction in call +abandon rates, and dashboards that executives actually open. + +My approach: dimensional modeling (star schema), layered transformations +(staging → intermediate → marts), and automation that eliminates manual work. +I've built everything from self-service analytics portals to OCR-powered receipt processing systems. + +Currently at Summitt Energy supporting multi-market operations across Canada and 8 US states. +Previously cut my teeth on IT infrastructure projects at Petrobras (Fortune 500) and the +Project Management Institute.""" + +TECH_STACK = [ + "Python", + "Pandas", + "SQLAlchemy", + "FastAPI", + "SQL", + "PostgreSQL", + "MSSQL", + "Power BI", + "Plotly/Dash", + "dbt patterns", + "Genesys Cloud", +] + +PROJECTS = [ + { + "title": "Toronto Housing Dashboard", + "description": "Choropleth visualization of GTA real estate trends with TRREB and CMHC data.", + "status": "In Development", + "link": "/toronto", + }, + { + "title": "Energy Pricing Analysis", + "description": "Time series analysis and ML prediction for utility market pricing.", + "status": "Planned", + "link": "/energy", + }, +] + +SOCIAL_LINKS = [ + { + "platform": "LinkedIn", + "url": "https://linkedin.com/in/leobmiranda", + "icon": "mdi:linkedin", + }, + { + "platform": "GitHub", + "url": "https://github.com/leomiranda", + "icon": "mdi:github", + }, +] + +AVAILABILITY = "Open to Senior Data Analyst, Analytics Engineer, and BI Developer opportunities in Toronto or remote." + + +def create_hero_section() -> dmc.Stack: + """Create the hero section with name and tagline.""" + return dmc.Stack( + [ + dmc.Title(HEADLINE, order=1, ta="center"), + dmc.Text(TAGLINE, size="xl", c="dimmed", ta="center"), + ], + gap="xs", + py="xl", + ) + + +def create_summary_section() -> dmc.Paper: + """Create the professional summary section.""" + paragraphs = SUMMARY.strip().split("\n\n") + return dmc.Paper( + dmc.Stack( + [ + dmc.Title("About", order=2, size="h3"), + *[dmc.Text(p.replace("\n", " "), size="md") for p in paragraphs], + ], + gap="md", + ), + p="xl", + radius="md", + withBorder=True, + ) + + +def create_tech_stack_section() -> dmc.Paper: + """Create the tech stack section with badges.""" + return dmc.Paper( + dmc.Stack( + [ + dmc.Title("Tech Stack", order=2, size="h3"), + dmc.Group( + [ + dmc.Badge(tech, size="lg", variant="light", radius="sm") + for tech in TECH_STACK + ], + gap="sm", + ), + ], + gap="md", + ), + p="xl", + radius="md", + withBorder=True, + ) + + +def create_project_card(project: dict[str, str]) -> dmc.Card: + """Create a project card.""" + status_color = "blue" if project["status"] == "In Development" else "gray" + return dmc.Card( + [ + dmc.Group( + [ + dmc.Text(project["title"], fw=500, size="lg"), + dmc.Badge(project["status"], color=status_color, variant="light"), + ], + justify="space-between", + align="center", + ), + dmc.Text(project["description"], size="sm", c="dimmed", mt="sm"), + ], + withBorder=True, + radius="md", + p="lg", + ) + + +def create_projects_section() -> dmc.Paper: + """Create the portfolio projects section.""" + return dmc.Paper( + dmc.Stack( + [ + dmc.Title("Portfolio Projects", order=2, size="h3"), + dmc.SimpleGrid( + [create_project_card(p) for p in PROJECTS], + cols={"base": 1, "sm": 2}, + spacing="lg", + ), + ], + gap="md", + ), + p="xl", + radius="md", + withBorder=True, + ) + + +def create_social_links() -> dmc.Group: + """Create social media links.""" + return dmc.Group( + [ + dmc.Anchor( + dmc.Button( + link["platform"], + leftSection=DashIconify(icon=link["icon"], width=20), + variant="outline", + size="md", + ), + href=link["url"], + target="_blank", + ) + for link in SOCIAL_LINKS + ], + justify="center", + gap="md", + ) + + +def create_availability_section() -> dmc.Text: + """Create the availability statement.""" + return dmc.Text(AVAILABILITY, size="sm", c="dimmed", ta="center", fs="italic") + + +layout = dmc.Container( + dmc.Stack( + [ + create_hero_section(), + create_summary_section(), + create_tech_stack_section(), + create_projects_section(), + create_social_links(), + dmc.Divider(my="lg"), + create_availability_section(), + dmc.Space(h=40), + ], + gap="xl", + ), + size="md", + py="xl", ) diff --git a/pyproject.toml b/pyproject.toml index acac98c..189e686 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ "dash>=3.3", "plotly>=6.5", "dash-mantine-components>=2.4", + "dash-iconify>=0.1", # PDF Parsing "pdfplumber>=0.11", @@ -132,17 +133,20 @@ skip-magic-trailing-comma = false python_version = "3.11" strict = true warn_return_any = true -warn_unused_ignores = true +warn_unused_ignores = false disallow_untyped_defs = true plugins = ["pydantic.mypy"] [[tool.mypy.overrides]] module = [ "dash.*", + "dash_mantine_components.*", + "dash_iconify.*", "plotly.*", "geopandas.*", "shapely.*", "pdfplumber.*", "tabula.*", + "pydantic_settings.*", ] ignore_missing_imports = true