Packages

phoenix_kit

1.7.178
1.7.209 1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib modules maintenance web maintenance_page_live.ex
Raw

lib/modules/maintenance/web/maintenance_page_live.ex

defmodule PhoenixKitWeb.Live.Modules.Maintenance.Page do
@moduledoc """
Standalone maintenance page LiveView for admin preview and direct access.
The main maintenance enforcement is handled by the layout override in
`check_maintenance_mode/1` (auth.ex) — non-admin users see the maintenance
layout on whatever page they're on, without a URL change.
This LiveView serves as:
- Admin preview at `/maintenance` (with banner + settings link)
- Fallback target for the plug's 503 response on non-LiveView controller routes
Supports countdown timer, PubSub status updates, and admin preview.
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Modules.Maintenance
alias PhoenixKit.Users.Auth.Scope
alias PhoenixKit.Utils.Routes
@impl true
def mount(_params, _session, socket) do
scope = socket.assigns[:phoenix_kit_current_scope]
is_admin = scope && (Scope.admin?(scope) || Scope.owner?(scope))
# If maintenance is not active and user is not admin, redirect away
if Maintenance.active?() or is_admin do
if connected?(socket) do
Maintenance.subscribe()
end
config = Maintenance.get_config()
scheduled_end = Maintenance.get_scheduled_end()
# Calculate ISO 8601 end time string for the JS countdown hook
end_iso =
case scheduled_end do
%DateTime{} = dt -> DateTime.to_iso8601(dt)
_ -> nil
end
socket =
socket
|> assign(:page_title, config.header)
|> assign(:header, config.header)
|> assign(:subtext, config.subtext)
|> assign(:is_admin, is_admin)
|> assign(:is_active, config.active)
|> assign(:scheduled_end_iso, end_iso)
{:ok, socket, layout: false}
else
{:ok, push_navigate(socket, to: Routes.path("/"))}
end
end
# PubSub: maintenance status changed
@impl true
def handle_info({:maintenance_status_changed, %{active: false}}, socket) do
if socket.assigns.is_admin do
# Admins stay — update the status indicator
{:noreply, assign(socket, :is_active, false)}
else
{:noreply, push_navigate(socket, to: Routes.path("/"))}
end
end
def handle_info({:maintenance_status_changed, %{active: true}}, socket) do
{:noreply, assign(socket, :is_active, true)}
end
# Catch-all for unexpected messages (e.g. other PubSub topics, system messages)
def handle_info(_message, socket) do
{:noreply, socket}
end
# JS countdown reached zero — verify server-side and redirect if maintenance ended
@impl true
def handle_event("check_status", _params, socket) do
if Maintenance.active?() do
{:noreply, socket}
else
if socket.assigns.is_admin do
{:noreply, assign(socket, :is_active, false)}
else
{:noreply, push_navigate(socket, to: Routes.path("/"))}
end
end
end
def handle_event(_event, _params, socket) do
{:noreply, socket}
end
@impl true
def render(assigns) do
~H"""
<div class="fixed inset-0 bg-base-200 flex items-center justify-center p-4 z-50">
<div class="max-w-2xl w-full space-y-6">
<%!-- Admin preview banner --%>
<%= if @is_admin do %>
<div class="alert alert-info shadow-lg">
<.icon name="hero-eye" class="w-5 h-5" />
<div class="flex-1">
<p class="font-semibold">{gettext("Admin Preview")}</p>
<p class="text-sm">
<%= if @is_active do %>
{gettext("Maintenance is active — regular users see this page.")}
<% else %>
{gettext("Maintenance is not active — this is a preview of the page.")}
<% end %>
</p>
</div>
<.link navigate={Routes.path("/admin/settings/maintenance")} class="btn btn-sm btn-ghost">
<.icon name="hero-cog-6-tooth" class="w-4 h-4" /> {gettext("Settings")}
</.link>
</div>
<% end %>
<%!-- Main maintenance card --%>
<div class="card bg-base-100 shadow-2xl border-2 border-dashed border-base-300">
<div class="card-body text-center py-12 px-6">
<div class="text-8xl mb-6 opacity-70">
🚧
</div>
<h1 class="text-5xl font-bold text-base-content mb-6">
{@header}
</h1>
<p class="text-xl text-base-content/70 mb-8 leading-relaxed">
{@subtext}
</p>
<%!-- Countdown timer --%>
<%= if @scheduled_end_iso do %>
<div class="divider"></div>
<div
id="maintenance-countdown"
phx-hook="MaintenanceCountdown"
data-end={@scheduled_end_iso}
class="text-base-content/50"
>
<p class="text-sm">
{gettext("Expected back in")}
<span id="countdown-value" class="font-mono font-semibold text-base-content/70"></span>
</p>
</div>
<% end %>
</div>
</div>
</div>
</div>
"""
end
end