Current section
Files
Jump to
Current section
Files
lib/skua/pages.ex
defmodule Skua.Pages do
@moduledoc false
# Templates for `mix skua.gen.pages`. Pure string builders with placeholder
# substitution (no runtime deps), mirroring Skua.Auth.Patches. `__WEB__` /
# `__APP_NAME__` / `__APP__` are filled in; HEEx `{...}` are real interpolations
# that must survive into the generated file.
@doc "The shared site nav function component (`<App>Web.SiteNav`)."
def site_nav_view(web, app_name, app) do
site_nav_template() |> fill(web, app_name, app)
end
@doc "The default homepage LiveView: shared nav + hero (version badges) + components."
def home_view(web, app_name, app) do
home_template() |> fill(web, app_name, app)
end
@doc "The authenticated dashboard LiveView (`/dashboard`): sidebar + content."
def dashboard_view(web, app_name, app) do
dashboard_template() |> fill(web, app_name, app)
end
defp fill(template, web, app_name, app) do
template
|> String.replace("__WEB__", web)
|> String.replace("__APP_NAME__", app_name)
|> String.replace("__APP__", app)
end
# === shared site nav ======================================================
# A plain function component so every `<Layouts.app>`-wrapped page (auth pages,
# dashboard) AND the standalone homepage can render the same nav. Auth links
# are conditional on `@current_scope`: a signed-in user sees their email + a
# Log out link (DELETE `~p"/users/log-out"`, the route phx.gen.auth ships);
# otherwise Register + Sign in. `current_scope` is optional so the nav renders
# even on routes that don't mount a scope (the homepage's `/`).
defp site_nav_template do
"""
defmodule __WEB__.SiteNav do
@moduledoc \"\"\"
The shared top navigation, rendered on every page. Generated by
`mix skua.gen.pages` and injected into `Layouts.app`, so it appears on the
homepage, the auth pages, and the dashboard. Edit it here.
\"\"\"
use __WEB__, :html
attr :current_scope, :map,
default: nil,
doc: "the current scope; when it has a user, the nav shows Log out"
attr :class, :any, default: nil
def site_nav(assigns) do
~H\"\"\"
<header
class={["sk-sitenav border-b", @class]}
style="border-color: var(--skua-border); background: var(--skua-bg)"
>
<style>
.sk-sitenav .sk-navlink { color: var(--skua-fg-muted); text-decoration: none; transition: color var(--skua-duration) var(--skua-ease); }
.sk-sitenav .sk-navlink:hover { color: var(--skua-fg); }
.sk-sitenav .sk-navlink--strong { color: var(--skua-fg); font-weight: 500; }
</style>
<nav class="mx-auto flex h-14 max-w-5xl items-center justify-between px-6">
<.link navigate={~p"/"} class="sk-navlink--strong text-sm font-semibold">
__APP_NAME__
</.link>
<div class="flex items-center gap-5 text-sm">
<a href="/#overview" class="sk-navlink">Overview</a>
<a href="/#components" class="sk-navlink">Components</a>
<%= if @current_scope && @current_scope.user do %>
<.link navigate={~p"/dashboard"} class="sk-navlink">Dashboard</.link>
<span class="hidden sm:inline" style="color: var(--skua-fg-muted)">
{@current_scope.user.email}
</span>
<.link href={~p"/users/log-out"} method="delete" class="sk-navlink">
Log out
</.link>
<% else %>
<.link navigate={~p"/users/register"} class="sk-navlink">Register</.link>
<.link navigate={~p"/users/log-in"} class="sk-navlink--strong">Sign in</.link>
<% end %>
</div>
</nav>
</header>
\"\"\"
end
end
"""
end
# === homepage =============================================================
defp home_template do
"""
defmodule __WEB__.HomeLive do
@moduledoc \"\"\"
Starter homepage generated by `mix skua.gen.pages`. Everything here is yours
to edit — the hero and the component showcase. The top nav lives in
`__WEB__.SiteNav` and is shared with the auth pages and the dashboard.
\"\"\"
use __WEB__, :live_view
alias __WEB__.SiteNav
@impl true
def mount(_params, _session, socket) do
{:ok,
assign(socket,
page_title: "__APP_NAME__",
form: to_form(%{"email" => "", "role" => "member"}, as: "demo"),
phoenix_vsn: vsn(:phoenix),
skua_vsn: vsn(:skua)
)}
end
defp vsn(app), do: app |> Application.spec(:vsn) |> to_string()
@impl true
def render(assigns) do
~H\"\"\"
<div class="min-h-screen" style="background: var(--skua-bg); color: var(--skua-fg)">
<SiteNav.site_nav current_scope={assigns[:current_scope]} />
<section id="overview" class="mx-auto max-w-5xl px-6 py-24 text-center">
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">__APP_NAME__</h1>
<p class="mx-auto mt-4 max-w-xl" style="color: var(--skua-fg-muted)">
A Phoenix LiveView app, scaffolded with Skua. Edit this page in
<code class="sk-code">lib/__APP___web/home_live.ex</code>.
</p>
<div class="mt-8 flex items-center justify-center gap-2">
<.badge variant="info">Phoenix v{@phoenix_vsn}</.badge>
<.badge variant="success">Skua v{@skua_vsn}</.badge>
</div>
<div class="mt-8 flex items-center justify-center gap-3">
<.button variant="primary">Get started</.button>
<.button variant="ghost">View components</.button>
</div>
</section>
<section id="components" class="border-t" style="border-color: var(--skua-border)">
<div class="mx-auto max-w-5xl px-6 py-20">
<h2 class="text-2xl font-semibold tracking-tight">Components</h2>
<p class="mt-2" style="color: var(--skua-fg-muted)">
A few of the Skua components now available in your app.
</p>
<div class="mt-10 grid gap-6 lg:grid-cols-2">
<.card>
<:title>Buttons & badges</:title>
<:subtitle>Token-styled, theme-aware variants.</:subtitle>
<div class="space-y-5">
<div class="flex flex-wrap items-center gap-3">
<.button variant="primary">Primary</.button>
<.button variant="secondary">Secondary</.button>
<.button variant="ghost">Ghost</.button>
<.button variant="danger">Danger</.button>
</div>
<div class="flex flex-wrap items-center gap-2">
<.badge variant="neutral">Neutral</.badge>
<.badge variant="info">Info</.badge>
<.badge variant="success">Success</.badge>
<.badge variant="warning">Warning</.badge>
<.badge variant="danger">Danger</.badge>
</div>
</div>
</.card>
<.card>
<:title>Form inputs</:title>
<:subtitle>Inputs, selects, and validation built in.</:subtitle>
<.form for={@form} class="space-y-4">
<.input
field={@form[:email]}
type="email"
label="Email"
placeholder="you@example.com"
/>
<.select
field={@form[:role]}
label="Role"
options={[{"Member", "member"}, {"Admin", "admin"}]}
/>
</.form>
</.card>
</div>
<div class="mt-6">
<.alert variant="info" title="Heads up">
These are live Skua components — open
<code class="sk-code">lib/__APP___web/home_live.ex</code>
and start editing.
</.alert>
</div>
<div class="mt-6">
<.tabs id="home-showcase">
<:tab label="Table" active>
<.table id="home-people" rows={people()}>
<:col :let={p} label="Name">{p.name}</:col>
<:col :let={p} label="Role">{p.role}</:col>
<:col :let={p} label="Status">
<.badge variant={p.status_variant}>{p.status}</.badge>
</:col>
</.table>
</:tab>
<:tab label="Details">
<.list>
<:item title="Framework">Phoenix LiveView</:item>
<:item title="Components">Skua</:item>
<:item title="Theme">Light & dark, token-driven</:item>
</.list>
</:tab>
</.tabs>
</div>
</div>
</section>
</div>
\"\"\"
end
defp people do
[
%{name: "Ada Lovelace", role: "Admin", status: "Active", status_variant: "success"},
%{name: "Alan Turing", role: "Member", status: "Invited", status_variant: "warning"},
%{name: "Grace Hopper", role: "Member", status: "Active", status_variant: "success"}
]
end
end
"""
end
# === dashboard ============================================================
# Rendered inside the authenticated `live_session` phx.gen.auth generates, so
# it's behind auth and `@current_scope.user` is always present. Its own left
# sidebar (Dashboard / Settings + Log out pinned to the bottom) and a content
# area with a couple of real Skua stat cards.
defp dashboard_template do
"""
defmodule __WEB__.DashboardLive do
@moduledoc \"\"\"
Authenticated dashboard generated by `mix skua.gen.pages`. Routed inside the
`:require_authenticated_user` live_session, so it's behind login. Left
sidebar + a content area with placeholder Skua cards — edit freely.
\"\"\"
use __WEB__, :live_view
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, page_title: "Dashboard")}
end
@impl true
def render(assigns) do
~H\"\"\"
<div
class="flex min-h-[calc(100vh-3.5rem)]"
style="background: var(--skua-bg); color: var(--skua-fg)"
>
<aside
class="sk-dash flex w-56 shrink-0 flex-col border-r"
style="border-color: var(--skua-border)"
>
<style>
.sk-dash .sk-sidelink { display: block; border-radius: var(--sk-r); padding: 0.5rem 0.75rem; font-size: var(--sk-fs-sm); color: var(--skua-fg-muted); text-decoration: none; transition: background var(--skua-duration) var(--skua-ease), color var(--skua-duration) var(--skua-ease); }
.sk-dash .sk-sidelink:hover { background: var(--sk-hover); color: var(--skua-fg); }
.sk-dash .sk-sidelink.is-active { background: var(--sk-hover); color: var(--skua-fg); font-weight: 500; }
</style>
<nav class="flex flex-1 flex-col gap-1 p-4">
<p class="px-2 pb-2 text-xs font-medium uppercase tracking-wide" style="color: var(--skua-fg-muted)">
Menu
</p>
<.link navigate={~p"/dashboard"} class="sk-sidelink is-active">Dashboard</.link>
<.link navigate={~p"/users/settings"} class="sk-sidelink">Settings</.link>
</nav>
<div class="border-t p-4" style="border-color: var(--skua-border)">
<.link href={~p"/users/log-out"} method="delete" class="sk-sidelink">
Log out
</.link>
</div>
</aside>
<main class="flex-1 px-6 py-8 sm:px-8">
<.header>
Dashboard
<:subtitle>Welcome back{@current_scope && ", "}{@current_scope && @current_scope.user.email}.</:subtitle>
</.header>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<.card>
<:subtitle>Total users</:subtitle>
<p class="text-3xl font-semibold">1,248</p>
<:footer>
<.badge variant="success">+12% this week</.badge>
</:footer>
</.card>
<.card>
<:subtitle>Active sessions</:subtitle>
<p class="text-3xl font-semibold">86</p>
<:footer>
<.badge variant="info">Live</.badge>
</:footer>
</.card>
<.card>
<:subtitle>Pending invites</:subtitle>
<p class="text-3xl font-semibold">4</p>
<:footer>
<.badge variant="warning">Awaiting</.badge>
</:footer>
</.card>
</div>
<div class="mt-6">
<.card>
<:title>Getting started</:title>
<:subtitle>This is a placeholder dashboard.</:subtitle>
<.list>
<:item title="Route">/dashboard</:item>
<:item title="Edit">lib/__APP___web/dashboard_live.ex</:item>
<:item title="Components">Skua cards, badges, list</:item>
</.list>
</.card>
</div>
</main>
</div>
\"\"\"
end
end
"""
end
end