defter 0.8.2

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

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

{% block content %}
<section class="space-y-2">
  <h1 class="text-2xl font-semibold tracking-tight">{{ t.admin }}</h1>
  {%- if let Some(error) = error %}
  <p role="alert" class="text-sm text-destructive">{{ error }}</p>
  {%- endif %}
</section>

<section class="mt-6 rounded-lg border bg-card p-5 space-y-3">
  <h2 class="font-semibold">{{ t.invites }}</h2>
  {%- if let Some(access) = access %}
  <p class="text-sm text-muted-foreground">{{ t.invites_off }}</p>
  <dl class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-sm">
    <dt class="text-muted-foreground">{{ t.access_claim }}</dt><dd class="font-mono">{{ access.claim }}</dd>
    <dt class="text-muted-foreground">{{ t.access_users }}</dt><dd class="font-mono">{{ access.users }}</dd>
    {%- if !access.admins.is_empty() %}
    <dt class="text-muted-foreground">{{ t.access_admins }}</dt><dd class="font-mono">{{ access.admins }}</dd>
    {%- endif %}
  </dl>
  {%- else %}
  <p class="text-sm text-muted-foreground">{{ t.invites_intro }}</p>
  <form method="post" action="/ui/admin/invite" class="flex flex-wrap items-end gap-2">
    <label class="space-y-1">
      <span class="block text-sm font-medium">{{ t.orcid_id }}</span>
      <input name="orcid" required placeholder="0000-0000-0000-0000" spellcheck="false"
             class="w-56 rounded-md border bg-background px-3 py-1.5 font-mono text-sm focus:outline-none focus:ring-2 focus:ring-ring">
    </label>
    <label class="space-y-1">
      <span class="block text-sm font-medium">{{ t.note }}</span>
      <input name="note" maxlength="200"
             class="w-56 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 bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90">{{ t.invite }}</button>
  </form>
  {%- if invites.is_empty() %}
  <p class="text-sm text-muted-foreground">{{ t.no_invites }}</p>
  {%- else %}
  <ul class="divide-y text-sm">
    {%- for invite in invites %}
    <li class="flex flex-wrap items-center justify-between gap-2 py-2">
      <span>
        <a href="https://orcid.org/{{ invite.orcid }}" class="font-mono underline">{{ invite.orcid }}</a>
        {%- if !invite.note.is_empty() %} <span class="text-muted-foreground">· {{ invite.note }}</span>{% endif %}
        <span class="block text-muted-foreground">{{ t.added }} {{ invite.added }}
          {%- if let Some(user) = invite.used_by %} · {{ t.invite_used }} {{ user }}{% endif %}</span>
      </span>
      {%- if invite.used_by.is_none() %}
      <form method="post" action="/ui/admin/uninvite">
        <input type="hidden" name="orcid" value="{{ invite.orcid }}">
        <button type="submit" class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted">{{ t.uninvite }}</button>
      </form>
      {%- endif %}
    </li>
    {%- endfor %}
  </ul>
  {%- endif %}
  {%- endif %}
</section>

<section class="mt-4 rounded-lg border bg-card p-5 space-y-3">
  <h2 class="font-semibold">{{ t.users }}</h2>
  <ul class="divide-y text-sm">
    {%- for user in users %}
    <li class="space-y-2 py-3">
      <div class="flex flex-wrap items-baseline justify-between gap-2">
        <span>
          <span class="font-medium">{{ user.name }}</span>
          <span class="text-muted-foreground">· {{ t.user }} {{ user.id }}
            {%- if user.owner %} · {{ t.owner }}{% endif %}
            {%- if user.me %} · {{ t.you }}{% endif %}
            {%- if user.access_disabled %} · <span class="text-destructive">{{ t.disabled_by_provider }}</span>
            {%- else if user.disabled %} · <span class="text-destructive">{{ t.disabled }}</span>{% endif %}</span>
          {%- if !user.orcid.is_empty() %}
          <a href="https://orcid.org/{{ user.orcid }}" class="block font-mono text-muted-foreground underline">{{ user.orcid }}</a>
          {%- endif %}
        </span>
        <span class="text-muted-foreground tabular-nums">{{ user.usage }} · {{ user.devices }} {{ t.devices_count }}
          {%- if let Some(sync) = user.sync %} · {{ t.sync_heading }} {{ sync }}{% endif %}
          {%- if let Some(last) = user.sync_last %}
          <span class="block{% if user.sync_failed %} text-destructive{% endif %}">{{ last }}</span>
          {%- endif %}</span>
      </div>
      <div class="flex flex-wrap items-end gap-2">
        <form method="post" action="/ui/admin/user" class="flex flex-wrap items-end gap-2">
          <input type="hidden" name="id" value="{{ user.id }}">
          <label class="space-y-1">
            <span class="block text-xs text-muted-foreground">{{ t.quota_mb }},
              {%- if user.owner %} {{ t.empty_for_no_limit }}{% else %} {{ t.default_quota_is }} {{ default_quota }}{% endif %}</span>
            <input name="quota_mb" value="{{ user.quota_mb }}" inputmode="numeric"
                   class="w-32 rounded-md border bg-background px-3 py-1.5 text-sm tabular-nums 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.save }}</button>
        </form>
        {%- if !user.me && !user.owner %}
        <form method="post" action="/ui/admin/user">
          <input type="hidden" name="id" value="{{ user.id }}">
          <input type="hidden" name="disabled" value="{% if user.disabled %}false{% else %}true{% endif %}">
          <button type="submit" class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted">
            {%- if user.disabled %}{{ t.enable }}{% else %}{{ t.disable }}{% endif -%}
          </button>
        </form>
        {%- endif %}
      </div>
    </li>
    {%- endfor %}
  </ul>
</section>

{%- if let Some(sync) = sync %}
<section class="mt-4 rounded-lg border bg-card p-5 space-y-2">
  <h2 class="font-semibold">{{ t.sync_heading }}</h2>
  <p class="text-sm {% if sync_failed %}text-destructive{% else %}text-muted-foreground{% endif %}">{{ sync }}</p>
</section>
{%- endif %}

<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 backup_failed %}text-destructive{% else %}text-muted-foreground{% endif %}">{{ backup }}</p>
</section>
{% endblock %}