Inspired by a tweet by Fanilo I vibe coded the below Project Prioritization dashboard.
Code
"""
Interactive Value vs Complexity Dashboard for Project Prioritization
This script creates an interactive visualization to help teams prioritize project tasks
based on their business value and complexity. Tasks are plotted on a scatter chart with
hover interactions to explore details about each task.
The visualization helps identify "quick wins" (high value, low complexity) and helps
teams make data-driven decisions about task prioritization.
🚀 Features:
- Interactive hover to explore task details
- Visual quadrant system for prioritization
- Modern, clean design with emojis and styling
- Built with HoloViz Panel and hvPlot
📊 Usage:
- Run with: panel serve script.py --show
- Or import and use: TaskPrioritizationDashboard(data=your_data)
Author: Marc Skov Madsen
License: MIT
Inspiration: https://x.com/andfanilo/status/1967841173822906830
"""
import pandas as pd
import panel as pn
import hvplot.pandas
import holoviews as hv
from holoviews import opts, streams
import param
from typing import cast
# Enable Panel extensions for interactive widgets and plots
pn.extension()
@pn.cache
def create_project_tasks() -> pd.DataFrame:
    """
    Create a DataFrame with business value vs complexity data for project tasks.
    
    This function generates sample data representing common software development
    tasks with their associated complexity (effort required) and business value
    (impact on business objectives).
    
    Returns
    -------
    pd.DataFrame
        DataFrame with columns:
        - task_name: str, descriptive name of the task
        - complexity: int, scale 1-10 (1=low complexity, 10=high complexity)
        - business_value: int, scale 1-10 (1=low value, 10=high value)
        - emoji: str, visual representation of the task type
        
    Examples
    --------
    >>> df = create_project_tasks()
    >>> print(df.head())
    """
    data = {
        "task_name": [
            "User Authentication",
            "Database Migration",
            "Mobile App UI",
            "API Documentation",
            "Performance Optimization",
            "Unit Test Coverage",
            "Dark Mode Theme",
        ],
        "complexity": [7, 9, 6, 3, 8, 4, 2],
        "business_value": [9, 6, 8, 5, 7, 6, 4],
        "emoji": ["🔐", "🗄️", "📱", "📚", "⚡", "✅", "🌙"],
    }
    return pd.DataFrame(data)
class TaskPrioritizationDashboard(pn.viewable.Viewer):
    """
    Interactive dashboard for visualizing and exploring project task prioritization.
    
    This component creates a scatter plot where tasks are positioned based on their
    complexity (x-axis) and business value (y-axis). Users can hover over points
    to see detailed information about each task.
    
    The dashboard includes quadrant guidelines to help identify:
    - Quick Wins: High value, low complexity (top-left)
    - Major Projects: High value, high complexity (top-right)
    - Fill-ins: Low value, low complexity (bottom-left)  
    - Questionable: Low value, high complexity (bottom-right)
    
    Parameters
    ----------
    data : pd.DataFrame
        DataFrame containing task information with columns:
        task_name, complexity, business_value, emoji
    selected_row : pd.Series or None
        Currently selected task row, updated on hover
    """
    
    data = cast(pd.DataFrame, param.DataFrame(constant=True, allow_None=False))
    selected_row = param.Parameter(default=None)
    # Configuration constants
    _max_hover_distance = 0.4
    _points_color = "#FF6B9D"  # Modern pink color
    _plot_size = 600
    _quadrant_line_position = 5
    def __init__(self, **params):
        """
        Initialize the Task Prioritization Dashboard.
        
        Parameters
        ----------
        **params
            Additional parameters passed to the parent Viewer class
        """
        super().__init__(**params)
        
        # Create the interactive plot
        self._setup_plot()
        
        # Create the UI layout
        self._create_layout()
    def _setup_plot(self) -> None:
        """Set up the interactive scatter plot with hover functionality."""
        data = self.data
        # Create scatter plot points
        points = data.hvplot.scatter(
            x="complexity",
            y="business_value",
            hover_cols=["task_name"],
            size=200,
            hover=False,
            toolbar=False,
            xlabel="Complexity →",
            ylabel="Business Value →",
            color=self._points_color,
            xlim=(0, 10),
            ylim=(0, 10),
            title="Project Task Prioritization Matrix",
            width=self._plot_size,
            height=self._plot_size,
        )
        
        # Set up hover interaction
        hover_stream = streams.PointerXY(source=points)
        pn.bind(
            self._update_selected_task, 
            hover_stream.param.x, 
            hover_stream.param.y, 
            watch=True
        )
        # Create task labels
        labels = data.hvplot.labels(
            x="complexity",
            y="business_value",
            text="task_name",
            text_font_size="9pt",
            text_color="black",
            text_baseline="bottom",
            hover=False,
            padding=0.25,
            toolbar=False,
        ).opts(opts.Labels(yoffset=0.4))
        
        # Add quadrant divider lines
        vertical_line = hv.VLine(x=self._quadrant_line_position).opts(
            color="gray", line_dash="dashed", line_width=1, alpha=0.6
        )
        horizontal_line = hv.HLine(y=self._quadrant_line_position).opts(
            color="gray", line_dash="dashed", line_width=1, alpha=0.6
        )
        
        # Combine all plot elements
        empty_plot = hv.Curve([])
        self._plot = empty_plot * vertical_line * horizontal_line * points * labels
    def _create_layout(self) -> None:
        """Create the dashboard layout with plot and task details."""
        self._view = pn.Column(
            "# 📊 Task Prioritization Dashboard",
            pn.pane.Markdown(
                """
                **How to use this dashboard:**
                - Hover over any point to see task details
                - **Top-left quadrant**: Quick wins (high value, low complexity)
                - **Top-right quadrant**: Major projects (high value, high complexity)
                - **Bottom-left quadrant**: Fill-ins (low value, low complexity)
                - **Bottom-right quadrant**: Questionable tasks (low value, high complexity)
                """,
                margin=(0, 0, 20, 0)
            ),
            pn.pane.HoloViews(
                self._plot, 
                height=self._plot_size, 
                width=self._plot_size + 100
            ),
            pn.pane.Markdown(
                self.task_details, 
                styles={"font-size": "18px", "padding": "20px", "background": "#f8f9fa"}, 
                width=self._plot_size + 100,
                margin=(20, 0, 0, 0)
            ),
            width=self._plot_size + 100,
            styles={"margin": "auto"}
        )
    @param.depends("selected_row")
    def task_details(self) -> str:
        """
        Generate formatted task details for the selected task.
        
        Returns
        -------
        str
            Markdown-formatted string with task information
        """
        if self.selected_row is None:
            return """
            ### 👆 Hover over a task to see details
            
            Select any point on the chart above to view detailed information about that task.
            """
        
        row = self.selected_row
        if not isinstance(row, pd.Series):
            return "Error: Invalid task data"
            
        value_stars = "⭐" * int(row['business_value'])
        complexity_gears = "⚙️" * int(row['complexity'])
        return f"""
        ### {row['emoji']} {row['task_name']}
        **Business Value:** {value_stars} ({row['business_value']}/10)
        **Complexity:** {complexity_gears} ({row['complexity']}/10)
        
        **Recommendation:** {self._get_priority_recommendation(row)}
        """
    def _get_priority_recommendation(self, task_row: pd.Series) -> str:
        """
        Get priority recommendation based on task position in the matrix.
        
        Parameters
        ----------
        task_row : pd.Series
            Task data containing complexity and business_value
            
        Returns
        -------
        str
            Priority recommendation text
        """
        value = int(task_row['business_value'])
        complexity = int(task_row['complexity'])
        
        if value >= self._quadrant_line_position and complexity < self._quadrant_line_position:
            return "🚀 **Quick Win** - High priority, implement soon!"
        elif value >= self._quadrant_line_position and complexity >= self._quadrant_line_position:
            return "🎯 **Major Project** - Plan carefully and allocate sufficient resources"
        elif value < self._quadrant_line_position and complexity < self._quadrant_line_position:
            return "📝 **Fill-in Task** - Good for spare time or junior developers"
        else:
            return "❓ **Questionable** - Consider if this task is really necessary"
    def _update_selected_task(self, x: float | None, y: float | None) -> None:
        """
        Update the selected task based on hover position.
        
        Parameters
        ----------
        x : float or None
            Hover x-coordinate (complexity)
        y : float or None  
            Hover y-coordinate (business value)
        """
        if x is None or y is None:
            self.selected_row = None
            return
        # Calculate distances to all points
        distances = (
            (self.data["complexity"] - x) ** 2 + 
            (self.data["business_value"] - y) ** 2
        )
        
        # Find closest point within hover distance
        closest_index = distances.idxmin()
        if distances[closest_index] <= self._max_hover_distance**2:
            self.selected_row = self.data.loc[closest_index]
        else:
            self.selected_row = None
    def __panel__(self) -> pn.viewable.Viewable:
        """Return the Panel view for display."""
        return self._view
# Create and serve the dashboard
if pn.state.served:
    # Create the dashboard with sample data
    dashboard = TaskPrioritizationDashboard(data=create_project_tasks())
    
    # Make it servable for Panel applications
    dashboard.servable(title="Task Prioritization Dashboard")
Install with pip install panel hvplot holoviews pandas.
Serve the app with panel serve app.py.