adding website
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}Nornsight{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='styles.css') }}">
|
||||
<script src="{{ url_for('static', path='vendor/htmx.min.js') }}" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="topbar">
|
||||
<a class="brand" href="/">
|
||||
<span class="brand-mark">N</span>
|
||||
<span>Nornsight</span>
|
||||
</a>
|
||||
{% if show_primary_nav|default(true) %}
|
||||
<nav class="primary-nav" aria-label="Primary">
|
||||
<a href="/">Issues</a>
|
||||
<a href="/legislators">Legislators</a>
|
||||
<a href="/compare">Compare</a>
|
||||
</nav>
|
||||
{% endif %}
|
||||
<nav class="account-nav" aria-label="Account">
|
||||
<a href="#" aria-disabled="true">Help</a>
|
||||
{% if is_authenticated|default(true) %}
|
||||
<details class="account-menu">
|
||||
<summary>My account</summary>
|
||||
<div class="account-menu-panel">
|
||||
<a href="#" aria-disabled="true">Account settings</a>
|
||||
<a class="sign-out" href="/logout">Sign out</a>
|
||||
</div>
|
||||
</details>
|
||||
{% else %}
|
||||
<a class="sign-in" href="/login">Sign in</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</header>
|
||||
{% block body %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,87 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Compare Legislators{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<main class="shell">
|
||||
<section class="page-heading stacked-heading">
|
||||
<div>
|
||||
<h1>Compare legislators</h1>
|
||||
<p>Up to 4 legislators · up to 8 issue axes · each polygon = one legislator's full issue profile</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="compare-controls">
|
||||
<form class="wide-search compare-search" action="/compare" method="get">
|
||||
<label class="sr-only" for="compare-legislator-search">Search legislators</label>
|
||||
{% for legislator_id in selected_legislators %}
|
||||
<input type="hidden" name="legislator_id" value="{{ legislator_id }}">
|
||||
{% endfor %}
|
||||
{% for topic in topics %}
|
||||
<input type="hidden" name="topic" value="{{ topic }}">
|
||||
{% endfor %}
|
||||
<input
|
||||
id="compare-legislator-search"
|
||||
type="search"
|
||||
name="q"
|
||||
value="{{ q }}"
|
||||
placeholder="Search legislators to add"
|
||||
autocomplete="off">
|
||||
<button type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
<h2>Legislators ({{ selected_legislator_options|length }} / 4)</h2>
|
||||
<div class="result-chips">
|
||||
{% for legislator in selected_legislator_options %}
|
||||
{% set without = selected_legislators | reject('equalto', legislator.legislator_id) | list %}
|
||||
<a href="{{ build_compare_url(legislator_ids=without, topics=topics, q=q) }}"><span class="legend-dot dot-{{ loop.index0 }}"></span>{{ legislator.display_name }}{% if legislator.state %} — {{ legislator.state }}{% endif %} ×</a>
|
||||
{% endfor %}
|
||||
{% if selected_legislator_options|length < 4 %}
|
||||
{% for option in legislator_options %}
|
||||
{% if option.legislator_id not in selected_legislators %}
|
||||
<a class="dashed-chip" href="{{ build_compare_url(legislator_ids=selected_legislators + [option.legislator_id], topics=topics, q=q) }}">+ {{ option.display_name }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h2>Issue axes ({{ topics|length }} / 8)</h2>
|
||||
<div class="axis-chips">
|
||||
{% for topic in topics %}
|
||||
{% set without_topic = topics[:loop.index0] + topics[loop.index:] %}
|
||||
<a href="{{ build_compare_url(legislator_ids=selected_legislators, topics=without_topic, q=q) }}">{{ topic }} ×</a>
|
||||
{% endfor %}
|
||||
{% if topics|length < 8 %}
|
||||
{% for topic in topic_options %}
|
||||
{% if topic not in topics %}
|
||||
<a href="{{ build_compare_url(legislator_ids=selected_legislators, topics=topics + [topic], q=q) }}">{{ topic }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="compare-card">
|
||||
<div class="radar-frame">{{ radar_svg | safe }}</div>
|
||||
<aside class="compare-legend">
|
||||
<h2>Legend</h2>
|
||||
{% for item in series %}
|
||||
<div class="legend-row">
|
||||
<span class="legend-line line-{{ loop.index0 }}"></span>
|
||||
<div>
|
||||
<strong>{{ item.legislator.display_name }}</strong>
|
||||
<small>{{ item.legislator.state or "US" }} · {{ item.legislator.party or "—" }} · avg {{ item.average_score|round(0) if item.average_score is not none else "—" }}</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<p>Outer ring = 100% support. Each axis is scored independently against full roll-call record.</p>
|
||||
<p><em>Max 4 legislators · max 8 axes</em></p>
|
||||
</aside>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<span>Actual record, not rhetoric</span>
|
||||
<span>Source: congressional roll-call votes</span>
|
||||
<span>Not affiliated with any political party or organization</span>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,30 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Legislative Accountability{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<main class="shell">
|
||||
<section class="page-heading">
|
||||
<div>
|
||||
<h1>Legislative accountability</h1>
|
||||
<p>US legislative accountability · precomputed legislator topic scores{% if latest_score_year %} through {{ latest_score_year }}{% endif %}</p>
|
||||
</div>
|
||||
<div class="heading-actions">
|
||||
<a href="#" aria-disabled="true">Methodology</a>
|
||||
<a href="#" aria-disabled="true">Data sources</a>
|
||||
<span>Last updated: {{ last_updated.strftime("%b %Y") if last_updated else "Unavailable" }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="notice">Choose one or more score topics, then select lawmakers to compare computed records over time.</div>
|
||||
|
||||
<div id="dashboard-body">
|
||||
{% include "partials/_dashboard.html" %}
|
||||
</div>
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<span>Actual record, not rhetoric</span>
|
||||
<span>Source: congressional roll-call votes</span>
|
||||
<span>Not affiliated with any political party or organization</span>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,148 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Legislator Profiles{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<main class="shell">
|
||||
<section class="page-heading stacked-heading">
|
||||
<div>
|
||||
<h1>Legislator profiles</h1>
|
||||
<p>Full issue taxonomy · search any current legislator</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<form class="wide-search legislator-search-form" action="/legislators" method="get">
|
||||
<label class="sr-only" for="legislator-search">Search legislators</label>
|
||||
<input
|
||||
id="legislator-search"
|
||||
type="search"
|
||||
name="q"
|
||||
value="{{ q }}"
|
||||
placeholder="Search by name or state"
|
||||
autocomplete="off"
|
||||
hx-get="/partials/legislator-suggestions"
|
||||
hx-trigger="input changed delay:200ms, search"
|
||||
hx-target="#legislator-suggestions"
|
||||
hx-swap="innerHTML">
|
||||
<label class="sr-only" for="legislator-page-size">Results per page</label>
|
||||
<select id="legislator-page-size" name="per_page" aria-label="Results per page">
|
||||
{% for option in per_page_options %}
|
||||
<option value="{{ option }}" {{ "selected" if option == per_page else "" }}>{{ option }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
<div id="legislator-suggestions" aria-live="polite"></div>
|
||||
|
||||
{% if q %}
|
||||
<section class="phonebook-results" aria-label="Matching legislators">
|
||||
<header>
|
||||
<h2>Matching legislators</h2>
|
||||
<span>{{ result_count }} result{{ "" if result_count == 1 else "s" }}</span>
|
||||
</header>
|
||||
{% if matches %}
|
||||
<ol class="phonebook-list" start="{{ ((page - 1) * per_page) + 1 }}">
|
||||
{% for option in matches %}
|
||||
<li>
|
||||
<a href="{{ build_legislator_url(legislator_id=option.legislator_id, q=q, per_page=per_page) }}">
|
||||
<span class="phonebook-name">{{ option.display_name }}</span>
|
||||
<span class="phonebook-meta">
|
||||
{{ option.state or "US" }}{% if option.party %} · {{ option.party }}{% endif %}{% if option.chamber %} · {{ option.chamber }}{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<nav class="pagination" aria-label="Legislator results pages">
|
||||
{% if previous_page %}
|
||||
<a href="{{ build_legislator_search_url(q=q, per_page=per_page, page=previous_page) }}">Previous</a>
|
||||
{% else %}
|
||||
<span>Previous</span>
|
||||
{% endif %}
|
||||
<strong>Page {{ page }} of {{ total_pages }}</strong>
|
||||
{% if next_page %}
|
||||
<a href="{{ build_legislator_search_url(q=q, per_page=per_page, page=next_page) }}">Next</a>
|
||||
{% else %}
|
||||
<span>Next</span>
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% else %}
|
||||
<p class="empty-state">No legislators matched this search.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if profile %}
|
||||
<section class="profile-card">
|
||||
<header class="profile-header">
|
||||
<div class="profile-identity">
|
||||
<span class="avatar">{{ profile.legislator.display_name.split(',')[0][:1] }}{{ profile.legislator.display_name.split(',')[-1].strip()[:1] }}</span>
|
||||
<div>
|
||||
<h2>{{ profile.legislator.display_name }} <span class="party-pill">{{ profile.legislator.chamber or "LEG" }}</span></h2>
|
||||
<p>{{ profile.legislator.state or "US" }} · {{ profile.legislator.party or "Unaffiliated" }}{% if profile.serving_since %} · Serving since {{ profile.serving_since }}{% endif %}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overall-score">
|
||||
<span>Overall avg</span>
|
||||
<strong>{{ profile.overall_score|round(0) if profile.overall_score is not none else "—" }}</strong>
|
||||
<small>/ 100</small>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if profile.top_topics or profile.bottom_topics %}
|
||||
<div class="topic-panels">
|
||||
<article>
|
||||
<h3>Most important issues for</h3>
|
||||
{% for item in profile.top_topics %}
|
||||
<a class="topic-row" href="{{ build_legislator_url(legislator_id=profile.legislator.legislator_id, topic=item.topic) }}">
|
||||
<strong class="score positive">{{ item.score|round(0) }}</strong>
|
||||
<span>{{ item.topic }}</span>
|
||||
<i style="width: {{ item.score }}%"></i>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</article>
|
||||
<article>
|
||||
<h3 class="opposed-heading">Most important issues against</h3>
|
||||
{% for item in profile.bottom_topics %}
|
||||
<a class="topic-row {{ 'active' if item.topic == selected_topic else '' }}" href="{{ build_legislator_url(legislator_id=profile.legislator.legislator_id, topic=item.topic) }}">
|
||||
<strong class="score negative">{{ item.score|round(0) }}</strong>
|
||||
<span>{{ item.topic }}</span>
|
||||
<i style="width: {{ item.score }}%"></i>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<section class="profile-history">
|
||||
<h3>{{ selected_topic or "Topic" }} — score history</h3>
|
||||
<div class="chart-frame">{{ history_svg | safe }}</div>
|
||||
{% if history_series %}
|
||||
<div class="chart-legend compact" aria-label="Chart legend">
|
||||
{% for item in history_series %}
|
||||
<div class="chart-legend-row">
|
||||
<span class="chart-legend-line line-0"></span>
|
||||
<span class="chart-legend-marker marker-0"></span>
|
||||
<div class="chart-legend-copy">
|
||||
<span class="chart-legend-label">{{ item.label }}</span>
|
||||
<span class="chart-legend-meta">
|
||||
{% if item.party %}{{ item.party }}{% endif %}{% if item.party and item.state %} · {% endif %}{% if item.state %}{{ item.state }}{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% else %}
|
||||
<p class="empty-state">No issue scores are available for this legislator yet.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<span>Actual record, not rhetoric</span>
|
||||
<span>Source: congressional roll-call votes</span>
|
||||
<span>Not affiliated with any political party or organization</span>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,45 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Sign in | Nornsight{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<main class="login-shell">
|
||||
<section class="login-panel" aria-labelledby="login-title">
|
||||
<div class="login-copy">
|
||||
<p class="eyebrow">Admin access</p>
|
||||
<h1 id="login-title">Sign in to Nornsight</h1>
|
||||
<p>Use the dashboard account to review rankings, profiles, and legislator comparisons.</p>
|
||||
</div>
|
||||
|
||||
<form class="login-form" action="/login" method="post">
|
||||
<input type="hidden" name="next" value="{{ next_path }}">
|
||||
|
||||
{% if error %}
|
||||
<p class="form-error" role="alert">{{ error }}</p>
|
||||
{% endif %}
|
||||
|
||||
<label for="username">Username</label>
|
||||
<input
|
||||
id="username"
|
||||
name="username"
|
||||
type="text"
|
||||
autocomplete="username"
|
||||
value="{{ username }}"
|
||||
required
|
||||
autofocus
|
||||
>
|
||||
|
||||
<label for="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
>
|
||||
|
||||
<button type="submit">Sign in</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,30 @@
|
||||
<section class="chart-card">
|
||||
<header>
|
||||
<h2>Score history{% if selected_issue_label %} — {{ selected_issue_label }}{% endif %}</h2>
|
||||
<a href="{{ build_url(request, compare=[]) }}"
|
||||
hx-get="/partials/dashboard{{ build_url(request, compare=[])|replace('/', '', 1) }}"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="{{ build_url(request, compare=[]) }}">Clear comparison</a>
|
||||
</header>
|
||||
<div class="chart-frame">
|
||||
{{ chart_svg | safe }}
|
||||
</div>
|
||||
{% if chart_series %}
|
||||
<div class="chart-legend" aria-label="Chart legend">
|
||||
{% for item in chart_series %}
|
||||
{% set style_index = loop.index0 % 4 %}
|
||||
<div class="chart-legend-row">
|
||||
<span class="chart-legend-line line-{{ style_index }}"></span>
|
||||
<span class="chart-legend-marker marker-{{ style_index }}"></span>
|
||||
<div class="chart-legend-copy">
|
||||
<span class="chart-legend-label">{{ item.label }}</span>
|
||||
<span class="chart-legend-meta">
|
||||
{% if item.party %}{{ item.party }}{% endif %}{% if item.party and item.state %} · {% endif %}{% if item.state %}{{ item.state }}{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="score-note">Scores reflect averaged precomputed topic rows per year. Sparse years are omitted rather than plotted as zero.</p>
|
||||
</section>
|
||||
@@ -0,0 +1,25 @@
|
||||
<section class="controls-grid">
|
||||
{% include "partials/_issue_filters.html" %}
|
||||
<div class="chamber-card">
|
||||
<a class="segment {{ 'active' if chamber == 'house' else '' }}"
|
||||
href="{{ build_url(request, chamber='house') }}"
|
||||
hx-get="/partials/dashboard{{ build_url(request, chamber='house')|replace('/', '', 1) }}"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="{{ build_url(request, chamber='house') }}">House</a>
|
||||
<a class="segment {{ 'active' if chamber == 'senate' else '' }}"
|
||||
href="{{ build_url(request, chamber='senate') }}"
|
||||
hx-get="/partials/dashboard{{ build_url(request, chamber='senate')|replace('/', '', 1) }}"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="{{ build_url(request, chamber='senate') }}">Senate</a>
|
||||
<a class="segment {{ 'active' if chamber == 'all' else '' }}"
|
||||
href="{{ build_url(request, chamber='all') }}"
|
||||
hx-get="/partials/dashboard{{ build_url(request, chamber='all')|replace('/', '', 1) }}"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="{{ build_url(request, chamber='all') }}">All</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p class="score-note">Support score: 1-100 precomputed from bill topic stance and roll-call votes. Higher means more aligned with the topic.</p>
|
||||
|
||||
{% include "partials/_rankings.html" %}
|
||||
{% include "partials/_chart.html" %}
|
||||
@@ -0,0 +1,46 @@
|
||||
<section class="filter-card">
|
||||
<h2>Issue filters</h2>
|
||||
<form class="issue-form"
|
||||
method="get"
|
||||
action="/"
|
||||
hx-get="/"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="true">
|
||||
<input type="hidden" name="chamber" value="{{ chamber }}">
|
||||
{% if congress %}
|
||||
<input type="hidden" name="congress" value="{{ congress }}">
|
||||
{% endif %}
|
||||
{% for legislator_id in compare %}
|
||||
<input type="hidden" name="compare" value="{{ legislator_id }}">
|
||||
{% endfor %}
|
||||
{% for issue in issues %}
|
||||
<span class="chip">
|
||||
{{ issue }}
|
||||
<a href="{{ build_url(request, issues=issues[:loop.index0] + issues[loop.index:]) }}"
|
||||
hx-get="/partials/dashboard{{ build_url(request, issues=issues[:loop.index0] + issues[loop.index:])|replace('/', '', 1) }}"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="{{ build_url(request, issues=issues[:loop.index0] + issues[loop.index:]) }}"
|
||||
aria-label="Remove {{ issue }}">×</a>
|
||||
</span>
|
||||
<input type="hidden" name="issues" value="{{ issue }}">
|
||||
{% endfor %}
|
||||
<label class="search-box">
|
||||
<span class="sr-only">Search issue areas</span>
|
||||
<input type="search" name="issues" placeholder="Search issue areas" autocomplete="off">
|
||||
</label>
|
||||
<button type="submit">Apply</button>
|
||||
</form>
|
||||
|
||||
{% if suggestions %}
|
||||
<div class="suggestions" aria-label="Issue suggestions">
|
||||
{% for suggestion in suggestions %}
|
||||
{% if suggestion not in issues %}
|
||||
<a href="{{ build_url(request, issues=issues + [suggestion]) }}"
|
||||
hx-get="/partials/dashboard{{ build_url(request, issues=issues + [suggestion])|replace('/', '', 1) }}"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="{{ build_url(request, issues=issues + [suggestion]) }}">{{ suggestion }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
@@ -0,0 +1,11 @@
|
||||
{% if matches %}
|
||||
<div class="result-chips" aria-label="Search suggestions">
|
||||
{% for option in matches %}
|
||||
<a href="{{ build_legislator_url(legislator_id=option.legislator_id) }}">
|
||||
{{ option.display_name }}{% if option.state %} · {{ option.state }}{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% elif q %}
|
||||
<p class="suggestion-empty">No matches</p>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,61 @@
|
||||
<section class="rankings-grid">
|
||||
<article class="ranking-card">
|
||||
<header>
|
||||
<h2>Most supportive</h2>
|
||||
<span>Top 10</span>
|
||||
</header>
|
||||
{% if rankings.supportive %}
|
||||
<ol class="ranking-list">
|
||||
{% for row in rankings.supportive %}
|
||||
{% set next_compare = toggle_compare(compare, row.legislator_id) %}
|
||||
<li class="{{ 'selected' if row.legislator_id in compare else '' }}">
|
||||
<a href="{{ build_url(request, compare=next_compare) }}"
|
||||
hx-get="/partials/dashboard{{ build_url(request, compare=next_compare)|replace('/', '', 1) }}"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="{{ build_url(request, compare=next_compare) }}">
|
||||
<span class="rank">{{ loop.index }}</span>
|
||||
<strong class="score positive">{{ row.score|round(1) }}</strong>
|
||||
<span class="member">
|
||||
<strong>{{ row.display_name }}</strong>
|
||||
<small>{{ row.state or "US" }}{% if row.party %} · {{ row.party[:1] }}{% endif %}</small>
|
||||
</span>
|
||||
<span class="votes">{{ row.total }} rows</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% else %}
|
||||
<p class="empty-state">{{ empty_message }}</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
<article class="ranking-card">
|
||||
<header>
|
||||
<h2>Most opposed</h2>
|
||||
<span>Bottom 10</span>
|
||||
</header>
|
||||
{% if rankings.opposed %}
|
||||
<ol class="ranking-list">
|
||||
{% for row in rankings.opposed %}
|
||||
{% set next_compare = toggle_compare(compare, row.legislator_id) %}
|
||||
<li class="{{ 'selected' if row.legislator_id in compare else '' }}">
|
||||
<a href="{{ build_url(request, compare=next_compare) }}"
|
||||
hx-get="/partials/dashboard{{ build_url(request, compare=next_compare)|replace('/', '', 1) }}"
|
||||
hx-target="#dashboard-body"
|
||||
hx-push-url="{{ build_url(request, compare=next_compare) }}">
|
||||
<span class="rank">{{ loop.index }}</span>
|
||||
<strong class="score negative">{{ row.score|round(1) }}</strong>
|
||||
<span class="member">
|
||||
<strong>{{ row.display_name }}</strong>
|
||||
<small>{{ row.state or "US" }}{% if row.party %} · {{ row.party[:1] }}{% endif %}</small>
|
||||
</span>
|
||||
<span class="votes">{{ row.total }} rows</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% else %}
|
||||
<p class="empty-state">{{ empty_message }}</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
</section>
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Database Setup Required{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<main class="shell">
|
||||
<section class="page-heading stacked-heading">
|
||||
<div>
|
||||
<h1>Database setup required</h1>
|
||||
<p>Configure DATA_SCIENCE_DEV before opening the dashboard.</p>
|
||||
</div>
|
||||
</section>
|
||||
<pre class="setup-error">{{ error }}</pre>
|
||||
</main>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user