towerops/lib/towerops_web/live/map_live/index.html.heex

188 lines
7.4 KiB
Text

<Layouts.authenticated
flash={@flash}
current_scope={@current_scope}
active_page="sites-map"
>
<.header>
<span class="flex items-center gap-2">
{t("Sites Map")}
<span class="inline-flex items-center rounded-full bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10 dark:bg-blue-400/10 dark:text-blue-400 dark:ring-blue-400/30">
{t("Geographic")}
</span>
</span>
<:subtitle>{t("Geographic locations of your sites")}</:subtitle>
<:actions>
<.button phx-click="refresh_map">
<.icon name="hero-arrow-path" class="h-5 w-5" /> Refresh
</.button>
<.button navigate={~p"/sites/new"} variant="primary">
<.icon name="hero-plus" class="h-5 w-5" /> Add Site
</.button>
</:actions>
</.header>
<div class="mb-6">
<div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-white/10 overflow-hidden">
<div class="p-4 border-b border-gray-200 dark:border-white/10">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-gray-900 dark:text-white">
{t("Geographic Site Locations")}
</h3>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
{t("Click markers to view site details")}
</p>
</div>
<div class="flex items-center space-x-2">
<span class="text-xs text-gray-500 dark:text-gray-400">
Showing {length(@sites)} sites with coordinates
</span>
</div>
</div>
</div>
<!-- Map Container -->
<div
id="leaflet-map"
phx-hook="SitesMap"
phx-update="ignore"
class="w-full"
style="height: 600px;"
data-sites={Jason.encode!(@sites)}
>
<!-- Loading State -->
<div class="flex items-center justify-center h-full">
<div class="text-center">
<.icon name="hero-arrow-path" class="h-8 w-8 text-gray-400 animate-spin mx-auto" />
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">Loading map...</p>
</div>
</div>
</div>
</div>
</div>
<!-- Stats and Site List -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Site Statistics -->
<div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-white/10 p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-4">{t("Statistics")}</h3>
<div class="space-y-4">
<div>
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">Total Sites</div>
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
{Sites.count_organization_sites(@organization.id)}
</div>
</div>
<div>
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">Geocoded Sites</div>
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
{length(@sites)}
</div>
</div>
<div>
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">Coverage</div>
<div class="text-lg font-semibold text-gray-900 dark:text-white">
<%= if Sites.count_organization_sites(@organization.id) > 0 do %>
{round(length(@sites) / Sites.count_organization_sites(@organization.id) * 100)}%
<% else %>
0%
<% end %>
</div>
</div>
</div>
</div>
<!-- Sites with Coordinates -->
<div class="lg:col-span-2 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-white/10">
<div class="px-6 py-4 border-b border-gray-200 dark:border-white/10">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
{t("Sites with Geographic Data")}
</h3>
</div>
<div class="max-h-96 overflow-y-auto">
<%= if length(@sites) > 0 do %>
<div class="divide-y divide-gray-200 dark:divide-white/10">
<%= for site <- @sites do %>
<div class="px-6 py-4 hover:bg-gray-50 dark:hover:bg-gray-700/50">
<div class="flex items-start justify-between">
<div class="flex-1 min-w-0">
<div class="flex items-center">
<h4 class="text-sm font-medium text-gray-900 dark:text-white truncate">
{site.name}
</h4>
<%= if site.device_count > 0 do %>
<span class="ml-2 inline-flex items-center rounded-full bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-700 dark:bg-blue-900 dark:text-blue-300">
{site.device_count} devices
</span>
<% end %>
</div>
<%= if site.address do %>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
{site.address}
</p>
<% end %>
<%= if site.description do %>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
{site.description}
</p>
<% end %>
<p class="mt-1 text-xs text-gray-400">
{Float.round(site.latitude, 4)}, {Float.round(site.longitude, 4)}
</p>
</div>
<div class="ml-4">
<.link
navigate={~p"/sites/#{site.id}"}
class="text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium"
>
View →
</.link>
</div>
</div>
</div>
<% end %>
</div>
<% else %>
<div class="px-6 py-8 text-center">
<.icon name="hero-map-pin" class="mx-auto h-12 w-12 text-gray-400" />
<h3 class="mt-2 text-sm font-medium text-gray-900 dark:text-white">
{t("No geocoded sites")}
</h3>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
{t("Add addresses and coordinates to your sites to see them on the map.")}
</p>
<div class="mt-4">
<.button navigate={~p"/sites"}>
{t("Manage Sites")}
</.button>
</div>
</div>
<% end %>
</div>
</div>
</div>
</Layouts.authenticated>
<script>
// Add Leaflet CSS and JS
document.addEventListener("DOMContentLoaded", function() {
if (!document.getElementById("leaflet-css")) {
const css = document.createElement("link");
css.id = "leaflet-css";
css.rel = "stylesheet";
css.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
css.integrity = "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=";
css.crossOrigin = "";
document.head.appendChild(css);
}
if (!document.getElementById("leaflet-js")) {
const js = document.createElement("script");
js.id = "leaflet-js";
js.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js";
js.integrity = "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=";
js.crossOrigin = "";
document.head.appendChild(js);
}
});
</script>