deploy fix
This commit is contained in:
parent
8c06f1f8fa
commit
f1bc428086
7 changed files with 173 additions and 78 deletions
|
|
@ -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: {
|
||||
|
|
|
|||
52
assets/package-lock.json
generated
52
assets/package-lock.json
generated
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@types/chart.js": "^4.0.1",
|
||||
"chart.js": "^4.5.0",
|
||||
"phoenix_live_view": "^1.0.17"
|
||||
}
|
||||
}
|
||||
|
|
@ -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__)}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
144
lib/aprsme_web/live/status_live/connection_card_component.ex
Normal file
144
lib/aprsme_web/live/status_live/connection_card_component.ex
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue