index.twig 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. {% extends 'layout.twig' %}
  2. {% block title %}Manual blocks — IRDB{% endblock %}
  3. {% block content %}
  4. {% import 'partials/sort.twig' as sort %}
  5. <div class="mx-auto max-w-5xl">
  6. <div class="flex items-center justify-between">
  7. <h1 class="text-2xl font-semibold tracking-tight">
  8. {% if active_section == 'subnets' %}Subnets{% else %}Manual blocks{% endif %}
  9. </h1>
  10. <span class="text-sm text-slate-500 dark:text-slate-400">{{ list.total|default(0) }} total</span>
  11. </div>
  12. {% set kind_links = [
  13. { label: 'All', value: '' },
  14. { label: 'IPs', value: 'ip' },
  15. { label: 'Subnets', value: 'subnet' },
  16. ] %}
  17. <div class="mt-4 flex gap-2 text-sm">
  18. {% for k in kind_links %}
  19. {% set is_active = (kind|default('') == k.value) or (kind == null and k.value == '') %}
  20. <a href="/app/manual-blocks{% if k.value %}?kind={{ k.value }}{% endif %}"
  21. class="rounded-full px-3 py-1 {% if is_active %}bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-100{% else %}border border-slate-300 text-slate-600 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800{% endif %}">
  22. {{ k.label }}
  23. </a>
  24. {% endfor %}
  25. </div>
  26. {% if can_write %}
  27. <section class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
  28. <h2 class="text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400">Add manual block</h2>
  29. <form method="post" action="/app/manual-blocks" class="mt-3 grid grid-cols-1 gap-3 md:grid-cols-4 text-sm" x-data="{ kind: '{{ kind|default('subnet') }}' }">
  30. <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
  31. <div>
  32. <label for="mb-kind" class="block text-xs font-medium text-slate-600 dark:text-slate-400">Kind</label>
  33. <select id="mb-kind" name="kind" x-model="kind"
  34. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 dark:border-slate-700 dark:bg-slate-950">
  35. <option value="ip">Single IP</option>
  36. <option value="subnet">Subnet (CIDR)</option>
  37. </select>
  38. </div>
  39. <div x-show="kind == 'ip'">
  40. <label for="mb-ip" class="block text-xs font-medium text-slate-600 dark:text-slate-400">IP</label>
  41. <input type="text" id="mb-ip" name="ip" placeholder="203.0.113.5"
  42. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 font-mono dark:border-slate-700 dark:bg-slate-950">
  43. </div>
  44. <div x-show="kind == 'subnet'">
  45. <label for="mb-cidr" class="block text-xs font-medium text-slate-600 dark:text-slate-400">CIDR</label>
  46. <input type="text" id="mb-cidr" name="cidr" placeholder="192.0.2.0/24"
  47. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 font-mono dark:border-slate-700 dark:bg-slate-950">
  48. </div>
  49. <div>
  50. <label for="mb-reason" class="block text-xs font-medium text-slate-600 dark:text-slate-400">Reason</label>
  51. <input type="text" id="mb-reason" name="reason"
  52. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 dark:border-slate-700 dark:bg-slate-950">
  53. </div>
  54. <div>
  55. <label for="mb-expires" class="block text-xs font-medium text-slate-600 dark:text-slate-400">Expires (optional)</label>
  56. <input type="date" id="mb-expires" name="expires_at"
  57. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 dark:border-slate-700 dark:bg-slate-950">
  58. <p class="mt-1 text-xs text-slate-400">Block ends at 23:59:59 UTC of the selected day.</p>
  59. </div>
  60. <div class="md:col-span-4 flex justify-end">
  61. <button type="submit" class="rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-indigo-500">Add block</button>
  62. </div>
  63. </form>
  64. </section>
  65. {% endif %}
  66. <section class="mt-6 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
  67. <table class="w-full text-sm" data-sortable-table="manual-blocks-index">
  68. <thead class="border-b border-slate-200 bg-slate-50 text-left text-xs uppercase tracking-wider text-slate-500 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-400">
  69. <tr>
  70. {{ sort.th('Kind', 'kind') }}
  71. {{ sort.th('Target', 'target') }}
  72. {{ sort.th('Reason', 'reason') }}
  73. {{ sort.th('Expires', 'expires', 'date') }}
  74. {{ sort.th('Created', 'created', 'date') }}
  75. {% if can_write %}<th class="px-4 py-2 text-right font-medium">Actions</th>{% endif %}
  76. </tr>
  77. </thead>
  78. <tbody class="divide-y divide-slate-100 dark:divide-slate-800">
  79. {% for item in list.items|default([]) %}
  80. <tr>
  81. <td class="px-4 py-2 font-mono text-xs uppercase" data-sort-value="{{ item.kind }}">{{ item.kind }}</td>
  82. <td class="px-4 py-2 font-mono" data-sort-value="{{ item.kind == 'ip' ? item.ip : item.cidr }}">{{ item.kind == 'ip' ? item.ip : item.cidr }}</td>
  83. <td class="px-4 py-2 text-slate-600 dark:text-slate-300" data-sort-value="{{ item.reason|default('') }}">{{ item.reason|default('—') }}</td>
  84. <td class="px-4 py-2 text-slate-500 dark:text-slate-400" data-sort-value="{{ item.expires_at|default('') }}">{% if item.expires_at %}<time class="irdb-dt" datetime="{{ item.expires_at }}">{{ item.expires_at }}</time>{% else %}—{% endif %}</td>
  85. <td class="px-4 py-2 text-slate-500 dark:text-slate-400" data-sort-value="{{ item.created_at|default('') }}">{% if item.created_at %}<time class="irdb-dt" datetime="{{ item.created_at }}">{{ item.created_at }}</time>{% endif %}</td>
  86. {% if can_write %}
  87. <td class="px-4 py-2 text-right">
  88. {% include 'partials/confirm_form.twig' with {
  89. action: '/app/manual-blocks/' ~ item.id ~ '/delete',
  90. label: 'Remove',
  91. description: 'This removes the manual block immediately.',
  92. } only %}
  93. </td>
  94. {% endif %}
  95. </tr>
  96. {% else %}
  97. <tr><td colspan="6" class="px-4 py-6 text-center text-slate-400">No manual blocks.</td></tr>
  98. {% endfor %}
  99. </tbody>
  100. </table>
  101. </section>
  102. </div>
  103. {% endblock %}