deploy fix

This commit is contained in:
Graham McIntire 2025-07-05 12:51:28 -05:00
parent 8c06f1f8fa
commit f1bc428086
No known key found for this signature in database
7 changed files with 173 additions and 78 deletions

View file

@ -1,10 +1,13 @@
/// <reference types="chart.js" />
/// <reference types="phoenix_live_view" />
// The above triple-slash directives help TypeScript find types for Chart.js and Phoenix LiveView.
// If you use a custom build or alias, ensure types are available in node_modules.
// Declare global Chart object from CDN
declare global {
interface Window {
Chart: any;
chartInstances?: Map<string, any>;
}
}
import Chart from 'chart.js/auto';
import type { Hook } from 'phoenix_live_view';
// Type for LiveView hooks
type Hook = any;
// Type for weather history data
interface WeatherHistoryDatum {
@ -86,7 +89,7 @@ export const WeatherChartHooks: Record<string, Hook> = {
const colors = getThemeColors();
const canvas = self.el.querySelector('canvas') as HTMLCanvasElement;
if (!canvas) return;
self.chart = new Chart(canvas, {
self.chart = new window.Chart(canvas, {
type: 'line',
data: {
labels: times,
@ -137,7 +140,7 @@ export const WeatherChartHooks: Record<string, Hook> = {
const colors = getThemeColors();
const canvas = self.el.querySelector('canvas') as HTMLCanvasElement;
if (!canvas) return;
self.chart = new Chart(canvas, {
self.chart = new window.Chart(canvas, {
type: 'line',
data: { labels: times, datasets: [{ label: 'Humidity (%)', data: humidity, borderColor: 'green', backgroundColor: 'rgba(0, 255, 0, 0.1)', tension: 0.1 }] },
options: {
@ -176,7 +179,7 @@ export const WeatherChartHooks: Record<string, Hook> = {
const colors = getThemeColors();
const canvas = self.el.querySelector('canvas') as HTMLCanvasElement;
if (!canvas) return;
self.chart = new Chart(canvas, {
self.chart = new window.Chart(canvas, {
type: 'line',
data: { labels: times, datasets: [{ label: 'Pressure (mb)', data: pressure, borderColor: 'purple', backgroundColor: 'rgba(128, 0, 128, 0.1)', tension: 0.1 }] },
options: {
@ -216,7 +219,7 @@ export const WeatherChartHooks: Record<string, Hook> = {
const colors = getThemeColors();
const canvas = self.el.querySelector('canvas') as HTMLCanvasElement;
if (!canvas) return;
self.chart = new Chart(canvas, {
self.chart = new window.Chart(canvas, {
type: 'line',
data: { labels: times, datasets: [{ label: 'Wind Speed (mph)', data: windSpeed, borderColor: 'orange', backgroundColor: 'rgba(255, 165, 0, 0.1)', tension: 0.1 }, { label: 'Wind Gust (mph)', data: windGust, borderColor: 'brown', backgroundColor: 'rgba(165, 42, 42, 0.1)', tension: 0.1 }] },
options: {
@ -257,7 +260,7 @@ export const WeatherChartHooks: Record<string, Hook> = {
const colors = getThemeColors();
const canvas = self.el.querySelector('canvas') as HTMLCanvasElement;
if (!canvas) return;
self.chart = new Chart(canvas, {
self.chart = new window.Chart(canvas, {
type: 'line',
data: { labels: times, datasets: [{ label: 'Rain (1h)', data: rain1h, borderColor: 'blue', backgroundColor: 'rgba(0, 0, 255, 0.1)', tension: 0.1 }, { label: 'Rain (24h)', data: rain24h, borderColor: 'cyan', backgroundColor: 'rgba(0, 255, 255, 0.1)', tension: 0.1 }, { label: 'Rain (since midnight)', data: rainSinceMidnight, borderColor: 'navy', backgroundColor: 'rgba(0, 0, 128, 0.1)', tension: 0.1 }] },
options: {
@ -296,7 +299,7 @@ export const WeatherChartHooks: Record<string, Hook> = {
const colors = getThemeColors();
const canvas = self.el.querySelector('canvas') as HTMLCanvasElement;
if (!canvas) return;
self.chart = new Chart(canvas, {
self.chart = new window.Chart(canvas, {
type: 'line',
data: { labels: times, datasets: [{ label: 'Luminosity', data: luminosity, borderColor: 'yellow', backgroundColor: 'rgba(255, 255, 0, 0.1)', tension: 0.1 }] },
options: {

View file

@ -1,52 +0,0 @@
{
"name": "assets",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"@types/chart.js": "^4.0.1",
"chart.js": "^4.5.0",
"phoenix_live_view": "^1.0.17"
}
},
"node_modules/@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="
},
"node_modules/@types/chart.js": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-4.0.1.tgz",
"integrity": "sha512-OwXJ6Eg14eFpCsTG6sljiGnvVpit5R9rapG7nwDnQlljsdplVIOYVuCSwy07IduCtihrOwvOnbhXKLyp3nvPcw==",
"deprecated": "This is a stub types definition. chart.js provides its own type definitions, so you do not need this installed.",
"dependencies": {
"chart.js": "*"
}
},
"node_modules/chart.js": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
"integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/morphdom": {
"version": "2.7.5",
"resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.7.5.tgz",
"integrity": "sha512-z6bfWFMra7kBqDjQGHud1LSXtq5JJC060viEkQFMBX6baIecpkNr2Ywrn2OQfWP3rXiNFQRPoFjD8/TvJcWcDg=="
},
"node_modules/phoenix_live_view": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/phoenix_live_view/-/phoenix_live_view-1.0.17.tgz",
"integrity": "sha512-ydjiQeMkNrGv8OGYgT7BjIho/XM3eSHrk4FCs6qiLg9EfPf5M8w1W5Ax/E1tF9E0w6VFQsWm1fKFdWqzAezgjQ==",
"dependencies": {
"morphdom": "2.7.5"
}
}
}
}

View file

@ -1,7 +0,0 @@
{
"dependencies": {
"@types/chart.js": "^4.0.1",
"chart.js": "^4.5.0",
"phoenix_live_view": "^1.0.17"
}
}

View file

@ -63,7 +63,7 @@ config :esbuild,
version: "0.24.2",
default: [
args:
~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/* --define:global.L=window.L),
~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/* --external:chart.js/auto --define:global.L=window.L),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
]

View file

@ -22,6 +22,13 @@
data-source-map="false"
>
</script>
<script
defer
phx-track-static
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.js"
>
</script>
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script>
</head>

View file

@ -0,0 +1,144 @@
defmodule AprsmeWeb.StatusLive.ConnectionCardComponent do
@moduledoc false
use Phoenix.LiveComponent
import Phoenix.Component
def render(assigns) do
~H"""
<div class="mb-8">
<h2 class="text-xl font-semibold text-gray-900 mb-4">APRS-IS Connection</h2>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Status:</span>
<%= if @aprs_status.connected do %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
<svg class="w-2 h-2 mr-1" fill="currentColor" viewBox="0 0 8 8">
<circle cx="4" cy="4" r="3" />
</svg>
Connected
</span>
<% else %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
<svg class="w-2 h-2 mr-1" fill="currentColor" viewBox="0 0 8 8">
<circle cx="4" cy="4" r="3" />
</svg>
Disconnected
</span>
<% end %>
</div>
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Server:</span>
<span class="text-sm text-gray-900 font-mono">
{@aprs_status.server}:{@aprs_status.port}
</span>
</div>
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Login ID:</span>
<span class="text-sm text-gray-900 font-mono">{@aprs_status.login_id}</span>
</div>
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Connected Since:</span>
<span class="text-sm text-gray-900">
<%= if @aprs_status.connected_at do %>
{Calendar.strftime(@aprs_status.connected_at, "%Y-%m-%d %H:%M:%S UTC")}
<% else %>
<span class="text-gray-400">Not connected</span>
<% end %>
</span>
</div>
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Uptime:</span>
<span class={[
"text-sm font-medium",
if(@aprs_status.connected, do: "text-green-700", else: "text-red-600")
]}>
{AprsmeWeb.StatusLive.Index.format_uptime(@aprs_status.uptime_seconds)}
</span>
</div>
<div class="flex items-center col-span-2">
<span class="text-sm font-medium text-gray-500 mr-2">Filter:</span>
<span class="text-sm text-gray-900 font-mono bg-gray-100 px-2 py-1 rounded border">
{@aprs_status.filter}
</span>
</div>
</div>
<!-- Packet Statistics -->
<div class="mt-4 pt-4 border-t border-gray-200">
<h3 class="text-sm font-medium text-gray-700 mb-3">Packet Statistics</h3>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Total Packets:</span>
<span class="text-sm text-gray-900 font-mono">
{AprsmeWeb.StatusLive.Index.format_number(@aprs_status.packet_stats.total_packets)}
</span>
</div>
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Packets/Sec:</span>
<span class="text-sm text-gray-900 font-mono">
{@aprs_status.packet_stats.packets_per_second}
</span>
</div>
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Last Packet:</span>
<span class="text-sm text-gray-900">
<%= if @aprs_status.packet_stats.last_packet_at do %>
{AprsmeWeb.StatusLive.Index.format_time_ago(
@aprs_status.packet_stats.last_packet_at
)}
<% else %>
<span class="text-gray-400">None</span>
<% end %>
</span>
</div>
<div class="flex items-center">
<span class="text-sm font-medium text-gray-500 mr-2">Stored Packets:</span>
<span class="text-sm text-gray-900 font-mono">
{AprsmeWeb.StatusLive.Index.format_number(@aprs_status.stored_packet_count)}
</span>
</div>
</div>
</div>
<div class="mt-4 pt-4 border-t border-gray-200">
<div class="flex items-center justify-between">
<span class="text-sm font-medium text-gray-500">Connection Health:</span>
<div class="flex items-center">
<div class="flex items-center mr-2">
<%= for i <- 1..5 do %>
<svg
class={[
"w-4 h-4",
if(i <= @health_score, do: "text-green-400", else: "text-gray-300")
]}
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
<% end %>
</div>
<span class="text-sm font-medium text-gray-700">
{@health_score}/5
</span>
</div>
</div>
<p class="mt-1 text-xs text-gray-500">
{AprsmeWeb.StatusLive.Index.get_health_description(@health_score, @aprs_status.connected)}
</p>
</div>
</div>
</div>
"""
end
end

View file

@ -369,9 +369,9 @@ defmodule AprsmeWeb.StatusLive.Index do
Process.send_after(self(), :refresh_status, @refresh_interval)
end
defp format_uptime(seconds) when seconds <= 0, do: "Not connected"
def format_uptime(seconds) when seconds <= 0, do: "Not connected"
defp format_uptime(seconds) do
def format_uptime(seconds) do
days = div(seconds, 86_400)
hours = div(rem(seconds, 86_400), 3600)
minutes = div(rem(seconds, 3600), 60)
@ -399,7 +399,7 @@ defmodule AprsmeWeb.StatusLive.Index do
end
end
defp get_health_description(score, connected) do
def get_health_description(score, connected) do
case {score, connected} do
{1, false} -> "Disconnected - Connection issues detected"
{2, true} -> "Recently connected - Monitoring stability"
@ -410,7 +410,7 @@ defmodule AprsmeWeb.StatusLive.Index do
end
end
defp format_time_ago(datetime) do
def format_time_ago(datetime) do
diff_seconds = DateTime.diff(DateTime.utc_now(), datetime)
cond do
@ -421,7 +421,7 @@ defmodule AprsmeWeb.StatusLive.Index do
end
end
defp format_number(number) when is_integer(number) do
def format_number(number) when is_integer(number) do
number
|> Integer.to_string()
|> String.reverse()
@ -429,5 +429,5 @@ defmodule AprsmeWeb.StatusLive.Index do
|> String.reverse()
end
defp format_number(number), do: to_string(number)
def format_number(number), do: to_string(number)
end