feat: add landing page at / with navigation links
Route GET / to a landing page with the Porchlight logo, tagline, and card-style navigation links to My Account and Administration.
This commit is contained in:
parent
cedf2a65e2
commit
0435b81c5a
4 changed files with 95 additions and 0 deletions
|
|
@ -146,4 +146,8 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
|||
async def health() -> dict[str, str]:
|
||||
return {"status": "ok"}
|
||||
|
||||
@app.get("/")
|
||||
async def landing(request: Request): # type: ignore[no-untyped-def]
|
||||
return templates.TemplateResponse(request, "index.html")
|
||||
|
||||
return app
|
||||
|
|
|
|||
|
|
@ -530,6 +530,66 @@ li {
|
|||
border: 0;
|
||||
}
|
||||
|
||||
/* ---------- Landing page ---------- */
|
||||
|
||||
.landing {
|
||||
text-align: center;
|
||||
padding-top: var(--sp-8);
|
||||
}
|
||||
|
||||
.landing-logo {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
margin-bottom: var(--sp-6);
|
||||
}
|
||||
|
||||
.landing h1 {
|
||||
margin-bottom: var(--sp-3);
|
||||
}
|
||||
|
||||
.landing-blurb {
|
||||
color: var(--fg-muted);
|
||||
font-size: var(--font-size-lg);
|
||||
max-width: 30rem;
|
||||
margin: 0 auto var(--sp-8);
|
||||
}
|
||||
|
||||
.landing-nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-4);
|
||||
max-width: 24rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.landing-link {
|
||||
display: block;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--sp-4) var(--sp-6);
|
||||
text-decoration: none;
|
||||
text-align: left;
|
||||
transition: border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.landing-link:hover {
|
||||
border-color: var(--accent);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.landing-link strong {
|
||||
display: block;
|
||||
color: var(--fg);
|
||||
font-size: var(--font-size-base);
|
||||
margin-bottom: var(--sp-1);
|
||||
}
|
||||
|
||||
.landing-link span {
|
||||
color: var(--fg-muted);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
/* ---------- Reduced motion ---------- */
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
|
|
|||
19
src/porchlight/templates/index.html
Normal file
19
src/porchlight/templates/index.html
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="landing">
|
||||
<img class="landing-logo" src="/static/logo.svg" alt="" width="96" height="96">
|
||||
<h1>Porchlight</h1>
|
||||
<p class="landing-blurb">OpenID Connect Provider with user management. Manage your identity, credentials, and connected applications.</p>
|
||||
<nav class="landing-nav" aria-label="Quick links">
|
||||
<a class="landing-link" href="/manage/profile">
|
||||
<strong>My Account</strong>
|
||||
<span>Manage your profile and credentials</span>
|
||||
</a>
|
||||
<a class="landing-link" href="/admin/users">
|
||||
<strong>Administration</strong>
|
||||
<span>Manage users and settings</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -28,6 +28,18 @@ async def test_app_has_repos_on_state(client: AsyncClient) -> None:
|
|||
assert isinstance(app.state.magic_link_repo, MagicLinkRepository)
|
||||
|
||||
|
||||
async def test_landing_page(client: AsyncClient) -> None:
|
||||
response = await client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert "text/html" in response.headers["content-type"]
|
||||
body = response.text
|
||||
assert "<h1>Porchlight</h1>" in body
|
||||
assert "/manage/profile" in body
|
||||
assert "/admin/users" in body
|
||||
assert "My Account" in body
|
||||
assert "Administration" in body
|
||||
|
||||
|
||||
async def test_dependency_functions() -> None:
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue