defter 0.4.2

Self-hosted Zotero sync and file storage server, with metadata in SQLite and attachments in S3.
{% extends "base.html" %}

{% block title %}{{ t.library }}{% endblock %}

{% block content %}
{#- Rendered alone for htmx's poll (see ui::dashboard), which swaps it in
    place of itself every 30 seconds. -#}
{% block status %}
<div id="status" hx-get="/" hx-trigger="every 30s" hx-swap="outerHTML">
  <section class="space-y-2">
    <h1 class="text-2xl font-semibold tracking-tight">{{ t.library }}</h1>
    <p class="text-sm text-muted-foreground">
      {{ t.library_version }} {{ status.version }} · {{ t.user }} {{ status.user_id }} ·
      defter {{ status.defter_version }} · commonmeta {{ status.commonmeta_version }}
    </p>
  </section>

  <section class="mt-6 grid grid-cols-2 gap-3 sm:grid-cols-4">
    {%- for stat in status.stats %}
    <div class="rounded-lg border bg-card p-4">
      <div class="text-xs uppercase tracking-wide text-muted-foreground">{{ stat.label }}</div>
      <div class="mt-1 text-2xl font-semibold tabular-nums">{{ stat.value }}</div>
    </div>
    {%- endfor %}
  </section>

  <section class="mt-8 rounded-lg border bg-card p-5 space-y-2">
    <h2 class="font-semibold">{{ t.attachment_storage }}</h2>
    <p class="text-sm"><code class="rounded bg-muted px-1.5 py-0.5">{{ status.storage_location }}</code></p>
    <p class="text-sm text-muted-foreground">
      {%- if status.signed_downloads %}
      {{ t.signed_downloads }}
      {%- else %}
      {{ t.streamed_downloads }}
      {%- endif %}
      {{ t.files_up_to }} {{ status.max_upload }} {{ t.are_accepted }}
    </p>
  </section>
</div>
{% endblock %}

{%- if let Some(profile) = profile %}
<section class="mt-4 rounded-lg border bg-card p-5 space-y-3">
  <h2 class="font-semibold">{{ t.public_profile }}</h2>
  <p class="text-sm"><a href="{{ profile.url }}" class="underline">{{ profile.url }}</a></p>
  <p class="text-sm text-muted-foreground">
    {%- if profile.hidden %}
    {{ t.profile_hidden }}
    {%- else %}
    {{ t.profile_visible }}
    {%- endif %}
    {{ t.it_lists }} {{ profile.public }}{{ t.all_of_my_publications }}
  </p>
  {%- if can_write %}
  <form method="post" action="/ui/profile">
    <input type="hidden" name="hidden" value="{% if profile.hidden %}false{% else %}true{% endif %}">
    <button type="submit" class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted">
      {%- if profile.hidden %}{{ t.show_profile }}{% else %}{{ t.hide_profile }}{% endif -%}
    </button>
  </form>
  {%- endif %}
</section>
{%- endif %}

<section class="mt-4 rounded-lg border bg-card p-5 space-y-3">
  <h2 class="font-semibold">{{ t.connect_zotero }}</h2>
  <p class="text-sm text-muted-foreground">
    {{ t.connect_requires }}
  </p>
  <pre class="overflow-x-auto rounded-md bg-muted p-3 text-sm"><code>extensions.zotero.api.url = {{ status.api_url }}/
extensions.zotero.streaming.enabled = false</code></pre>
  <p class="text-sm text-muted-foreground">
    {{ t.connect_then }}
  </p>
</section>

<section class="mt-4 rounded-lg border bg-card p-5 space-y-3">
  <h2 class="font-semibold">{{ t.enrich }}</h2>
  <p class="text-sm text-muted-foreground">
    {{ t.enrich_uses }}
  </p>
  <pre class="overflow-x-auto rounded-md bg-muted p-3 text-sm"><code>commonmeta sync --zotero-url {{ status.api_url }}</code></pre>
</section>
{% endblock %}