adminx-core 3.0.0

Framework-neutral core for the adminx admin-panel framework: the Resource trait, storage abstraction, registry, and neutral request/response types shared by every web-framework and database adapter.
Documentation
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>{{ title | default(value="Set up two-factor auth") }}</title>
  <script>window.tailwind = { config: { darkMode: 'class' } };</script>
  <script src="{{ tailwind_src | safe }}"></script>
  <script>
    (function () {
      try {
        var stored = localStorage.getItem('adminx-theme');
        var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
        if ((stored || (prefersDark ? 'dark' : 'light')) === 'dark') document.documentElement.classList.add('dark');
      } catch (_) {}
    })();
  </script>
  <style>
    /* The generated QR is an <svg> with its own dimensions; make it fill its box. */
    .qr-box svg { width: 100%; height: 100%; display: block; }
  </style>
</head>
<body class="h-full bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100 selection:bg-indigo-200/50 dark:selection:bg-indigo-400/30">
  <div class="flex items-center justify-center min-h-screen px-4 py-10">
    <div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg w-full max-w-md border border-gray-200 dark:border-gray-700">
      <div class="text-center mb-8">
        <div class="mx-auto w-16 h-16 bg-gradient-to-r from-indigo-600 to-fuchsia-600 rounded-full flex items-center justify-center mb-4">
          <svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
          </svg>
        </div>
        <h2 class="text-2xl font-bold text-gray-900 dark:text-white">Set up two-factor auth</h2>
        <p class="text-gray-600 dark:text-gray-400 mt-2">Scan the QR code with an authenticator app, then enter the 6-digit code to enable MFA.</p>
      </div>

      {% if error %}
      <div class="mb-6 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
        <div class="flex items-center">
          <svg class="w-5 h-5 text-red-500 mr-2 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
          </svg>
          <span class="text-red-700 dark:text-red-400 text-sm font-medium">{{ error }}</span>
        </div>
      </div>
      {% endif %}

      <div class="flex justify-center mb-6">
        <div class="qr-box w-52 h-52 p-3 bg-white rounded-lg border border-gray-200">{{ qr_svg | safe }}</div>
      </div>

      <p class="text-xs text-gray-500 dark:text-gray-400 mb-1 text-center">Can't scan? Enter this key manually:</p>
      <code class="block text-center break-all bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-sm text-gray-800 dark:text-gray-200 mb-6">{{ secret }}</code>

      <form method="post" action="{{ mount | safe }}/mfa/enable" class="space-y-6">
        <input type="hidden" name="_csrf" value="{{ csrf_token }}">
        <div>
          <label for="code" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Verification code</label>
          <input type="text" id="code" name="code" required autofocus inputmode="numeric" pattern="[0-9]*"
            autocomplete="one-time-code" placeholder="123456"
            class="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 text-center tracking-[0.4em] focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-colors">
        </div>
        <button type="submit"
          class="w-full flex justify-center items-center py-3 px-4 rounded-lg shadow-sm text-sm font-medium text-white bg-gradient-to-r from-indigo-600 to-fuchsia-600 hover:from-indigo-700 hover:to-fuchsia-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800 transition-all duration-200">
          Enable MFA
        </button>
      </form>

      <div class="mt-6 text-center">
        <a href="{{ mount | safe }}" class="text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300">Skip for now</a>
      </div>
    </div>
  </div>
</body>
</html>