implemented 08-web-ui.md: added Thymeleaf-based web UI: templates for builds, artifacts, current builds, and fragments; static assets for favicon, CSS, and JavaScript
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
<!doctype html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head th:replace="~{fragments :: head(${pageTitle})}"></head>
|
||||
<body>
|
||||
<main>
|
||||
<h1 th:replace="~{fragments :: header(${pageTitle})}"></h1>
|
||||
<div th:replace="~{fragments :: nav(${view})}"></div>
|
||||
<article class="panel">
|
||||
<h2>Build</h2>
|
||||
<ul th:if="${result != null}" class="build-facts">
|
||||
<li>
|
||||
<span th:class="'status status-' + ${result.status}" th:text="${result.status}">success</span>
|
||||
</li>
|
||||
<li>Branch:
|
||||
<span class="branch link-tools">
|
||||
<a th:if="${result.branchUrl != null}" th:href="${result.branchUrl}" target="_blank"
|
||||
rel="noopener noreferrer" th:text="${result.branch}">main</a>
|
||||
<span th:if="${result.branchUrl == null}" th:text="${result.branch}">main</span>
|
||||
<button class="copy-button" type="button" th:attr="data-copy=${result.branch}"
|
||||
title="Copy branch name" aria-label="Copy branch name">⧉</button>
|
||||
</span>
|
||||
</li>
|
||||
<li>Commit:
|
||||
<span class="link-tools">
|
||||
<code>
|
||||
<a th:if="${result.commitUrl != null}" th:href="${result.commitUrl}" target="_blank"
|
||||
rel="noopener noreferrer" th:text="${result.commitAbbrev}">0123abc</a>
|
||||
<span th:if="${result.commitUrl == null}" th:text="${result.commitAbbrev}">0123abc</span>
|
||||
</code>
|
||||
<button class="copy-button" type="button" th:attr="data-copy=${result.commit}"
|
||||
title="Copy full commit ID" aria-label="Copy full commit ID">⧉</button>
|
||||
</span>
|
||||
</li>
|
||||
<li>Started: <span th:text="${result.startedAt}">2026-07-07 12:00</span>
|
||||
<th:block th:if="${result.duration != ''}">— Duration: <span th:text="${result.duration}">1:23</span></th:block>
|
||||
</li>
|
||||
<li th:if="${buildCommand != null}">Build command (as currently configured):<br>
|
||||
<code th:text="${buildCommand}">./gradlew test</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p th:if="${result == null}" class="muted">
|
||||
This build has no stored result anymore — only its artifact files remain.
|
||||
</p>
|
||||
|
||||
<h2>Logs</h2>
|
||||
<ul th:if="${!#lists.isEmpty(logs)}">
|
||||
<li th:each="log : ${logs}">
|
||||
<a th:href="'/artifacts/' + ${artifactKey} + '/' + ${log}" th:text="${log}">build.log</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p th:if="${#lists.isEmpty(logs)}" class="muted">
|
||||
No log files are stored for this build<th:block th:unless="${hasArtifacts}">
|
||||
— the build has not finished yet, or its artifacts were pruned</th:block>.
|
||||
</p>
|
||||
|
||||
<h2>Build Artifacts</h2>
|
||||
<ul th:if="${!#lists.isEmpty(reportIndexes)}">
|
||||
<li th:each="report : ${reportIndexes}">
|
||||
<a th:href="'/artifacts/' + ${artifactKey} + '/reports/' + ${report}"
|
||||
th:text="'reports/' + ${report}">reports/tests/index.html</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p th:if="${#lists.isEmpty(reportIndexes)}" class="muted">
|
||||
No artifact directories were produced by this build.
|
||||
</p>
|
||||
</article>
|
||||
</main>
|
||||
<footer th:replace="~{fragments :: footer}"></footer>
|
||||
<script src="/gittally.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,76 @@
|
||||
<!doctype html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head th:replace="~{fragments :: head(${pageTitle})}"></head>
|
||||
<body>
|
||||
<main>
|
||||
<h1 th:replace="~{fragments :: header(${pageTitle})}"></h1>
|
||||
<div th:replace="~{fragments :: nav(${view})}"></div>
|
||||
<div class="table-wrap">
|
||||
<table id="builds-table"
|
||||
th:attr="data-api=${apiPath},data-allow-restart=${allowRestart},data-empty-message=${emptyMessage}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<th>Branch</th>
|
||||
<th>Commit</th>
|
||||
<th>Started</th>
|
||||
<th>Duration</th>
|
||||
<th>Artifacts</th>
|
||||
<th class="actions-column">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="build-rows">
|
||||
<tr th:if="${#lists.isEmpty(rows)}">
|
||||
<td class="empty" colspan="7" th:text="${emptyMessage}">No builds recorded yet.</td>
|
||||
</tr>
|
||||
<tr th:each="row : ${rows}"
|
||||
th:attr="data-artifact-key=${row.artifactKey},data-branch=${row.branch},data-started-at=${row.startedAtIso},data-status=${row.status}">
|
||||
<td data-label="Status">
|
||||
<span th:class="'status status-' + ${row.status}" th:text="${row.status}">success</span>
|
||||
</td>
|
||||
<td class="branch" data-label="Branch">
|
||||
<span class="link-tools">
|
||||
<a th:if="${row.branchUrl != null}" th:href="${row.branchUrl}" target="_blank"
|
||||
rel="noopener noreferrer" th:text="${row.branch}">main</a>
|
||||
<span th:if="${row.branchUrl == null}" th:text="${row.branch}">main</span>
|
||||
<button class="copy-button" type="button" th:attr="data-copy=${row.branch}"
|
||||
title="Copy branch name" aria-label="Copy branch name">⧉</button>
|
||||
</span>
|
||||
</td>
|
||||
<td data-label="Commit">
|
||||
<span class="link-tools">
|
||||
<code>
|
||||
<a th:if="${row.commitUrl != null}" th:href="${row.commitUrl}" target="_blank"
|
||||
rel="noopener noreferrer" th:text="${row.commitAbbrev}">0123abc</a>
|
||||
<span th:if="${row.commitUrl == null}" th:text="${row.commitAbbrev}">0123abc</span>
|
||||
</code>
|
||||
<button class="copy-button" type="button" th:attr="data-copy=${row.commit}"
|
||||
title="Copy full commit ID" aria-label="Copy full commit ID">⧉</button>
|
||||
</span>
|
||||
</td>
|
||||
<td data-label="Started" th:text="${row.startedAt}">2026-07-07 12:00</td>
|
||||
<td class="duration-cell" data-label="Duration" th:text="${row.duration}">1:23</td>
|
||||
<td data-label="Artifacts">
|
||||
<a th:if="${row.artifactKey != ''}" class="artifact-link"
|
||||
th:href="'/builds/' + ${row.artifactKey}" title="Open artifacts">📄</a>
|
||||
<span th:if="${row.artifactKey == ''}">n/a</span>
|
||||
</td>
|
||||
<td class="actions-cell">
|
||||
<div class="actions">
|
||||
<button th:if="${allowRestart}" class="action-button" type="button" data-action="restart"
|
||||
th:attr="data-branch=${row.branch}" title="Restart build"
|
||||
aria-label="Restart build">↻</button>
|
||||
<button th:if="${row.artifactKey != ''}" class="action-button delete-button" type="button"
|
||||
data-action="delete" th:attr="data-artifact-key=${row.artifactKey}"
|
||||
title="Delete stored build" aria-label="Delete stored build">×</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
<footer th:replace="~{fragments :: footer}"></footer>
|
||||
<script src="/gittally.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!doctype html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head th:replace="~{fragments :: head(${pageTitle})}"></head>
|
||||
<body>
|
||||
<main>
|
||||
<h1 th:replace="~{fragments :: header(${pageTitle})}"></h1>
|
||||
<div th:replace="~{fragments :: nav(${view})}"></div>
|
||||
<div id="current-builds" data-api="/api/builds/current">
|
||||
<p id="no-current" class="empty-panel" th:style="${#lists.isEmpty(currentBuilds)} ? '' : 'display: none'">
|
||||
No build is currently running.
|
||||
</p>
|
||||
<section class="build-card" th:each="build : ${currentBuilds}"
|
||||
th:attr="data-artifact-key=${build.artifactKey},data-started-at=${build.startedAtIso},data-status=${build.status}">
|
||||
<header class="build-card-header">
|
||||
<span th:class="'status status-' + ${build.status}" th:text="${build.status}">running</span>
|
||||
<span class="branch link-tools">
|
||||
<a th:if="${build.branchUrl != null}" th:href="${build.branchUrl}" target="_blank"
|
||||
rel="noopener noreferrer" th:text="${build.branch}">main</a>
|
||||
<span th:if="${build.branchUrl == null}" th:text="${build.branch}">main</span>
|
||||
</span>
|
||||
<code>
|
||||
<a th:if="${build.commitUrl != null}" th:href="${build.commitUrl}" target="_blank"
|
||||
rel="noopener noreferrer" th:text="${build.commitAbbrev}">0123abc</a>
|
||||
<span th:if="${build.commitUrl == null}" th:text="${build.commitAbbrev}">0123abc</span>
|
||||
</code>
|
||||
<span class="muted" th:text="'started ' + ${build.startedAt}">started 2026-07-07 12:00</span>
|
||||
<span class="duration-cell running-duration"></span>
|
||||
<span class="build-card-actions">
|
||||
<button class="action-button cancel-button" type="button" data-action="cancel"
|
||||
th:attr="data-artifact-key=${build.artifactKey}" title="Cancel build">× Cancel</button>
|
||||
</span>
|
||||
</header>
|
||||
<pre class="live-log" th:attr="aria-label='live log of ' + ${build.branch}"></pre>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<footer th:replace="~{fragments :: footer}"></footer>
|
||||
<script src="/gittally.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!doctype html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head th:fragment="head(title)">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title th:text="${#strings.isEmpty(repoName)} ? ${title} + ' — GitTally' : ${title} + ' — ' + ${repoName} + ' — GitTally'">GitTally</title>
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="/gittally.css">
|
||||
<meta name="gittally-control-token" th:content="${controlToken}">
|
||||
<meta name="gittally-gitea-repo-url" th:content="${giteaRepoUrl}">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1 th:fragment="header(title)">
|
||||
<a class="title-home" href="/" aria-label="Open latest builds"><img src="/favicon.svg" alt=""></a>
|
||||
<span th:text="${title}">Latest Builds</span>
|
||||
<span class="repo-name" th:unless="${#strings.isEmpty(repoName)}" th:text="${repoName}">owner/repo</span>
|
||||
</h1>
|
||||
|
||||
<div th:fragment="nav(view)" class="view-row">
|
||||
<nav class="view-toggle">
|
||||
<span th:if="${view == 'latest'}">Latest</span>
|
||||
<a th:unless="${view == 'latest'}" href="/">Latest</a>
|
||||
<span th:if="${view == 'history'}">History</span>
|
||||
<a th:unless="${view == 'history'}" href="/history">History</a>
|
||||
<span th:if="${view == 'current'}">Current</span>
|
||||
<a th:unless="${view == 'current'}" href="/current">Current</a>
|
||||
</nav>
|
||||
<span class="view-row-actions">
|
||||
<span id="live-indicator" class="status status-unknown" title="live-update state">static</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<footer th:fragment="footer" class="site-footer">
|
||||
<strong><em th:text="'GitTally v' + ${version}">GitTally</em></strong>
|
||||
— © <a href="https://michael.hoennig.de" target="_blank" rel="noopener noreferrer">Michael Hönnig</a>, 2026
|
||||
<th:block th:unless="${#strings.isEmpty(impressumUrl)}">
|
||||
— <a th:href="${impressumUrl}" target="_blank" rel="noopener noreferrer">Impressum (Legal Disclosure)</a>
|
||||
</th:block>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user