{% extends "base.html" %}
{% block title %}{{ t.library }}{% endblock %}
{% block content %}
<p class="mb-4 flex flex-wrap items-baseline justify-between gap-2 text-sm text-muted-foreground">
<span>{{ t.signed_in_as }} <span class="font-medium text-foreground">{{ name }}</span></span>
{%- if status.admin %}
<a href="/ui/admin" class="underline">{{ t.admin }}</a>
{%- endif %}
</p>
{#- 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">{{ t.storage_used }}: <span class="tabular-nums">{{ status.usage }}</span></p>
{%- if status.over_quota %}
<p role="alert" class="text-sm text-destructive">{{ t.over_quota }}</p>
{%- endif %}
{%- if status.admin %}
<p class="text-sm"><code class="rounded bg-muted px-1.5 py-0.5">{{ status.storage_location }}</code></p>
{%- endif %}
<p class="text-sm text-muted-foreground">
{%- if status.admin %}
{%- if status.signed_downloads %}
{{ t.signed_downloads }}
{%- else %}
{{ t.streamed_downloads }}
{%- endif %}
{%- endif %}
{{ t.files_up_to }} {{ status.max_upload }} {{ t.are_accepted }}
</p>
</section>
{%- if status.admin %}
<section class="mt-4 rounded-lg border bg-card p-5 space-y-2">
<h2 class="font-semibold">{{ t.backups }}</h2>
<p class="text-sm {% if status.backup_failed %}text-destructive{% else %}text-muted-foreground{% endif %}">{{ status.backup }}</p>
</section>
{%- endif %}
</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.groups }}</h2>
<p class="text-sm text-muted-foreground">{{ t.groups_intro }}</p>
{%- if groups.is_empty() %}
<p class="text-sm text-muted-foreground">{{ t.no_groups }}</p>
{%- else %}
<ul class="divide-y text-sm">
{%- for group in groups %}
<li class="flex items-baseline justify-between gap-2 py-2">
<a href="/ui/groups/{{ group.id }}" class="font-medium underline">{{ group.name }}</a>
<span class="text-muted-foreground">{{ group.role }}</span>
</li>
{%- endfor %}
</ul>
{%- endif %}
{%- if can_write %}
<form method="post" action="/ui/groups" class="flex flex-wrap items-end gap-2">
<label class="space-y-1">
<span class="block text-sm font-medium">{{ t.group_name }}</span>
<input name="name" required maxlength="100"
class="w-64 rounded-md border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
</label>
<button type="submit" class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted">{{ t.create_group }}</button>
</form>
{%- endif %}
</section>
<section class="mt-4 rounded-lg border bg-card p-5 space-y-3">
<h2 class="font-semibold">{{ t.devices }}</h2>
{%- if devices.is_empty() %}
<p class="text-sm text-muted-foreground">{{ t.no_devices }}</p>
{%- else %}
<p class="text-sm text-muted-foreground">{{ t.devices_intro }}</p>
<ul class="divide-y text-sm">
{%- for device in devices %}
<li class="flex flex-wrap items-center justify-between gap-2 py-2">
<span>
<span class="font-medium">{{ device.name }}</span>
<code class="ml-1 rounded bg-muted px-1.5 py-0.5 text-xs">{{ device.prefix }}…</code>
<span class="block text-muted-foreground">
{{ t.added }} {{ device.added }} ·
{%- if let Some(last_used) = device.last_used %} {{ t.last_used }} {{ last_used }}{% else %} {{ t.never_used }}{% endif %}
</span>
</span>
{%- if can_write %}
<form method="post" action="/ui/keys/revoke">
<input type="hidden" name="id" value="{{ device.id }}">
<button type="submit" class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted">{{ t.revoke }}</button>
</form>
{%- endif %}
</li>
{%- endfor %}
</ul>
{%- endif %}
</section>
<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_intro }}</p>
<ol class="list-decimal space-y-1 pl-5 text-sm">
<li>{{ t.connect_download }} <a href="{{ status.plugin_url }}" class="underline">defter for Zotero</a>{{ t.connect_install }}</li>
<li>{{ t.connect_server }} <code class="rounded bg-muted px-1.5 py-0.5">{{ status.server_url }}</code></li>
<li>{{ t.connect_login }}</li>
</ol>
</section>
{%- if let Some(sync) = sync %}
<section class="mt-4 rounded-lg border bg-card p-5 space-y-3">
<h2 class="font-semibold">{{ t.sync_heading }}</h2>
<p class="text-sm text-muted-foreground">{{ t.sync_intro }}</p>
{%- if can_write %}
<form method="post" action="/ui/sync" class="space-y-2 text-sm">
<label class="flex items-start gap-2">
<input type="checkbox" name="enabled" class="mt-1"{% if sync.setting.enabled %} checked{% endif %}>
<span class="font-medium">{{ t.sync_enabled }}</span>
</label>
<label class="flex items-start gap-2">
<input type="checkbox" name="pdf" class="mt-1"{% if sync.setting.pdf %} checked{% endif %}>
<span>{{ t.sync_pdf }}</span>
</label>
<label class="flex items-start gap-2">
<input type="checkbox" name="publications" class="mt-1"
{%- if sync.setting.publications %} checked{% endif %}{% if !sync.has_orcid %} disabled{% endif %}>
<span>{{ t.sync_publications }}
{%- if !sync.has_orcid %} <span class="block text-muted-foreground">{{ t.sync_needs_orcid }}</span>{% endif %}</span>
</label>
<label class="flex items-start gap-2">
<input type="checkbox" name="deduplicate" class="mt-1"{% if sync.setting.deduplicate %} checked{% endif %}>
<span>{{ t.sync_deduplicate }}</span>
</label>
<label class="flex items-start gap-2">
<input type="checkbox" name="overwrite" class="mt-1"{% if sync.setting.overwrite %} checked{% endif %}>
<span>{{ t.sync_overwrite }}
<span class="block text-muted-foreground">{{ t.sync_overwrite_hint }}</span></span>
</label>
<button type="submit" class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted">{{ t.save }}</button>
</form>
<div class="flex flex-wrap gap-2">
<form method="post" action="/ui/sync/run">
<input type="hidden" name="dry_run" value="true">
<button type="submit" class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted">{{ t.sync_preview }}</button>
</form>
{%- if sync.setting.enabled %}
<form method="post" action="/ui/sync/run">
<input type="hidden" name="dry_run" value="false">
<button type="submit" class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted">{{ t.sync_now }}</button>
</form>
{%- endif %}
</div>
{%- endif %}
{%- if sync.busy %}
<p role="alert" class="text-sm text-destructive">{{ t.sync_busy }}</p>
{%- endif %}
{%- if sync.running %}
<p class="text-sm">{{ t.sync_running }}</p>
{%- endif %}
{%- if let Some(run) = sync.last %}
<p class="text-sm">
<span class="{% if run.failed %}text-destructive{% else %}text-muted-foreground{% endif %}">{{ run.label }} {{ run.at }}, {{ run.status }}.</span>
{{ run.counts }}.
</p>
{%- if let Some(error) = run.error %}
<p role="alert" class="text-sm text-destructive">{{ error }}</p>
{%- endif %}
{%- if !run.results.is_empty() %}
<p class="text-sm"><a href="/ui/sync/log.json" class="underline">{{ t.sync_log }}</a></p>
<details class="text-sm">
<summary class="cursor-pointer">{{ t.sync_details }}</summary>
<ul class="mt-2 divide-y">
{%- for result in run.results %}
<li class="py-1.5">
{%- if !result.key.is_empty() %}<code class="rounded bg-muted px-1.5 py-0.5 text-xs">{{ result.key }}</code> {% endif %}
{%- if !result.doi.is_empty() %}<a href="https://doi.org/{{ result.doi }}" class="underline">{{ result.doi }}</a> {% endif %}
<span class="text-muted-foreground">{{ result.status }}</span>
{%- if !result.detail.is_empty() %}<span class="block text-muted-foreground">{{ result.detail }}</span>{% endif %}
</li>
{%- endfor %}
</ul>
{%- if run.more > 0 %}
<p class="mt-1 text-muted-foreground">{{ run.more }} {{ t.sync_more }}</p>
{%- endif %}
</details>
{%- endif %}
{%- else %}
<p class="text-sm text-muted-foreground">{{ t.sync_never }}</p>
{%- endif %}
</section>
{%- endif %}
{% endblock %}