add marketing site

This commit is contained in:
Graham McIntire 2026-01-06 14:34:48 -06:00
parent 7b1aa087d2
commit 3b9fffdd07
No known key found for this signature in database
8 changed files with 294 additions and 12 deletions

View file

@ -303,6 +303,78 @@ defmodule ToweropsWeb.Layouts do
"""
end
@doc """
Renders the marketing layout.
Use this for public-facing marketing pages.
## Examples
<Layouts.marketing flash={@flash}>
<h1>Marketing Content</h1>
</Layouts.marketing>
"""
attr :flash, :map, required: true, doc: "the map of flash messages"
slot :inner_block, required: true
def marketing(assigns) do
~H"""
<div class="bg-white">
<header class="py-10">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<nav class="relative z-50 flex justify-between">
<div class="flex items-center md:gap-x-12">
<.link navigate={~p"/"} aria-label="Home">
<img src="/images/towerops_logo.png" alt="Towerops" class="h-10 w-auto" />
</.link>
</div>
<div class="flex items-center gap-x-5 md:gap-x-8">
<div class="hidden md:block">
<.link
navigate={~p"/users/log-in"}
class="inline-block rounded-lg px-2 py-1 text-sm text-slate-700 hover:bg-slate-100 hover:text-slate-900"
>
Sign in
</.link>
</div>
<.link
navigate={~p"/users/register"}
class="group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus-visible:outline-2 focus-visible:outline-offset-2 bg-blue-600 text-white hover:text-slate-100 hover:bg-blue-500 active:bg-blue-800 active:text-blue-100 focus-visible:outline-blue-600"
>
<span>
Get started <span class="hidden lg:inline">today</span>
</span>
</.link>
</div>
</nav>
</div>
</header>
<main>
{render_slot(@inner_block)}
</main>
<footer class="bg-slate-50">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="py-16">
<img src="/images/towerops_logo.png" alt="Towerops" class="mx-auto h-10 w-auto" />
</div>
<div class="flex flex-col items-center border-t border-slate-400/10 py-10 sm:flex-row-reverse sm:justify-between">
<div class="flex gap-x-6"></div>
<p class="mt-6 text-sm text-slate-500 sm:mt-0">
Copyright &copy; 2025 Towerops. All rights reserved.
</p>
</div>
</div>
</footer>
</div>
<.flash_group flash={@flash} />
"""
end
@doc """
Renders the admin layout.

View file

@ -3,11 +3,10 @@ defmodule ToweropsWeb.PageController do
def home(conn, _params) do
# If user is authenticated, redirect to organization list
# Otherwise, redirect to login page
if conn.assigns.current_scope && conn.assigns.current_scope.user do
redirect(conn, to: ~p"/orgs")
else
redirect(conn, to: ~p"/users/log-in")
render(conn, :home, layout: false)
end
end
end

View file

@ -0,0 +1,213 @@
<Layouts.marketing flash={@flash}>
<!-- Hero Section -->
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pt-20 pb-16 text-center lg:pt-32">
<h1 class="mx-auto max-w-4xl font-display text-5xl font-medium tracking-tight text-slate-900 sm:text-7xl">
Network monitoring
<span class="relative whitespace-nowrap text-blue-600">
<svg
aria-hidden="true"
viewBox="0 0 418 42"
class="absolute top-2/3 left-0 h-[0.58em] w-full fill-blue-300/70"
preserveAspectRatio="none"
>
<path d="M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z" />
</svg>
<span class="relative">made simple</span>
</span>
for network operators.
</h1>
<p class="mx-auto mt-6 max-w-2xl text-lg tracking-tight text-slate-700">
Monitor network equipment via SNMP, track performance metrics in real-time, and respond to alerts instantly.
Built specifically for telecom tower infrastructure.
</p>
<div class="mt-10 flex justify-center gap-x-6">
<.link
navigate={~p"/users/register"}
class="group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus-visible:outline-2 focus-visible:outline-offset-2 bg-blue-600 text-white hover:text-slate-100 hover:bg-blue-500 active:bg-blue-800 active:text-blue-100 focus-visible:outline-blue-600"
>
Get 1 month free
</.link>
</div>
</div>
<!-- Primary Features Section -->
<section
id="features"
aria-label="Features for monitoring your infrastructure"
class="relative overflow-hidden bg-blue-600 pt-20 pb-28 sm:py-32"
>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 relative">
<div class="max-w-2xl md:mx-auto md:text-center xl:max-w-none">
<h2 class="font-display text-3xl tracking-tight text-white sm:text-4xl md:text-5xl">
Everything you need to monitor your network.
</h2>
<p class="mt-6 text-lg tracking-tight text-blue-100">
Comprehensive SNMP monitoring with real-time alerts and performance tracking.
</p>
</div>
<div class="mt-16 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
<!-- Feature 1 -->
<div class="relative rounded-2xl bg-white/10 p-8 ring-1 ring-white/10">
<div class="flex items-center gap-x-3">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-white/20">
<.icon name="hero-signal" class="h-6 w-6 text-white" />
</div>
<h3 class="text-lg font-semibold text-white">SNMP Monitoring</h3>
</div>
<p class="mt-4 text-sm text-blue-100">
Automatic device discovery via SNMP. Track interface statistics, sensor readings,
and equipment health with support for MikroTik, Cisco, and generic SNMP devices.
</p>
</div>
<!-- Feature 2 -->
<div class="relative rounded-2xl bg-white/10 p-8 ring-1 ring-white/10">
<div class="flex items-center gap-x-3">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-white/20">
<.icon name="hero-bell-alert" class="h-6 w-6 text-white" />
</div>
<h3 class="text-lg font-semibold text-white">Real-time Alerts</h3>
</div>
<p class="mt-4 text-sm text-blue-100">
Get instant notifications when equipment goes down or thresholds are exceeded.
Configurable alert rules with email notifications to keep you informed.
</p>
</div>
<!-- Feature 3 -->
<div class="relative rounded-2xl bg-white/10 p-8 ring-1 ring-white/10">
<div class="flex items-center gap-x-3">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-white/20">
<.icon name="hero-chart-bar" class="h-6 w-6 text-white" />
</div>
<h3 class="text-lg font-semibold text-white">Performance Charts</h3>
</div>
<p class="mt-4 text-sm text-blue-100">
Visualize historical data with interactive time-series charts. Track bandwidth,
temperature sensors, and custom metrics over configurable time ranges.
</p>
</div>
</div>
</div>
</section>
<!-- Secondary Features Section -->
<section
id="secondary-features"
aria-label="Features for managing your infrastructure"
class="pt-20 pb-14 sm:pt-32 sm:pb-20 lg:pb-32"
>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="mx-auto max-w-2xl md:text-center">
<h2 class="font-display text-3xl tracking-tight text-slate-900 sm:text-4xl">
Built for multi-site operations.
</h2>
<p class="mt-4 text-lg tracking-tight text-slate-700">
Organize equipment by site, collaborate with your team, and maintain clear visibility
across your entire network infrastructure.
</p>
</div>
<div class="mt-20 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
<!-- Feature 1 -->
<div>
<div class="w-9 rounded-lg bg-blue-600">
<svg aria-hidden="true" class="h-9 w-9" fill="none">
<path
d="M8 17a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2Z"
fill="#fff"
opacity=".5"
/>
<path
d="M8 24a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2Z"
fill="#fff"
opacity=".3"
/>
<path
d="M8 10a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2Z"
fill="#fff"
/>
</svg>
</div>
<h3 class="mt-6 text-sm font-medium text-blue-600">Site Management</h3>
<p class="mt-2 font-display text-xl text-slate-900">
Organize equipment by physical location
</p>
<p class="mt-4 text-sm text-slate-600">
Group devices by tower site or facility. View site-wide status and health metrics
at a glance, then drill down into individual equipment details.
</p>
</div>
<!-- Feature 2 -->
<div>
<div class="w-9 rounded-lg bg-blue-600">
<svg aria-hidden="true" class="h-9 w-9" fill="none">
<path
opacity=".5"
d="M25.778 25.778c.39.39 1.027.393 1.384-.028A11.952 11.952 0 0 0 30 18c0-6.627-5.373-12-12-12S6 11.373 6 18c0 2.954 1.067 5.659 2.838 7.75.357.421.993.419 1.384.028.39-.39.386-1.02.036-1.448A9.959 9.959 0 0 1 8 18c0-5.523 4.477-10 10-10s10 4.477 10 10a9.959 9.959 0 0 1-2.258 6.33c-.35.427-.354 1.058.036 1.448Z"
fill="#fff"
/>
<path
d="M12 28.395V28a6 6 0 0 1 12 0v.395A11.945 11.945 0 0 1 18 30c-2.186 0-4.235-.584-6-1.605ZM21 16.5c0-1.933-.5-3.5-3-3.5s-3 1.567-3 3.5 1.343 3.5 3 3.5 3-1.567 3-3.5Z"
fill="#fff"
/>
</svg>
</div>
<h3 class="mt-6 text-sm font-medium text-blue-600">Team Collaboration</h3>
<p class="mt-2 font-display text-xl text-slate-900">Share access with your team</p>
<p class="mt-4 text-sm text-slate-600">
Create organizations and invite team members. Control access with owner, admin, and member roles.
Each team member sees only what they need.
</p>
</div>
<!-- Feature 3 -->
<div>
<div class="w-9 rounded-lg bg-blue-600">
<svg aria-hidden="true" class="h-9 w-9" fill="none">
<defs>
<linearGradient
id="reporting-gradient"
x1="11.5"
y1="18"
x2="36"
y2="15.5"
gradientUnits="userSpaceOnUse"
>
<stop offset=".194" stop-color="#fff" />
<stop offset="1" stop-color="#6692F1" />
</linearGradient>
</defs>
<path
d="m30 15-4 5-4-11-4 18-4-11-4 7-4-5"
stroke="url(#reporting-gradient)"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
<h3 class="mt-6 text-sm font-medium text-blue-600">Historical Data</h3>
<p class="mt-2 font-display text-xl text-slate-900">Track trends over time</p>
<p class="mt-4 text-sm text-slate-600">
All sensor readings and interface statistics are stored for historical analysis.
View data from the past hour, day, week, or month with interactive charts.
</p>
</div>
</div>
</div>
</section>
<!-- Call to Action Section -->
<section class="relative overflow-hidden bg-blue-600 py-32">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 relative">
<div class="mx-auto max-w-lg text-center">
<h2 class="font-display text-3xl tracking-tight text-white sm:text-4xl">
Start monitoring today
</h2>
<p class="mt-4 text-lg tracking-tight text-white">
Get started in minutes. No credit card required for the free trial.
</p>
<.link
navigate={~p"/users/register"}
class="group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus-visible:outline-2 focus-visible:outline-offset-2 mt-10 bg-white text-slate-900 hover:bg-blue-50 active:bg-blue-200 active:text-slate-600 focus-visible:outline-white"
>
Get 1 month free
</.link>
</div>
</div>
</section>
</Layouts.marketing>

View file

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 71 48" fill="currentColor" aria-hidden="true">
<path
d="m26.371 33.477-.552-.1c-3.92-.729-6.397-3.1-7.57-6.829-.733-2.324.597-4.035 3.035-4.148 1.995-.092 3.362 1.055 4.57 2.39 1.557 1.72 2.984 3.558 4.514 5.305 2.202 2.515 4.797 4.134 8.347 3.634 3.183-.448 5.958-1.725 8.371-3.828.363-.316.761-.592 1.144-.886l-.241-.284c-2.027.63-4.093.841-6.205.735-3.195-.16-6.24-.828-8.964-2.582-2.486-1.601-4.319-3.746-5.19-6.611-.704-2.315.736-3.934 3.135-3.6.948.133 1.746.56 2.463 1.165.583.493 1.143 1.015 1.738 1.493 2.8 2.25 6.712 2.375 10.265-.068-5.842-.026-9.817-3.24-13.308-7.313-1.366-1.594-2.7-3.216-4.095-4.785-2.698-3.036-5.692-5.71-9.79-6.623C12.8-.623 7.745.14 2.893 2.361 1.926 2.804.997 3.319 0 4.149c.494 0 .763.006 1.032 0 2.446-.064 4.28 1.023 5.602 3.024.962 1.457 1.415 3.104 1.761 4.798.513 2.515.247 5.078.544 7.605.761 6.494 4.08 11.026 10.26 13.346 2.267.852 4.591 1.135 7.172.555ZM10.751 3.852c-.976.246-1.756-.148-2.56-.962 1.377-.343 2.592-.476 3.897-.528-.107.848-.607 1.306-1.336 1.49Zm32.002 37.924c-.085-.626-.62-.901-1.04-1.228-1.857-1.446-4.03-1.958-6.333-2-1.375-.026-2.735-.128-4.031-.61-.595-.22-1.26-.505-1.244-1.272.015-.78.693-1 1.31-1.184.505-.15 1.026-.247 1.6-.382-1.46-.936-2.886-1.065-4.787-.3-2.993 1.202-5.943 1.06-8.926-.017-1.684-.608-3.179-1.563-4.735-2.408l-.077.057c1.29 2.115 3.034 3.817 5.004 5.271 3.793 2.8 7.936 4.471 12.784 3.73A66.714 66.714 0 0 1 37 40.877c1.98-.16 3.866.398 5.753.899Zm-9.14-30.345c-.105-.076-.206-.266-.42-.069 1.745 2.36 3.985 4.098 6.683 5.193 4.354 1.767 8.773 2.07 13.293.51 3.51-1.21 6.033-.028 7.343 3.38.19-3.955-2.137-6.837-5.843-7.401-2.084-.318-4.01.373-5.962.94-5.434 1.575-10.485.798-15.094-2.553Zm27.085 15.425c.708.059 1.416.123 2.124.185-1.6-1.405-3.55-1.517-5.523-1.404-3.003.17-5.167 1.903-7.14 3.972-1.739 1.824-3.31 3.87-5.903 4.604.043.078.054.117.066.117.35.005.699.021 1.047.005 3.768-.17 7.317-.965 10.14-3.7.89-.86 1.685-1.817 2.544-2.71.716-.746 1.584-1.159 2.645-1.07Zm-8.753-4.67c-2.812.246-5.254 1.409-7.548 2.943-1.766 1.18-3.654 1.738-5.776 1.37-.374-.066-.75-.114-1.124-.17l-.013.156c.135.07.265.151.405.207.354.14.702.308 1.07.395 4.083.971 7.992.474 11.516-1.803 2.221-1.435 4.521-1.707 7.013-1.336.252.038.503.083.756.107.234.022.479.255.795.003-2.179-1.574-4.526-2.096-7.094-1.872Zm-10.049-9.544c1.475.051 2.943-.142 4.486-1.059-.452.04-.643.04-.827.076-2.126.424-4.033-.04-5.733-1.383-.623-.493-1.257-.974-1.889-1.457-2.503-1.914-5.374-2.555-8.514-2.5.05.154.054.26.108.315 3.417 3.455 7.371 5.836 12.369 6.008Zm24.727 17.731c-2.114-2.097-4.952-2.367-7.578-.537 1.738.078 3.043.632 4.101 1.728a13 13 0 0 0 1.182 1.106c1.6 1.29 4.311 1.352 5.896.155-1.861-.726-1.861-.726-3.601-2.452Zm-21.058 16.06c-1.858-3.46-4.981-4.24-8.59-4.008a9.667 9.667 0 0 1 2.977 1.39c.84.586 1.547 1.311 2.243 2.055 1.38 1.473 3.534 2.376 4.962 2.07-.656-.412-1.238-.848-1.592-1.507Zl-.006.006-.036-.004.021.018.012.053Za.127.127 0 0 0 .015.043c.005.008.038 0 .058-.002Zl-.008.01.005.026.024.014Z"
fill="#FD4F00"
/>
</svg>

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

View file

@ -112,10 +112,13 @@ defmodule Towerops.Alerts.AlertNotifierTest do
message: "Equipment is down"
})
{:ok, _results} = AlertNotifier.deliver_alert_notification(alert)
{:ok, results} = AlertNotifier.deliver_alert_notification(alert)
assert length(results) == 2
# Verify email sent to owner contains all expected information
assert_email_sent(fn email ->
email.to == [{"", owner.email}] &&
email.to |> List.first() |> elem(1) == owner.email &&
email.subject =~ "Equipment Down" &&
email.text_body =~ organization.name &&
email.text_body =~ equipment.name &&

View file

@ -3,9 +3,10 @@ defmodule ToweropsWeb.PageControllerTest do
import Towerops.AccountsFixtures
test "GET / redirects to login when not authenticated", %{conn: conn} do
test "GET / renders marketing page when not authenticated", %{conn: conn} do
conn = get(conn, ~p"/")
assert redirected_to(conn) == ~p"/users/log-in"
assert html_response(conn, 200) =~ "Network monitoring"
assert html_response(conn, 200) =~ "made simple"
end
test "GET / redirects to organizations when authenticated", %{conn: conn} do