about.twig 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. {% extends "layout.twig" %}
  2. {% block content %}
  3. <section class="space-y-12" x-data="screenshotModal">
  4. {# --------------- Hero --------------- #}
  5. <header class="text-center max-w-3xl mx-auto pt-2">
  6. <div class="flex justify-center mb-4">
  7. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="96" height="96" aria-hidden="true" class="block" fill="none">
  8. <defs>
  9. <radialGradient id="brand-cycle-glow-about" cx="32" cy="32" r="20" gradientUnits="userSpaceOnUse">
  10. <stop offset="0" stop-color="#6366f1" stop-opacity="0.55"/>
  11. <stop offset="0.6" stop-color="#6366f1" stop-opacity="0.12"/>
  12. <stop offset="1" stop-color="#6366f1" stop-opacity="0"/>
  13. </radialGradient>
  14. </defs>
  15. <circle cx="32" cy="32" r="20" fill="url(#brand-cycle-glow-about)"/>
  16. <path d="M52 32 A20 20 0 1 1 32 12" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"/>
  17. <path d="M44 8 L52 12 L48 20" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
  18. <circle cx="32" cy="32" r="5" fill="#6366f1"/>
  19. <circle cx="48" cy="20" r="2.5" fill="currentColor" opacity="0.55"/>
  20. <circle cx="52" cy="40" r="2.5" fill="currentColor" opacity="0.55"/>
  21. <circle cx="40" cy="50" r="2.5" fill="currentColor" opacity="0.55"/>
  22. <circle cx="20" cy="48" r="2.5" fill="currentColor" opacity="0.55"/>
  23. <circle cx="14" cy="32" r="2.5" fill="#10b981"/>
  24. </svg>
  25. </div>
  26. <h1 class="text-4xl font-bold tracking-tight">Sprint Planner</h1>
  27. <p class="mt-3 text-lg text-slate-600 dark:text-slate-300">
  28. One shared sprint board for the whole team — replacing the
  29. spreadsheet that everybody copies and nobody keeps in sync.
  30. </p>
  31. </header>
  32. {# --------------- What it is --------------- #}
  33. <div class="rounded-lg border bg-white p-6 dark:bg-slate-800 dark:border-slate-700">
  34. <h2 class="text-2xl font-semibold tracking-tight">What it is</h2>
  35. <p class="mt-3 text-slate-700 dark:text-slate-300">
  36. Sprint Planner is a small web app for ops/dev teams that plan their
  37. work in two- to four-week sprints. It replaces an Excel sprint
  38. workbook with a proper website: shared by everyone, edited live,
  39. backed by a database, and protected with sign-in. Every change is
  40. recorded so it is always clear who moved a number and when.
  41. </p>
  42. <p class="mt-3 text-slate-700 dark:text-slate-300">
  43. Each sprint has its own page. The top of the page is a
  44. <em>working-days matrix</em> — for every week of the sprint, every
  45. team member tells the planner how many days they will be available.
  46. The bottom is the <em>task list</em> — every piece of work the
  47. team will tackle, with the days each person is going to spend on
  48. it. The page sums these numbers up live so the team can see
  49. whether they are over- or under-committed for the sprint.
  50. </p>
  51. </div>
  52. {# --------------- How it works --------------- #}
  53. <div class="space-y-6">
  54. <h2 class="text-2xl font-semibold tracking-tight text-center">How a sprint runs end-to-end</h2>
  55. <ol class="grid gap-4 md:grid-cols-2">
  56. <li class="rounded-lg border bg-white p-5 dark:bg-slate-800 dark:border-slate-700">
  57. <span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-indigo-100 text-indigo-700 font-semibold dark:bg-indigo-900 dark:text-indigo-200">1</span>
  58. <h3 class="mt-3 font-semibold">Create the sprint</h3>
  59. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  60. An admin gives the sprint a name and picks a start and end
  61. date. The planner splits that range into calendar weeks
  62. automatically — up to twenty-six.
  63. </p>
  64. </li>
  65. <li class="rounded-lg border bg-white p-5 dark:bg-slate-800 dark:border-slate-700">
  66. <span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-indigo-100 text-indigo-700 font-semibold dark:bg-indigo-900 dark:text-indigo-200">2</span>
  67. <h3 class="mt-3 font-semibold">Pick the team</h3>
  68. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  69. Add the people who will work on this sprint, set the
  70. reserve buffer (how much capacity to keep aside for
  71. interruptions), and tick the public holidays so the
  72. available-days count is right out of the box.
  73. </p>
  74. </li>
  75. <li class="rounded-lg border bg-white p-5 dark:bg-slate-800 dark:border-slate-700">
  76. <span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-indigo-100 text-indigo-700 font-semibold dark:bg-indigo-900 dark:text-indigo-200">3</span>
  77. <h3 class="mt-3 font-semibold">Capture the work</h3>
  78. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  79. Add tasks with a title, an owner, a priority, and an
  80. estimate of total days. Re-order them by drag-and-drop;
  81. filter by owner, priority, or text search.
  82. </p>
  83. </li>
  84. <li class="rounded-lg border bg-white p-5 dark:bg-slate-800 dark:border-slate-700">
  85. <span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-indigo-100 text-indigo-700 font-semibold dark:bg-indigo-900 dark:text-indigo-200">4</span>
  86. <h3 class="mt-3 font-semibold">Allocate days</h3>
  87. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  88. For every task, type how many days each person will spend.
  89. The planner re-totals the row, deducts the reserve, and
  90. shows what each person still has free — in green when
  91. there is room, in red when overcommitted.
  92. </p>
  93. </li>
  94. <li class="rounded-lg border bg-white p-5 dark:bg-slate-800 dark:border-slate-700">
  95. <span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-indigo-100 text-indigo-700 font-semibold dark:bg-indigo-900 dark:text-indigo-200">5</span>
  96. <h3 class="mt-3 font-semibold">Track progress</h3>
  97. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  98. Mark each cell as <em>started</em>, <em>done</em>, or
  99. <em>cancelled</em> as the sprint runs. Use the focus
  100. filter to look at one person's plate at a time without
  101. losing the rest.
  102. </p>
  103. </li>
  104. <li class="rounded-lg border bg-white p-5 dark:bg-slate-800 dark:border-slate-700">
  105. <span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-indigo-100 text-indigo-700 font-semibold dark:bg-indigo-900 dark:text-indigo-200">6</span>
  106. <h3 class="mt-3 font-semibold">Present and audit</h3>
  107. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  108. A big-screen view trims the chrome down for the daily
  109. stand-up or the sprint review. The audit log captures
  110. every edit — who, when, before, after — so questions
  111. like &ldquo;wait, who changed that number?&rdquo; have a
  112. factual answer.
  113. </p>
  114. </li>
  115. </ol>
  116. </div>
  117. {# --------------- Tour of the interface (gallery) --------------- #}
  118. <div class="space-y-6">
  119. <h2 class="text-2xl font-semibold tracking-tight text-center">A tour of the interface</h2>
  120. <p class="text-center text-slate-600 dark:text-slate-400 max-w-2xl mx-auto text-sm">
  121. Click any picture to enlarge it.
  122. </p>
  123. <div class="grid gap-6 md:grid-cols-2">
  124. {# 1. Sprints overview #}
  125. <figure class="rounded-lg border bg-white overflow-hidden flex flex-col dark:bg-slate-800 dark:border-slate-700">
  126. <button type="button"
  127. x-on:click="open($event)"
  128. data-src="/assets/img/screenshots/Sprints_view.png"
  129. data-alt="Sprints overview"
  130. aria-label="Enlarge: Sprints overview"
  131. class="block w-full bg-slate-50 dark:bg-slate-900/50 focus:outline-none focus:ring-2 focus:ring-indigo-400 group">
  132. <img src="/assets/img/screenshots/Sprints_view.png"
  133. alt="Sprints overview"
  134. class="w-full h-56 object-cover object-top transition group-hover:opacity-90">
  135. </button>
  136. <figcaption class="p-4 flex-1 flex flex-col">
  137. <div class="flex items-baseline justify-between gap-3">
  138. <h3 class="font-semibold">Sprints overview</h3>
  139. <button type="button"
  140. x-on:click="open($event)"
  141. data-src="/assets/img/screenshots/Sprints_view.png"
  142. data-alt="Sprints overview"
  143. class="text-xs text-indigo-700 hover:underline dark:text-indigo-400 whitespace-nowrap">Click to enlarge</button>
  144. </div>
  145. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  146. The home page lists every sprint with its dates, worker
  147. count, task count, and reserve percentage. Click a row
  148. to open it.
  149. </p>
  150. </figcaption>
  151. </figure>
  152. {# 2. Sprint settings #}
  153. <figure class="rounded-lg border bg-white overflow-hidden flex flex-col dark:bg-slate-800 dark:border-slate-700">
  154. <button type="button"
  155. x-on:click="open($event)"
  156. data-src="/assets/img/screenshots/Sprint_settings.png"
  157. data-alt="Sprint settings"
  158. aria-label="Enlarge: Sprint settings"
  159. class="block w-full bg-slate-50 dark:bg-slate-900/50 focus:outline-none focus:ring-2 focus:ring-indigo-400 group">
  160. <img src="/assets/img/screenshots/Sprint_settings.png"
  161. alt="Sprint settings"
  162. class="w-full h-56 object-cover object-top transition group-hover:opacity-90">
  163. </button>
  164. <figcaption class="p-4 flex-1 flex flex-col">
  165. <div class="flex items-baseline justify-between gap-3">
  166. <h3 class="font-semibold">Sprint settings</h3>
  167. <button type="button"
  168. x-on:click="open($event)"
  169. data-src="/assets/img/screenshots/Sprint_settings.png"
  170. data-alt="Sprint settings"
  171. class="text-xs text-indigo-700 hover:underline dark:text-indigo-400 whitespace-nowrap">Click to enlarge</button>
  172. </div>
  173. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  174. Edit the dates and reserve buffer, add or remove team
  175. members, mark which weekdays are working days for each
  176. week, and set a per-person reserve.
  177. </p>
  178. </figcaption>
  179. </figure>
  180. {# 3. Working-days matrix #}
  181. <figure class="rounded-lg border bg-white overflow-hidden flex flex-col dark:bg-slate-800 dark:border-slate-700">
  182. <button type="button"
  183. x-on:click="open($event)"
  184. data-src="/assets/img/screenshots/Sprint_recources_view.png"
  185. data-alt="Working-days matrix (Arbeitstage)"
  186. aria-label="Enlarge: Working-days matrix"
  187. class="block w-full bg-slate-50 dark:bg-slate-900/50 focus:outline-none focus:ring-2 focus:ring-indigo-400 group">
  188. <img src="/assets/img/screenshots/Sprint_recources_view.png"
  189. alt="Working-days matrix (Arbeitstage)"
  190. class="w-full h-56 object-cover object-top transition group-hover:opacity-90">
  191. </button>
  192. <figcaption class="p-4 flex-1 flex flex-col">
  193. <div class="flex items-baseline justify-between gap-3">
  194. <h3 class="font-semibold">Working-days matrix</h3>
  195. <button type="button"
  196. x-on:click="open($event)"
  197. data-src="/assets/img/screenshots/Sprint_recources_view.png"
  198. data-alt="Working-days matrix (Arbeitstage)"
  199. class="text-xs text-indigo-700 hover:underline dark:text-indigo-400 whitespace-nowrap">Click to enlarge</button>
  200. </div>
  201. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  202. For every team member, type the number of days they
  203. will be available each week. Half-day steps are
  204. allowed. The summary row at the top shows the total
  205. capacity for the whole sprint.
  206. </p>
  207. </figcaption>
  208. </figure>
  209. {# 4. Task list #}
  210. <figure class="rounded-lg border bg-white overflow-hidden flex flex-col dark:bg-slate-800 dark:border-slate-700">
  211. <button type="button"
  212. x-on:click="open($event)"
  213. data-src="/assets/img/screenshots/Sprint_task_view.png"
  214. data-alt="Task list with per-worker allocations"
  215. aria-label="Enlarge: Task list"
  216. class="block w-full bg-slate-50 dark:bg-slate-900/50 focus:outline-none focus:ring-2 focus:ring-indigo-400 group">
  217. <img src="/assets/img/screenshots/Sprint_task_view.png"
  218. alt="Task list with per-worker allocations"
  219. class="w-full h-56 object-cover object-top transition group-hover:opacity-90">
  220. </button>
  221. <figcaption class="p-4 flex-1 flex flex-col">
  222. <div class="flex items-baseline justify-between gap-3">
  223. <h3 class="font-semibold">Task list</h3>
  224. <button type="button"
  225. x-on:click="open($event)"
  226. data-src="/assets/img/screenshots/Sprint_task_view.png"
  227. data-alt="Task list with per-worker allocations"
  228. class="text-xs text-indigo-700 hover:underline dark:text-indigo-400 whitespace-nowrap">Click to enlarge</button>
  229. </div>
  230. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  231. Every task is one row: title, owner, priority, total
  232. days, and one cell per team member. Search, sort,
  233. filter by owner or priority, hide the columns you
  234. don't need, and drag rows to reorder.
  235. </p>
  236. </figcaption>
  237. </figure>
  238. {# 5. Cell time + status picker #}
  239. <figure class="rounded-lg border bg-white overflow-hidden flex flex-col dark:bg-slate-800 dark:border-slate-700">
  240. <button type="button"
  241. x-on:click="open($event)"
  242. data-src="/assets/img/screenshots/Task_user_time_status.png"
  243. data-alt="Per-cell time and status picker"
  244. aria-label="Enlarge: Per-cell time and status picker"
  245. class="block w-full bg-slate-50 dark:bg-slate-900/50 focus:outline-none focus:ring-2 focus:ring-indigo-400 group">
  246. <img src="/assets/img/screenshots/Task_user_time_status.png"
  247. alt="Per-cell time and status picker"
  248. class="w-full h-56 object-contain transition group-hover:opacity-90">
  249. </button>
  250. <figcaption class="p-4 flex-1 flex flex-col">
  251. <div class="flex items-baseline justify-between gap-3">
  252. <h3 class="font-semibold">Cell time &amp; status</h3>
  253. <button type="button"
  254. x-on:click="open($event)"
  255. data-src="/assets/img/screenshots/Task_user_time_status.png"
  256. data-alt="Per-cell time and status picker"
  257. class="text-xs text-indigo-700 hover:underline dark:text-indigo-400 whitespace-nowrap">Click to enlarge</button>
  258. </div>
  259. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  260. Click a cell to slide the day count and tag progress
  261. — assigned, started, done, or cancelled. The cell tints
  262. yellow / green / red so the team can scan progress at
  263. a glance.
  264. </p>
  265. </figcaption>
  266. </figure>
  267. {# 6. Task menu #}
  268. <figure class="rounded-lg border bg-white overflow-hidden flex flex-col dark:bg-slate-800 dark:border-slate-700">
  269. <button type="button"
  270. x-on:click="open($event)"
  271. data-src="/assets/img/screenshots/Task_edit_menu.png"
  272. data-alt="Task menu — edit, move, copy, delete"
  273. aria-label="Enlarge: Task menu"
  274. class="block w-full bg-slate-50 dark:bg-slate-900/50 focus:outline-none focus:ring-2 focus:ring-indigo-400 group">
  275. <img src="/assets/img/screenshots/Task_edit_menu.png"
  276. alt="Task menu — edit, move, copy, delete"
  277. class="w-full h-56 object-contain transition group-hover:opacity-90">
  278. </button>
  279. <figcaption class="p-4 flex-1 flex flex-col">
  280. <div class="flex items-baseline justify-between gap-3">
  281. <h3 class="font-semibold">Task menu</h3>
  282. <button type="button"
  283. x-on:click="open($event)"
  284. data-src="/assets/img/screenshots/Task_edit_menu.png"
  285. data-alt="Task menu — edit, move, copy, delete"
  286. class="text-xs text-indigo-700 hover:underline dark:text-indigo-400 whitespace-nowrap">Click to enlarge</button>
  287. </div>
  288. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  289. Each task carries a small menu: edit the title and
  290. description, attach a link, move or copy the task to
  291. another sprint, or delete it. The right-hand pane
  292. shows the read-only details.
  293. </p>
  294. </figcaption>
  295. </figure>
  296. {# 7. Presenter view #}
  297. <figure class="rounded-lg border bg-white overflow-hidden flex flex-col dark:bg-slate-800 dark:border-slate-700">
  298. <button type="button"
  299. x-on:click="open($event)"
  300. data-src="/assets/img/screenshots/Sprint_presenter_screen.png"
  301. data-alt="Big-screen presenter view"
  302. aria-label="Enlarge: Big-screen presenter view"
  303. class="block w-full bg-slate-50 dark:bg-slate-900/50 focus:outline-none focus:ring-2 focus:ring-indigo-400 group">
  304. <img src="/assets/img/screenshots/Sprint_presenter_screen.png"
  305. alt="Big-screen presenter view"
  306. class="w-full h-56 object-cover object-top transition group-hover:opacity-90">
  307. </button>
  308. <figcaption class="p-4 flex-1 flex flex-col">
  309. <div class="flex items-baseline justify-between gap-3">
  310. <h3 class="font-semibold">Big-screen presenter</h3>
  311. <button type="button"
  312. x-on:click="open($event)"
  313. data-src="/assets/img/screenshots/Sprint_presenter_screen.png"
  314. data-alt="Big-screen presenter view"
  315. class="text-xs text-indigo-700 hover:underline dark:text-indigo-400 whitespace-nowrap">Click to enlarge</button>
  316. </div>
  317. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  318. A stripped-down view tuned for the projector at a
  319. stand-up. Worker columns can rotate to fit a wide
  320. team into one screen without scrolling.
  321. </p>
  322. </figcaption>
  323. </figure>
  324. {# 8. Audit log #}
  325. <figure class="rounded-lg border bg-white overflow-hidden flex flex-col dark:bg-slate-800 dark:border-slate-700">
  326. <button type="button"
  327. x-on:click="open($event)"
  328. data-src="/assets/img/screenshots/Auditlog.png"
  329. data-alt="Audit log"
  330. aria-label="Enlarge: Audit log"
  331. class="block w-full bg-slate-50 dark:bg-slate-900/50 focus:outline-none focus:ring-2 focus:ring-indigo-400 group">
  332. <img src="/assets/img/screenshots/Auditlog.png"
  333. alt="Audit log"
  334. class="w-full h-56 object-cover object-top transition group-hover:opacity-90">
  335. </button>
  336. <figcaption class="p-4 flex-1 flex flex-col">
  337. <div class="flex items-baseline justify-between gap-3">
  338. <h3 class="font-semibold">Audit log</h3>
  339. <button type="button"
  340. x-on:click="open($event)"
  341. data-src="/assets/img/screenshots/Auditlog.png"
  342. data-alt="Audit log"
  343. class="text-xs text-indigo-700 hover:underline dark:text-indigo-400 whitespace-nowrap">Click to enlarge</button>
  344. </div>
  345. <p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
  346. Every create, update, and delete is logged with user,
  347. time, and a before/after snapshot. Filter by user,
  348. action, entity, or date range — useful when something
  349. looks off and someone wants to know what happened.
  350. </p>
  351. </figcaption>
  352. </figure>
  353. </div>
  354. </div>
  355. {# --------------- Quickstart --------------- #}
  356. <div class="rounded-lg border bg-white p-6 dark:bg-slate-800 dark:border-slate-700">
  357. <h2 class="text-2xl font-semibold tracking-tight">Quickstart</h2>
  358. <p class="mt-2 text-sm text-slate-600 dark:text-slate-400">
  359. For a pilot install on one host. You need <strong>Docker</strong>
  360. (with the <code>docker compose</code> plugin) and not much else.
  361. </p>
  362. <ol class="mt-5 space-y-5 text-sm">
  363. <li>
  364. <p class="font-semibold">1. Clone the repository.</p>
  365. <pre class="mt-2 p-3 rounded bg-slate-900 text-slate-100 text-xs overflow-x-auto"><code>git clone https://git.chiapparini.org/chiappa/sprint_planer_web.git
  366. cd sprint_planer_web</code></pre>
  367. </li>
  368. <li>
  369. <p class="font-semibold">2. Make a config file.</p>
  370. <pre class="mt-2 p-3 rounded bg-slate-900 text-slate-100 text-xs overflow-x-auto"><code>cp .env.example .env
  371. chmod 600 .env</code></pre>
  372. <p class="mt-2 text-slate-600 dark:text-slate-400">
  373. Open <code>.env</code> and either fill in the
  374. <code>ENTRA_*</code> values (to sign in with a Microsoft
  375. work account) or set
  376. <code>LOCAL_ADMIN_EMAIL</code> and
  377. <code>LOCAL_ADMIN_PASSWORD_HASH</code> for a local sign-in.
  378. </p>
  379. </li>
  380. <li>
  381. <p class="font-semibold">3. Generate the local-admin password hash (only if you use the local fallback).</p>
  382. <pre class="mt-2 p-3 rounded bg-slate-900 text-slate-100 text-xs overflow-x-auto"><code>docker run --rm -it php:8.3-cli php -r \
  383. 'echo password_hash(readline("Password: "), PASSWORD_DEFAULT), PHP_EOL;'</code></pre>
  384. <p class="mt-2 text-slate-600 dark:text-slate-400">
  385. Paste the resulting <code>$2y$…</code> string into
  386. <code>.env</code> as <code>LOCAL_ADMIN_PASSWORD_HASH</code>
  387. — keep the single quotes around it.
  388. </p>
  389. </li>
  390. <li>
  391. <p class="font-semibold">4. Build and start the stack.</p>
  392. <pre class="mt-2 p-3 rounded bg-slate-900 text-slate-100 text-xs overflow-x-auto"><code>docker compose up -d --build</code></pre>
  393. <p class="mt-2 text-slate-600 dark:text-slate-400">
  394. The default port is <code>8080</code>; change
  395. <code>HTTP_PORT</code> in <code>.env</code> if you need
  396. something else.
  397. </p>
  398. </li>
  399. <li>
  400. <p class="font-semibold">5. Open the app and sign in.</p>
  401. <pre class="mt-2 p-3 rounded bg-slate-900 text-slate-100 text-xs overflow-x-auto"><code>http://localhost:8080</code></pre>
  402. <p class="mt-2 text-slate-600 dark:text-slate-400">
  403. The first person to sign in via local admin (or whoever
  404. you nominated through <code>BOOTSTRAP_ADMIN_*</code> on
  405. the Microsoft path) becomes the administrator.
  406. Subsequent admin promotions happen on the
  407. <strong>Users</strong> page.
  408. </p>
  409. </li>
  410. </ol>
  411. <p class="mt-5 text-xs text-slate-500 dark:text-slate-400">
  412. Backups, upgrades, reverse-proxy setup, Microsoft Entra
  413. registration: see <code>doc/admin-manual.md</code> in the
  414. repository.
  415. </p>
  416. </div>
  417. {# --------------- Source / license / version --------------- #}
  418. <div class="rounded-lg border bg-slate-50 p-6 dark:bg-slate-800/60 dark:border-slate-700 text-sm">
  419. <div class="grid gap-6 md:grid-cols-3">
  420. <div>
  421. <h3 class="font-semibold uppercase tracking-wider text-xs text-slate-500 dark:text-slate-400">Source</h3>
  422. <a href="https://git.chiapparini.org/chiappa/sprint_planer_web"
  423. class="mt-1 inline-block text-blue-700 hover:underline dark:text-blue-400 break-all"
  424. rel="noopener noreferrer">
  425. git.chiapparini.org/chiappa/sprint_planer_web
  426. </a>
  427. </div>
  428. <div>
  429. <h3 class="font-semibold uppercase tracking-wider text-xs text-slate-500 dark:text-slate-400">License</h3>
  430. <p class="mt-1">
  431. <a href="https://www.apache.org/licenses/LICENSE-2.0"
  432. class="text-blue-700 hover:underline dark:text-blue-400"
  433. rel="noopener noreferrer">Apache License 2.0</a><br>
  434. <span class="text-slate-600 dark:text-slate-400">© 2026 {{ appCreator }}</span>
  435. </p>
  436. </div>
  437. <div>
  438. <h3 class="font-semibold uppercase tracking-wider text-xs text-slate-500 dark:text-slate-400">Version</h3>
  439. <p class="mt-1 font-mono">{{ appVersion }}</p>
  440. </div>
  441. </div>
  442. </div>
  443. {# --------------- Modal overlay --------------- #}
  444. <div x-show="isOpen"
  445. x-on:keydown.escape.window="close()"
  446. x-cloak
  447. role="dialog"
  448. aria-modal="true"
  449. aria-label="Enlarged screenshot"
  450. class="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-8 bg-slate-900/85 backdrop-blur-sm">
  451. {# Click-to-dismiss backdrop. Sits behind the image / close button so
  452. clicks on either don't propagate. #}
  453. <button type="button"
  454. x-on:click="close()"
  455. aria-label="Close enlarged screenshot"
  456. tabindex="-1"
  457. class="absolute inset-0 w-full h-full cursor-default focus:outline-none"></button>
  458. {# Prominent close button — top-right, white pill. #}
  459. <button type="button"
  460. x-on:click="close()"
  461. aria-label="Close"
  462. class="absolute top-4 right-4 z-10 inline-flex items-center gap-2 rounded-full bg-white px-4 py-2 text-sm font-semibold text-slate-900 shadow-lg hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-white">
  463. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="18" height="18" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
  464. <line x1="5" y1="5" x2="15" y2="15"></line>
  465. <line x1="15" y1="5" x2="5" y2="15"></line>
  466. </svg>
  467. <span>Close</span>
  468. </button>
  469. {# The enlarged image itself — `pointer-events-none` would block our
  470. backdrop button below from the image's footprint, so we leave it
  471. interactive but it has no click handler — clicking it does nothing. #}
  472. <img x-bind:src="src"
  473. x-bind:alt="alt"
  474. class="relative max-w-[95vw] max-h-[90vh] object-contain rounded shadow-2xl">
  475. </div>
  476. </section>
  477. {% endblock %}