defmodule DaProductAppWeb.AnalyticsLive do use DaProductAppWeb, :live_view alias DaProductApp.TerminalManagement @impl true def mount(_params, _session, socket) do # Subscribe to real-time updates if connected?(socket) do TerminalManagement.subscribe_to_device_updates() end socket = socket |> assign(:page_title, "Analytics & Reporting") |> assign(:current_tab, "overview") |> assign(:date_range, :last_30_days) |> assign(:loading, true) |> assign(:current_page , "anaylytics") |> load_analytics_data() {:ok, socket} end @impl true def handle_params(params, _url, socket) do tab = params["tab"] || "overview" date_range = String.to_atom(params["range"] || "last_30_days") socket = socket |> assign(:current_tab, tab) |> assign(:date_range, date_range) |> load_analytics_data() {:noreply, socket} end @impl true def handle_event("change_tab", %{"tab" => tab}, socket) do {:noreply, push_patch(socket, to: ~p"/analytics?tab=#{tab}&range=#{socket.assigns.date_range}")} end @impl true def handle_event("change_date_range", %{"range" => range}, socket) do date_range = String.to_atom(range) {:noreply, push_patch(socket, to: ~p"/analytics?tab=#{socket.assigns.current_tab}&range=#{range}")} end @impl true def handle_event("export_report", %{"format" => format}, socket) do analytics_data = socket.assigns.analytics_data case TerminalManagement.export_analytics_report(analytics_data, String.to_atom(format)) do {:ok, exported_data} -> # Generate filename with timestamp timestamp = DateTime.utc_now() |> DateTime.to_unix() # Determine content type and push appropriate download event case format do "json" -> filename = "analytics_report_#{timestamp}.json" {:noreply, push_event(socket, "download_file", %{ content: exported_data, filename: filename, type: "application/json" })} "csv" -> filename = "analytics_report_#{timestamp}.csv" {:noreply, push_event(socket, "download_file", %{ content: exported_data, filename: filename, type: "text/csv" })} "pdf" -> filename = "analytics_report_#{timestamp}.html" {:noreply, push_event(socket, "download_file", %{ content: exported_data, filename: filename, type: "text/html" })} _ -> socket = put_flash(socket, :error, "Unsupported format: #{format}") {:noreply, socket} end {:error, reason} -> socket = put_flash(socket, :error, "Export failed: #{reason}") {:noreply, socket} end end @impl true def handle_event("schedule_report", params, socket) do schedule_config = %{ schedule: params["schedule"], recipients: String.split(params["recipients"], ","), format: String.to_atom(params["format"]), filters: %{} } case TerminalManagement.schedule_analytics_report(schedule_config) do %{status: "scheduled"} = result -> socket = put_flash(socket, :info, "Report scheduled successfully (ID: #{result.report_id})") {:noreply, socket} _ -> socket = put_flash(socket, :error, "Failed to schedule report") {:noreply, socket} end end @impl true def handle_event("refresh_analytics", _params, socket) do socket = socket |> assign(:loading, true) |> load_analytics_data() {:noreply, socket} end @impl true def handle_info({:device_update, _device_data}, socket) do # Refresh analytics when device data changes socket = load_analytics_data(socket) {:noreply, socket} end @impl true def handle_info({:monitoring_update, _monitoring_data}, socket) do # Refresh analytics when monitoring data changes socket = load_analytics_data(socket) {:noreply, socket} end defp load_analytics_data(socket) do date_range = socket.assigns[:date_range] || :last_30_days analytics_data = TerminalManagement.generate_device_analytics(date_range, %{ include_utilization: true, include_cost_analysis: true, include_predictions: true, include_location_data: true }) # Load actual device data for geographic mapping devices = TerminalManagement.list_terminals_with_latest_status() # Prepare device data for map with location information map_devices = Enum.map(devices, fn device -> location = TerminalManagement.get_latest_terminal_location(device.id) %{ serial_number: device.serial_number, status: device.status, area: device.area, vendor: device.vendor, model: device.model, lat: parse_float(location.lat), lng: parse_float(location.lng) } end) |> Enum.filter(fn device -> # Only include devices with valid coordinates device.lat != nil and device.lng != nil and is_number(device.lat) and is_number(device.lng) end) socket |> assign(:analytics_data, analytics_data) |> assign(:map_devices, map_devices) |> assign(:loading, false) end # Helper function to parse float values defp parse_float(nil), do: nil defp parse_float(value) when is_number(value), do: value defp parse_float(value) when is_binary(value) do case Float.parse(value) do {float_val, _} -> float_val :error -> nil end end defp parse_float(_), do: nil @impl true def render(assigns) do ~H"""

πŸ“Š Analytics & Reporting

Comprehensive insights into your device ecosystem

<%= if @loading do %>
Loading analytics data...
<% end %>
<%= case @current_tab do %> <% "overview" -> %> <%= render_overview_tab(assigns) %> <% "trends" -> %> <%= render_trends_tab(assigns) %> <% "performance" -> %> <%= render_performance_tab(assigns) %> <% "geographic" -> %> <%= render_geographic_tab(assigns) %> <% "compliance" -> %> <%= render_compliance_tab(assigns) %> <% "export" -> %> <%= render_export_tab(assigns) %> <% _ -> %> <%= render_overview_tab(assigns) %> <% end %>
""" end defp render_overview_tab(assigns) do ~H"""

Total Devices

<%= @analytics_data.summary.total_devices %>

+<%= @analytics_data.summary.new_devices %> new devices this period

Active Devices

<%= @analytics_data.summary.active_devices %>

<%= @analytics_data.summary.activation_rate %>% activation rate

Performance Score

<%= @analytics_data.performance.overall_kpis.performance_grade %>

<%= @analytics_data.performance.overall_kpis.reliability_score %>% reliability

Compliance Rate

<%= @analytics_data.compliance.compliance_score %>%

<%= @analytics_data.compliance.device_compliance.non_compliant_devices %> devices need attention

Device Status Distribution

<%= for {status, count} <- @analytics_data.summary.status_distribution do %>

<%= count %>

<%= status %>

<% end %>

Top Device Models

<%= for model_data <- Enum.take(@analytics_data.summary.model_distribution, 5) do %>
<%= model_data.model || "SR-600" %> <%= model_data.count %> devices
<% end %>

Top Vendors

<%= for vendor_data <- Enum.take(@analytics_data.summary.vendor_distribution, 5) do %>
<%= vendor_data.vendor || "Morefun" %> <%= vendor_data.count %> devices
<% end %>
""" end defp render_trends_tab(assigns) do ~H"""

πŸ“ˆ Growth Trends

<%= @analytics_data.trends.growth_rate %>%

Growth Rate

<%= length(@analytics_data.trends.daily.registrations) %>

Active Days

<%= length(@analytics_data.trends.weekly) %>

Weeks Tracked

Daily Device Registrations

<%= for reg_data <- Enum.take(@analytics_data.trends.daily.registrations, 10) do %>
<%= reg_data.date %> <%= reg_data.registrations %> registrations
<% end %>

Weekly Trends

<%= for week_data <- @analytics_data.trends.weekly do %>

Week <%= week_data.week %>

<%= week_data.registrations %> new registrations

<% end %>
""" end defp render_performance_tab(assigns) do ~H"""

<%= @analytics_data.performance.overall_kpis.overall_success_rate %>%

Overall Success Rate

<%= @analytics_data.performance.overall_kpis.operational_efficiency %>%

Operational Efficiency

<%= @analytics_data.performance.overall_kpis.reliability_score %>%

Reliability Score

<%= @analytics_data.performance.overall_kpis.performance_grade %>

Performance Grade

πŸ“€ Parameter Push Performance

Total Pushes

<%= @analytics_data.performance.parameter_push.total_pushes %>

Success Rate

<%= @analytics_data.performance.parameter_push.success_rate %>%

Avg Response Time

<%= @analytics_data.performance.parameter_push.avg_response_time %>

Most Pushed Parameters:

<%= for param <- @analytics_data.performance.parameter_push.most_pushed_parameters do %> <%= param %> <% end %>

πŸ”„ OTA Update Performance

Total Updates

<%= @analytics_data.performance.ota_updates.total_updates %>

Successful

<%= @analytics_data.performance.ota_updates.successful_updates %>

Pending

<%= @analytics_data.performance.ota_updates.pending_updates %>

Failed

<%= @analytics_data.performance.ota_updates.failed_updates %>

⚑ Response Time Analytics

<%= @analytics_data.performance.response_times.avg_response_time %>ms

Average

<%= @analytics_data.performance.response_times.p95_response_time %>ms

95th Percentile

<%= @analytics_data.performance.response_times.p99_response_time %>ms

99th Percentile

<%= @analytics_data.performance.response_times.fastest_response %>ms

Fastest

<%= @analytics_data.performance.response_times.slowest_response %>ms

Slowest

""" end defp render_geographic_tab(assigns) do ~H"""

<%= @analytics_data.geographic.coverage_metrics.total_areas_covered %>

Areas Covered

<%= @analytics_data.geographic.coverage_metrics.avg_devices_per_area %>

Avg Devices/Area

<%= @analytics_data.geographic.coverage_metrics.area_coverage_score %>

Coverage Score

🌍 Geographic Distribution

<%= for area <- @analytics_data.geographic.area_distribution do %>

<%= area.area %>

<%= area.online_count %>/<%= area.device_count %> online

<%= area.device_count %>

devices

<% end %>
<%= if @analytics_data.geographic.geographic_activity.enabled do %>

πŸ“ Location Analytics

Regional Clusters

<%= for cluster <- @analytics_data.geographic.geographic_activity.location_clusters do %>
<%= cluster.region %>
<%= cluster.device_count %> devices (<%= cluster.activity_score %>% active)
<% end %>

Mobility Patterns

Stationary Devices <%= @analytics_data.geographic.geographic_activity.mobility_patterns.stationary_devices %>%
Mobile Devices <%= @analytics_data.geographic.geographic_activity.mobility_patterns.mobile_devices %>%
Avg Daily Movement <%= @analytics_data.geographic.geographic_activity.mobility_patterns.avg_daily_movement %>
<% end %>

πŸ—ΊοΈ Device Location Map

Showing <%= length(@map_devices) %> devices with location data
Online
Offline
Unknown
""" end defp render_compliance_tab(assigns) do ~H"""

<%= @analytics_data.compliance.compliance_score %>%

Overall Compliance

<%= @analytics_data.compliance.security_metrics.security_score %>%

Security Score

<%= @analytics_data.compliance.update_compliance.update_compliance_rate %>%

Update Compliance

πŸ›‘οΈ Device Compliance Status

Compliant Devices <%= @analytics_data.compliance.device_compliance.compliant_devices %>
Non-Compliant Devices <%= @analytics_data.compliance.device_compliance.non_compliant_devices %>
Compliance Rate <%= @analytics_data.compliance.device_compliance.compliance_rate %>%

Common Violations

<%= for violation <- @analytics_data.compliance.device_compliance.common_violations do %> <%= violation %> <% end %>

πŸ”„ Update Compliance

<%= @analytics_data.compliance.update_compliance.devices_requiring_updates %>

Need Updates

<%= @analytics_data.compliance.update_compliance.overdue_updates %>

Overdue

<%= @analytics_data.compliance.update_compliance.update_compliance_rate %>%

Compliance Rate

<%= @analytics_data.compliance.update_compliance.avg_update_delay %>

Avg Delay

πŸ”’ Security Metrics

<%= @analytics_data.compliance.security_metrics.vulnerabilities_detected %>

Vulnerabilities

<%= @analytics_data.compliance.security_metrics.security_patches_applied %>

Patches Applied

<%= @analytics_data.compliance.security_metrics.encryption_compliance %>%

Encryption Compliance

<%= @analytics_data.compliance.security_metrics.security_score %>

Security Score

""" end defp render_export_tab(assigns) do ~H"""

πŸ“€ Quick Export

⏰ Schedule Automated Reports

πŸ“Š Current Report Summary

Generated: <%= @analytics_data.generated_at |> DateTime.to_string() %>

Date Range: <%= @analytics_data.date_range.start |> DateTime.to_date() %> to <%= @analytics_data.date_range.end |> DateTime.to_date() %>

Total Devices: <%= @analytics_data.summary.total_devices %>

Performance Grade: <%= @analytics_data.performance.overall_kpis.performance_grade %>

Compliance Score: <%= @analytics_data.compliance.compliance_score %>%

Coverage Areas: <%= @analytics_data.geographic.coverage_metrics.total_areas_covered %>

""" end end