layout.twig 6.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width,initial-scale=1">
  6. <title>{{ title|default('Sprint Planner') }}</title>
  7. <script src="/assets/js/theme-init.js"></script>
  8. <link rel="stylesheet" href="/assets/css/app.css">
  9. <script src="/assets/js/vendor/htmx.min.js" defer></script>
  10. <script src="/assets/js/vendor/sortable.min.js" defer></script>
  11. <script src="/assets/js/app.js" defer></script>
  12. <script src="/assets/js/vendor/alpine-csp.min.js" defer></script>
  13. {% block head_extra %}{% endblock %}
  14. </head>
  15. <body class="bg-slate-100 text-slate-900 antialiased dark:bg-slate-900 dark:text-slate-100">
  16. <header class="border-b bg-white dark:bg-slate-800 dark:border-slate-700">
  17. <div class="max-w-7xl mx-auto px-4 py-3 flex items-center gap-4">
  18. <a href="/" class="font-semibold tracking-tight">Sprint Planner</a>
  19. <nav class="ml-auto flex items-center gap-4 text-sm">
  20. {% if currentUser is not null %}
  21. <a href="/" class="text-slate-600 hover:text-slate-900 hover:underline dark:text-slate-300 dark:hover:text-slate-100">Sprints</a>
  22. {% if currentUser.isAdmin %}
  23. <a href="/sprints/new" class="text-slate-600 hover:text-slate-900 hover:underline dark:text-slate-300 dark:hover:text-slate-100">New sprint</a>
  24. <a href="/sprints/import" class="text-slate-600 hover:text-slate-900 hover:underline dark:text-slate-300 dark:hover:text-slate-100">Import</a>
  25. {% endif %}
  26. <span class="text-slate-400 dark:text-slate-600">·</span>
  27. <span class="text-slate-600 dark:text-slate-300">
  28. {{ currentUser.displayName }}
  29. {% if currentUser.isAdmin %}
  30. <span class="ml-1 inline-block px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider bg-amber-100 text-amber-800 rounded dark:bg-amber-900 dark:text-amber-200">admin</span>
  31. {% endif %}
  32. </span>
  33. <div class="relative" x-data="appMenu">
  34. <button type="button"
  35. x-ref="trigger"
  36. x-on:click="toggle()"
  37. x-bind:aria-expanded="open"
  38. aria-haspopup="true"
  39. aria-controls="app-menu"
  40. aria-label="Open menu"
  41. class="p-2 rounded-md hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:text-slate-200 dark:hover:bg-slate-700">
  42. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" class="block stroke-current" fill="none" stroke-width="2" stroke-linecap="round">
  43. <line x1="3" y1="5" x2="17" y2="5"></line>
  44. <line x1="3" y1="10" x2="17" y2="10"></line>
  45. <line x1="3" y1="15" x2="17" y2="15"></line>
  46. </svg>
  47. </button>
  48. <div id="app-menu"
  49. role="menu"
  50. x-show="open"
  51. x-on:click.outside="close()"
  52. x-on:keydown.escape.window="closeAndFocus()"
  53. x-on:click="closeOnItem($event)"
  54. x-cloak
  55. class="absolute right-0 mt-2 min-w-[12rem] rounded-md border border-slate-200 bg-white shadow-lg py-1 z-10 dark:bg-slate-800 dark:border-slate-700">
  56. {% if currentUser.isAdmin %}
  57. <a href="/workers" role="menuitem"
  58. class="block px-3 py-2 text-sm text-slate-700 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:text-slate-200 dark:hover:bg-slate-700">Workers</a>
  59. <a href="/users" role="menuitem"
  60. class="block px-3 py-2 text-sm text-slate-700 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:text-slate-200 dark:hover:bg-slate-700">Users</a>
  61. <a href="/audit" role="menuitem"
  62. class="block px-3 py-2 text-sm text-slate-700 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:text-slate-200 dark:hover:bg-slate-700">Audit log</a>
  63. <a href="/settings" role="menuitem"
  64. class="block px-3 py-2 text-sm text-slate-700 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:text-slate-200 dark:hover:bg-slate-700">Settings</a>
  65. {% endif %}
  66. <button type="button" role="menuitem" x-data="themeToggle" x-on:click="flip()"
  67. class="w-full text-left px-3 py-2 text-sm text-slate-700 hover:bg-slate-50 flex items-center justify-between focus:outline-none focus:ring-2 focus:ring-slate-400 dark:text-slate-200 dark:hover:bg-slate-700">
  68. <span>Theme</span>
  69. <span x-text="label" class="text-slate-500 dark:text-slate-400">Light</span>
  70. </button>
  71. <hr class="my-1 border-slate-200 dark:border-slate-700">
  72. <form method="post" action="/auth/logout">
  73. <input type="hidden" name="_csrf" value="{{ csrfToken }}">
  74. <button type="submit" role="menuitem"
  75. class="block w-full text-left px-3 py-2 text-sm text-slate-700 hover:bg-slate-50 font-[inherit] focus:outline-none focus:ring-2 focus:ring-slate-400 dark:text-slate-200 dark:hover:bg-slate-700">
  76. Sign out
  77. </button>
  78. </form>
  79. </div>
  80. </div>
  81. {% else %}
  82. <a href="/auth/login"
  83. class="text-blue-700 hover:underline dark:text-blue-400 dark:hover:text-blue-300">Sign in</a>
  84. {% endif %}
  85. </nav>
  86. </div>
  87. </header>
  88. <main class="max-w-7xl mx-auto px-4 py-6">
  89. {% block content %}{% endblock %}
  90. </main>
  91. </body>
  92. </html>