Files
indiekit-endpoint-blogroll/views/blogroll-dashboard.njk
Ricardo 4ad4c13bbc refactor: align views with upstream @indiekit/frontend patterns
- Extract ~560 lines of inline CSS to external assets/styles.css
- Create intermediate layout (layouts/blogroll.njk) for CSS loading
- Use section(), badge(), button(), prose() macros instead of raw HTML
- Remove custom page headers (document.njk heading() handles via title/parent)
- Add parent breadcrumb navigation to all sub-pages
- Add consumeFlashMessage() to dashboard and sources controllers
- Rename CSS class prefix from br-* to blogroll-* for clarity
- Use upstream CSS custom properties without fallback values
- Fix Microsub orphan detection (soft-delete unsubscribed blogs)
- Fix upsert to conditionally set microsub fields (avoid path conflicts)
- Skip soft-deleted blogs during clear-and-resync

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:42:27 +01:00

117 lines
5.1 KiB
Plaintext

{% extends "layouts/blogroll.njk" %}
{% block blogroll %}
{% call section({ title: __("blogroll.stats.title") }) %}
<dl class="blogroll-stats">
<div class="blogroll-stat">
<dt>{{ __("blogroll.stats.sources") }}</dt>
<dd>{{ stats.sources }}</dd>
</div>
<div class="blogroll-stat">
<dt>{{ __("blogroll.stats.blogs") }}</dt>
<dd>{{ stats.blogs }}</dd>
</div>
<div class="blogroll-stat">
<dt>{{ __("blogroll.stats.items") }}</dt>
<dd>{{ stats.items }}</dd>
</div>
<div class="blogroll-stat">
<dt>{{ __("blogroll.stats.errors") }}</dt>
<dd class="{% if stats.errors > 0 %}blogroll-stat--error{% endif %}">{{ stats.errors }}</dd>
</div>
<div class="blogroll-stat">
<dt>{{ __("blogroll.stats.lastSync") }}</dt>
<dd>{% if syncStatus.lastSync %}{{ syncStatus.lastSync | date("PPpp") }}{% else %}{{ __("blogroll.never") }}{% endif %}</dd>
</div>
</dl>
<div class="blogroll-actions">
{{ button({ href: baseUrl + "/sources", text: __("blogroll.sources.manage"), classes: "button--secondary" }) }}
{{ button({ href: baseUrl + "/blogs", text: __("blogroll.blogs.manage"), classes: "button--secondary" }) }}
</div>
{% endcall %}
{% call section({ title: __("blogroll.actions.title") }) %}
<div class="blogroll-actions">
<form method="post" action="{{ baseUrl }}/sync" style="display: inline;">
{{ button({ type: "submit", text: __("blogroll.actions.syncNow") }) }}
</form>
<form method="post" action="{{ baseUrl }}/clear-resync" style="display: inline;" onsubmit="return confirm('{{ __("blogroll.actions.clearConfirm") }}');">
{{ button({ type: "submit", text: __("blogroll.actions.clearResync"), classes: "button--secondary" }) }}
</form>
</div>
{% endcall %}
{% if blogsWithErrors.length > 0 %}
{% call section({ title: __("blogroll.errors.title") }) %}
<ul class="blogroll-list">
{% for blog in blogsWithErrors %}
<li class="blogroll-list__item blogroll-list__item--compact">
<div>
<span class="blogroll-item__title">{{ blog.title }}</span>
<span class="blogroll-item__error">{{ blog.lastError }}</span>
</div>
{{ button({ href: baseUrl + "/blogs/" + blog._id, text: __("blogroll.edit"), classes: "button--small button--secondary" }) }}
</li>
{% endfor %}
</ul>
{% if stats.errors > blogsWithErrors.length %}
<p class="blogroll-empty">
<a href="{{ baseUrl }}/blogs?status=error">{{ __("blogroll.errors.seeAll", { count: stats.errors }) }}</a>
</p>
{% endif %}
{% endcall %}
{% endif %}
{% call section({ title: __("blogroll.sources.recent") }) %}
{% if sources.length > 0 %}
<ul class="blogroll-list">
{% for source in sources %}
<li class="blogroll-list__item blogroll-list__item--compact">
<div>
<span class="blogroll-item__title">{{ source.name }}</span>
<span class="blogroll-item__meta">{{ source.type }} · {% if source.lastSyncAt %}{{ source.lastSyncAt | date("PP") }}{% else %}{{ __("blogroll.never") }}{% endif %}</span>
</div>
{{ badge({ color: "green" if source.enabled else "yellow", text: __("blogroll.enabled") if source.enabled else __("blogroll.disabled") }) }}
</li>
{% endfor %}
</ul>
{% else %}
{{ prose({ text: __("blogroll.sources.empty") }) }}
{% endif %}
<div class="blogroll-actions">
{{ button({ href: baseUrl + "/sources/new", text: __("blogroll.sources.add"), classes: "button--secondary button--small" }) }}
</div>
{% endcall %}
{% call section({ title: __("blogroll.blogs.recent") }) %}
{% if recentBlogs.length > 0 %}
<ul class="blogroll-list">
{% for blog in recentBlogs %}
<li class="blogroll-list__item blogroll-list__item--compact">
<div>
<span class="blogroll-item__title">{{ blog.title }}</span>
<span class="blogroll-item__meta">{{ blog.category or "Uncategorized" }} · {{ blog.itemCount }} items</span>
</div>
{{ badge({ color: "green" if blog.status == "active" else ("red" if blog.status == "error" else "yellow"), text: blog.status }) }}
</li>
{% endfor %}
</ul>
{% else %}
{{ prose({ text: __("blogroll.blogs.empty") }) }}
{% endif %}
<div class="blogroll-actions">
{{ button({ href: baseUrl + "/blogs/new", text: __("blogroll.blogs.add"), classes: "button--secondary button--small" }) }}
</div>
{% endcall %}
{% call section({ title: __("blogroll.api.title") }) %}
<ul class="blogroll-api-list">
<li><code>GET {{ baseUrl }}/api/blogs</code> - {{ __("blogroll.api.blogs") }}</li>
<li><code>GET {{ baseUrl }}/api/items</code> - {{ __("blogroll.api.items") }}</li>
<li><code>GET {{ baseUrl }}/api/categories</code> - {{ __("blogroll.api.categories") }}</li>
<li><code>GET {{ baseUrl }}/api/opml</code> - {{ __("blogroll.api.opml") }}</li>
<li><code>GET {{ baseUrl }}/api/status</code> - {{ __("blogroll.api.status") }}</li>
</ul>
{% endcall %}
{% endblock %}