Skip to content

nshkrdotcom/apex_ui

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ApexUI

Advanced web UI components for APEX framework applications. Build production-ready monitoring dashboards, interactive visualizations, and terminal-themed interfaces with Phoenix LiveView.

Features

  • 🎯 Advanced Chart Widgets: Multi-format chart support (line, bar, pie, area) with real-time updates
  • 🖥️ Terminal Theme Components: Consistent green-on-dark terminal aesthetic
  • ⚡ LiveView Components: Production-ready widgets and layouts optimized for real-time updates
  • 🎮 3D Visualizations: Three.js integration for interactive cluster topology displays
  • 📡 Real-time Updates: WebSocket-based live data streaming with configurable intervals
  • 📱 Responsive Design: Mobile-friendly and adaptive layouts
  • đź§© Modular Architecture: Mix-and-match component system with optional dependencies

Installation

Add apex_ui to your list of dependencies in mix.exs:

def deps do
  [
    {:apex_ui, "~> 0.0.1"}
  ]
end

Quick Start

1. Add Components to Your LiveView

defmodule MyAppWeb.DashboardLive do
  use MyAppWeb, :live_view
  import ApexUI.Components.Widgets.ChartWidget

  def render(assigns) do
    ~H"""
    <div class="grid grid-cols-2 gap-4 p-4">
      <.live_component 
        module={ApexUI.Components.Widgets.ChartWidget}
        id="system-metrics"
        chart_type={:line}
        data={@metrics_data}
        title="System Metrics"
        real_time={true}
        height={400}
      />
      
      <.live_component 
        module={ApexUI.Components.Widgets.ChartWidget}
        id="process-distribution"
        chart_type={:pie}
        data={@process_data}
        title="Process Distribution"
        color_scheme={:multi}
      />
    </div>
    """
  end

  def mount(_params, _session, socket) do
    if connected?(socket) do
      :timer.send_interval(1000, self(), :update_metrics)
    end

    socket = 
      socket
      |> assign(:metrics_data, get_metrics_data())
      |> assign(:process_data, get_process_data())

    {:ok, socket}
  end

  def handle_info(:update_metrics, socket) do
    socket = 
      socket
      |> assign(:metrics_data, get_metrics_data())
      |> assign(:process_data, get_process_data())

    {:noreply, socket}
  end

  defp get_metrics_data do
    # Your data fetching logic
    [
      %{x: 1, y: :rand.uniform(100)},
      %{x: 2, y: :rand.uniform(100)},
      %{x: 3, y: :rand.uniform(100)}
    ]
  end

  defp get_process_data do
    [
      %{value: 30, label: "GenServers"},
      %{value: 20, label: "Supervisors"}, 
      %{value: 15, label: "Tasks"}
    ]
  end
end

2. Include JavaScript Assets

Add to your assets/js/app.js:

import {ApexUIHooks} from "../deps/apex_ui/assets/js/hooks"

let liveSocket = new LiveSocket("/live", Socket, {
  hooks: ApexUIHooks,
  params: {_csrf_token: csrfToken}
})

3. Include CSS (Optional)

For custom styling, add to your assets/css/app.css:

@import "../deps/apex_ui/assets/css/apex_ui.css";

Available Components

Core Widget Components

ChartWidget

Multi-format chart component with real-time capabilities:

<.live_component 
  module={ApexUI.Components.Widgets.ChartWidget}
  id="my-chart"
  chart_type={:line}     # :line, :bar, :pie, :area
  data={@chart_data}
  title="My Chart"
  real_time={true}
  color_scheme={:green}  # :green, :blue, :multi
  height={300}
  show_grid={true}
  show_legend={true}
/>

Supported Data Formats:

# Line/Bar/Area charts
[
  %{x: 1, y: 10, timestamp: ~U[2024-01-01 12:00:00Z]},
  %{x: 2, y: 15, timestamp: ~U[2024-01-01 12:01:00Z]}
]

# Pie charts  
[
  %{value: 30, label: "Category A"},
  %{value: 20, label: "Category B"}
]

SystemMetricsWidget

Real-time system monitoring with multiple metrics:

<.live_component 
  module={ApexUI.Components.Widgets.SystemMetricsWidget}
  id="system-metrics"
  data={@system_data}
  refresh_interval={1000}
  show_alerts={true}
/>

ProcessListWidget

Interactive process management interface:

<.live_component 
  module={ApexUI.Components.Widgets.ProcessListWidget}
  id="process-list"
  processes={@processes}
  filterable={true}
  sortable={true}
  actions={[:inspect, :kill, :trace]}
/>

Layout Components

TerminalPanelLayout

Flexible terminal-themed panel system:

<.live_component 
  module={ApexUI.Components.Layout.TerminalPanelLayout}
  id="dashboard-layout"
  layout_type={:grid}    # :grid, :split, :tabs
  panels={@dashboard_panels}
/>

TerminalStatusBar

Status bar with real-time metrics:

<.live_component 
  module={ApexUI.Components.Terminal.TerminalStatusBar}
  id="status-bar"
  metrics={@status_metrics}
  navigation_links={@nav_links}
/>

Advanced LiveView Pages

SystemDashboardLive

Complete system monitoring dashboard:

# In your router
live "/dashboard", ApexUI.Live.SystemDashboardLive, :index

ClusterVisualizationLive

3D cluster topology visualization:

# In your router
live "/cluster/viz", ApexUI.Live.ClusterVisualizationLive, :index

ArsenalLive

Arsenal command center interface:

# In your router (requires arsenal)
live "/arsenal", ApexUI.Live.ArsenalLive, :index

Configuration

Application Configuration

Add to your config/config.exs:

config :apex_ui,
  theme: :terminal_green,        # :terminal_green, :terminal_blue, :dark
  real_time_updates: true,
  chart_defaults: %{
    height: 300,
    width: 400,
    animated: true,
    color_scheme: :green,
    show_grid: true,
    show_legend: true
  },
  terminal_defaults: %{
    background: "bg-gray-900",
    text_color: "text-green-400", 
    border_color: "border-green-500/30",
    accent_color: "text-green-300"
  }

Theme Customization

Override default colors by setting CSS variables:

:root {
  --apex-ui-primary: #10b981;      /* Green */
  --apex-ui-secondary: #064e3b;    /* Dark green */
  --apex-ui-background: #111827;   /* Dark gray */
  --apex-ui-surface: #1f2937;     /* Medium gray */
  --apex-ui-text: #d1fae5;        /* Light green */
}

Tailwind Integration

Include ApexUI components in your Tailwind config:

// tailwind.config.js
module.exports = {
  content: [
    "./lib/**/*.ex",
    "./lib/**/*.heex", 
    "../deps/apex_ui/lib/**/*.ex"
  ],
  theme: {
    extend: {
      colors: {
        'apex-green': {
          50: '#ecfdf5',
          500: '#10b981',
          900: '#064e3b'
        }
      }
    }
  }
}

Real-time Updates

WebSocket Integration

ApexUI components automatically subscribe to real-time updates:

# In your LiveView
def mount(_params, _session, socket) do
  if connected?(socket) do
    # Subscribe to system metrics
    Phoenix.PubSub.subscribe(MyApp.PubSub, "system_metrics")
    
    # Subscribe to process updates  
    Phoenix.PubSub.subscribe(MyApp.PubSub, "process_updates")
  end
  
  {:ok, socket}
end

def handle_info({:system_metrics, data}, socket) do
  {:noreply, assign(socket, :metrics_data, data)}
end

Broadcasting Updates

# From your application
Phoenix.PubSub.broadcast(MyApp.PubSub, "system_metrics", {
  :system_metrics, 
  %{cpu: 45.2, memory: 67.8, timestamp: DateTime.utc_now()}
})

JavaScript Integration

Custom Hooks

Extend ApexUI with custom JavaScript hooks:

// assets/js/custom_hooks.js
import {ApexUIHooks} from "../deps/apex_ui/assets/js/hooks"

const CustomHooks = {
  ...ApexUIHooks,
  MyCustomWidget: {
    mounted() {
      // Custom widget logic
    },
    updated() {
      // Handle updates
    }
  }
}

export default CustomHooks

Three.js Visualization

For cluster visualization, ensure Three.js is available:

cd assets && npm install three
// assets/js/app.js  
import * as THREE from 'three'
window.THREE = THREE

Advanced Usage

Custom Data Adapters

Create custom data adapters for your specific use case:

defmodule MyApp.MetricsAdapter do
  @behaviour ApexUI.DataAdapter

  def format_chart_data(raw_data, chart_type) do
    case chart_type do
      :line ->
        Enum.map(raw_data, fn point ->
          %{x: point.timestamp, y: point.value}
        end)
      
      :pie ->
        Enum.map(raw_data, fn {label, value} ->
          %{label: label, value: value}
        end)
    end
  end
end

Component Extensions

Extend existing components:

defmodule MyApp.CustomChartWidget do
  use ApexUI.Components.Widgets.ChartWidget
  
  # Override or extend behavior
  def handle_event("custom_action", params, socket) do
    # Custom event handling
    {:noreply, socket}
  end
end

Examples

Complete Dashboard

defmodule MyAppWeb.MonitoringDashboard do
  use MyAppWeb, :live_view

  def render(assigns) do
    ~H"""
    <div class="min-h-screen bg-gray-900 text-green-400">
      <!-- Status Bar -->
      <.live_component 
        module={ApexUI.Components.Terminal.TerminalStatusBar}
        id="status-bar"
        metrics={@status_metrics}
      />
      
      <!-- Main Dashboard -->
      <div class="grid grid-cols-12 gap-4 p-4">
        <!-- System Metrics Chart -->
        <div class="col-span-8">
          <.live_component 
            module={ApexUI.Components.Widgets.ChartWidget}
            id="system-chart"
            chart_type={:line}
            data={@system_metrics}
            title="System Performance"
            real_time={true}
            height={400}
          />
        </div>
        
        <!-- Process Distribution -->
        <div class="col-span-4">
          <.live_component 
            module={ApexUI.Components.Widgets.ChartWidget}
            id="process-pie"
            chart_type={:pie}
            data={@process_distribution}
            title="Process Types"
            color_scheme={:multi}
          />
        </div>
        
        <!-- Process List -->
        <div class="col-span-12">
          <.live_component 
            module={ApexUI.Components.Widgets.ProcessListWidget}
            id="process-list"
            processes={@processes}
            filterable={true}
            actions={[:inspect, :kill]}
          />
        </div>
      </div>
    </div>
    """
  end
end

Real-time Metrics Collection

defmodule MyApp.MetricsCollector do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
  end

  def init(state) do
    schedule_collection()
    {:ok, state}
  end

  def handle_info(:collect_metrics, state) do
    metrics = %{
      cpu: get_cpu_usage(),
      memory: get_memory_usage(), 
      process_count: get_process_count(),
      timestamp: DateTime.utc_now()
    }

    Phoenix.PubSub.broadcast(MyApp.PubSub, "system_metrics", {
      :system_metrics, 
      metrics
    })

    schedule_collection()
    {:noreply, state}
  end

  defp schedule_collection do
    Process.send_after(self(), :collect_metrics, 1000)
  end

  defp get_cpu_usage, do: :cpu_sup.avg1() / 256 * 100
  defp get_memory_usage, do: :memsup.get_memory_data()
  defp get_process_count, do: length(Process.list())
end

Testing

Component Testing

defmodule ApexUI.Components.Widgets.ChartWidgetTest do
  use ExUnit.Case, async: true
  import Phoenix.LiveViewTest

  test "renders chart with data" do
    data = [%{x: 1, y: 10}, %{x: 2, y: 20}]
    
    html = render_component(ApexUI.Components.Widgets.ChartWidget, %{
      id: "test-chart",
      chart_type: :line,
      data: data,
      title: "Test Chart"
    })

    assert html =~ "Test Chart"
    assert html =~ "(2 points)"
  end
end

Integration Testing

defmodule MyAppWeb.DashboardLiveTest do
  use MyAppWeb.ConnCase
  import Phoenix.LiveViewTest

  test "displays real-time metrics", %{conn: conn} do
    {:ok, view, html} = live(conn, "/dashboard")
    
    assert html =~ "System Metrics"
    
    # Simulate metric update
    send(view.pid, {:system_metrics, %{cpu: 50.0, memory: 75.0}})
    
    assert render(view) =~ "50.0"
  end
end

Performance

Optimization Tips

  1. Limit data points: Use max_data_points to prevent memory issues
  2. Batch updates: Collect multiple metrics before broadcasting
  3. Use Process.send_after: Avoid blocking operations in LiveView processes
  4. Debounce rapid updates: Implement client-side debouncing for high-frequency data

Memory Management

# Limit chart data to prevent memory growth
def handle_info({:metrics_update, new_data}, socket) do
  current_data = socket.assigns.chart_data
  
  updated_data = 
    (current_data ++ [new_data])
    |> Enum.take(-100)  # Keep only last 100 points
  
  {:noreply, assign(socket, :chart_data, updated_data)}
end

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT License - see LICENSE for details.