This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new a445421 Use Bootstrap throughout
a445421 is described below
commit a4454216869161824103fba6031bf3060aff9346
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Mar 14 16:59:32 2025 +0200
Use Bootstrap throughout
---
atr/blueprints/admin/templates/update-pmcs.html | 4 +-
atr/static/css/atr-small.css | 353 ------------------------
atr/static/css/atr.css | 44 +--
atr/static/css/bootstrap.custom.css | 4 +-
atr/templates/candidate-create.html | 9 -
atr/templates/candidate-review.html | 5 -
atr/templates/dev-send-email.html | 9 -
atr/templates/docs-verify.html | 44 +--
atr/templates/error.html | 50 +---
atr/templates/keys-add.html | 136 +++------
atr/templates/keys-review.html | 182 ++----------
atr/templates/layouts/base.html | 6 +-
atr/templates/notfound.html | 30 +-
atr/templates/package-add.html | 269 +++++++-----------
atr/templates/package-check.html | 294 +++-----------------
atr/templates/project-directory.html | 143 ++++------
atr/templates/project-view.html | 261 ++++++------------
atr/templates/release-bulk.html | 315 +++++++--------------
atr/templates/release-vote.html | 250 +++++------------
atr/templates/vote-policy-add.html | 9 -
atr/templates/vote-policy-edit.html | 9 -
bootstrap/custom.scss | 4 +-
22 files changed, 562 insertions(+), 1868 deletions(-)
diff --git a/atr/blueprints/admin/templates/update-pmcs.html
b/atr/blueprints/admin/templates/update-pmcs.html
index 16714ed..3041b65 100644
--- a/atr/blueprints/admin/templates/update-pmcs.html
+++ b/atr/blueprints/admin/templates/update-pmcs.html
@@ -98,7 +98,7 @@
const submitForm = async () => {
const button = document.getElementById("submitButton");
button.disabled = true;
- document.body.style.cursor = 'wait'
+ document.body.style.cursor = "wait";
const statusElement = document.getElementById("status");
while (statusElement.firstChild) {
@@ -121,7 +121,7 @@
addStatusMessage(statusElement, error, "error")
} finally {
button.disabled = false;
- document.body.style.cursor = 'default'
+ document.body.style.cursor = "default";
}
};
diff --git a/atr/static/css/atr-small.css b/atr/static/css/atr-small.css
deleted file mode 100644
index 27d70a8..0000000
--- a/atr/static/css/atr-small.css
+++ /dev/null
@@ -1,353 +0,0 @@
-@font-face {
- font-family: "Jost";
- src: url("../webfonts/jost-v.woff2") format("woff2");
- font-weight: 100 900;
- font-style: normal;
-}
-
-@font-face {
- font-family: "Inter";
- src: url("../webfonts/inter-v.woff2") format("woff2");
- font-weight: 100 900;
- font-style: normal;
-}
-
-* {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
-}
-
-body {
- font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe
UI", "Oxygen", "Ubuntu", "Cantarell", "Open Sans", "Helvetica Neue", sans-serif;
- -webkit-font-smoothing: antialiased;
- font-size: 17px;
- line-height: 24px;
- font-variation-settings: "opsz" 22;
- font-weight: 425;
-}
-
-input, textarea, select, option {
- border-width: 2px !important;
- border-color: #cccccc !important;
- font-size: 17px !important;
- font-weight: 425 !important;
-}
-
-input, textarea, button, select, option {
- font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe
UI", "Oxygen", "Ubuntu", "Cantarell", "Open Sans", "Helvetica Neue", sans-serif;
- font-size: 17px;
- line-height: 24px;
- font-variation-settings: "opsz" 22;
- font-weight: 425;
-}
-
-select, input[type="file"] {
- padding: 6px 12px;
-}
-
-a {
- font-weight: 450;
-}
-
-h1, h2, h3 {
- font-weight: 475;
- font-family: "Jost", system-ui, -apple-system, BlinkMacSystemFont, "Segoe
UI", "Oxygen", "Ubuntu", "Cantarell", "Open Sans", "Helvetica Neue", sans-serif;
-}
-
-h1 {
- margin-bottom: 2rem;
-}
-
-h2 {
- border-bottom: 1px solid #d1d2d3;
- padding-bottom: 0.5rem;
- margin-top: 2.5rem;
- margin-bottom: 1.5rem;
-}
-
-h3, p, ul, form {
- margin-bottom: 1rem;
-}
-
-ul {
- padding-left: 1rem;
-}
-
-table {
- border-collapse: collapse;
-}
-
-table th {
- text-align: left;
-}
-
-table td {
- /* Not sure if we should keep it this way, but it seems pretty good */
- font-family: ui-monospace, "SFMono-Regular", "Menlo", "Monaco",
"Consolas", monospace;
- word-break: break-all;
- font-size: 0.9em;
-}
-
-table tr {
- /* This doesn't always work; not clear why */
- border-bottom: 1px solid #c1c2c3;
-}
-
-table tr:last-child {
- border-bottom: none;
-}
-
-aside h1 {
- font-size: 3rem;
- line-height: 1.1;
- text-align: center;
-}
-
-aside h1 span.rest {
- color: #777777;
-}
-
-aside h3 {
- margin-top: 1.5rem;
-}
-
-pre {
- white-space: pre-wrap;
-
- /* word-wrap: anywhere; */
- word-break: break-all;
-}
-
-footer {
- padding: 1rem;
- background: #eeeeee;
- font-size: 15px;
- margin: 2rem;
- color: #333333;
- font-variation-settings: "opsz" 14;
- border-radius: 0.5rem;
- border: 2px solid #d1d2d3;
- display: table;
- text-align: center;
-}
-
-footer a {
- color: #333333;
- font-weight: 425;
-}
-
-footer a:visited {
- color: #333333;
- font-weight: 425;
-}
-
-footer p {
- margin-bottom: 0;
-}
-
-input,
-textarea {
- font-family: monospace;
- padding: 0.5rem;
-}
-
-textarea {
- width: 100%;
- min-height: 200px;
-}
-
-form.striking {
- background-color: #ffffee;
- border: 2px solid #ddddbb;
- padding: 1rem;
- border-radius: 0.5rem;
-}
-
-.site-title {
- text-decoration: none;
- color: inherit;
-}
-
-span.warning {
- color: #cc0000;
- font-weight: 550;
-}
-
-.wrapper {
- min-height: 100vh;
- display: flex;
- flex-direction: column;
-}
-
-.back-link {
- display: inline-block;
- margin-bottom: 1rem;
- color: #0000ee;
- text-decoration: none;
-}
-
-.back-link:hover {
- text-decoration: underline;
-}
-
-.ribbon {
- height: 8px;
- background: linear-gradient(90deg, #282661 0%, #662f8f 20%, #9e2165 40%,
#cb2138 60%, #ea7826 80%, #f7ae18 100%);
-}
-
-.content {
- flex: 1;
- display: flex;
-}
-
-.main-container {
- flex: 1;
- display: flex;
- flex-direction: column;
-}
-
-.main-content {
- flex: 1;
- padding: 2rem;
-}
-
-.sidebar {
- width: 250px;
- background-color: #f6f7f8;
- border-right: 1px solid #d1d2d3;
- padding: 1rem;
-}
-
-.sidebar .user-section {
- margin-bottom: 1.5rem;
- text-align: center;
- border-top: 1px solid #d1d2d3;
- border-bottom: 1px solid #d1d2d3;
- padding: 1.5rem 0;
-}
-
-.sidebar hr {
- border: none;
- border-top: 1px solid #999999;
- margin: 1.5rem auto;
- width: 62%;
- height: 0;
-}
-
-.sidebar nav {
- margin-top: 1.5rem;
-}
-
-.sidebar nav ul {
- list-style: none;
-}
-
-.sidebar nav li {
- margin-bottom: 1rem;
-}
-
-.hamburger {
- display: none;
- background: none;
- border: none;
- cursor: pointer;
- padding: 0;
- z-index: 100;
-}
-
-.hamburger span {
- display: block;
- width: 25px;
- height: 3px;
- background-color: #333333;
- margin: 5px 0;
- transition: 0.3s;
-}
-
-.nav-toggle {
- display: none;
-}
-
-@media (width <= 768px) {
- .hamburger {
- display: block;
- position: fixed;
- top: 20px;
- padding-left: 2rem;
- transition: 0.3s;
- }
-
- .sidebar {
- position: fixed;
- left: -250px;
- top: 20px;
- bottom: 0;
- transition: 0.3s;
- z-index: 99;
- }
-
- /* Show sidebar when checkbox is checked */
- .nav-toggle:checked ~ .sidebar {
- left: 0;
- }
-
- /* Move hamburger with sidebar */
- .nav-toggle:checked ~ .hamburger {
- padding-left: calc(250px + 2rem); /* sidebar width + padding */
- }
-
- .nav-toggle:checked ~ .hamburger span:nth-child(1) {
- transform: rotate(45deg) translate(5px, 5px);
- }
-
- .nav-toggle:checked ~ .hamburger span:nth-child(2) {
- opacity: 0%;
- }
-
- .nav-toggle:checked ~ .hamburger span:nth-child(3) {
- transform: rotate(-45deg) translate(7px, -7px);
- }
-
- .main-content {
- margin-left: 0;
- padding-top: 4rem;
- }
-}
-
-/* Flash Messages */
-.flash-message {
- padding: 1rem;
- margin-bottom: 1rem;
- border-radius: 4px;
-}
-
-.flash-warning {
- background-color: #fff3cd;
- border: 1px solid #ffeeba;
- color: #856404;
-}
-
-.flash-error {
- background-color: #f8d7da;
- border: 1px solid #f5c6cb;
- color: #721c24;
-}
-
-.flash-success {
- background-color: #d4edda;
- border: 1px solid #c3e6cb;
- color: #155724;
-}
-
-.admin-content {
- box-shadow: inset 0 0 0 10px #dc3545;
-}
-
-.warning-banner {
- border: 2px solid #dc3545;
- background: #ffcccc;
- padding: 1rem;
- margin: 0 0 2rem;
- border-radius: 4px;
-}
diff --git a/atr/static/css/atr.css b/atr/static/css/atr.css
index a726a53..8615229 100644
--- a/atr/static/css/atr.css
+++ b/atr/static/css/atr.css
@@ -27,6 +27,13 @@ body {
font-weight: 425;
}
+input, textarea, select, option {
+ border-width: 2px !important;
+ border-color: #cccccc !important;
+ font-size: 17px !important;
+ font-weight: 425 !important;
+}
+
input, textarea, button, select, option {
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe
UI", "Oxygen", "Ubuntu", "Cantarell", "Open Sans", "Helvetica Neue", sans-serif;
font-size: 17px;
@@ -35,6 +42,12 @@ input, textarea, button, select, option {
font-weight: 425;
}
+label[for] {
+ font-weight: 450;
+ border-bottom: 1px dashed #d1d2d3;
+ cursor: pointer;
+}
+
select, input[type="file"] {
padding: 6px 12px;
}
@@ -67,31 +80,15 @@ ul {
padding-left: 1rem;
}
-label {
- font-weight: 500;
- border-bottom: 1px dashed #b1b2b3;
- padding-bottom: 0.5rem;
- cursor: pointer;
-}
-
table {
- width: 100%;
border-collapse: collapse;
}
table th {
text-align: left;
- padding: 0.5rem;
- font-weight: 500;
- width: 30%;
- color: #333333;
}
table td {
- padding: 0.5rem;
-
- /* Almost all of our tables have data in them */
-
/* Not sure if we should keep it this way, but it seems pretty good */
font-family: ui-monospace, "SFMono-Regular", "Menlo", "Monaco",
"Consolas", monospace;
word-break: break-all;
@@ -155,21 +152,6 @@ footer p {
margin-bottom: 0;
}
-button {
- margin-top: 1rem;
- padding: 0.5rem 1rem;
- background: #004477;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- font-weight: 500;
-}
-
-button:hover {
- background: #003366;
-}
-
input,
textarea {
font-family: monospace;
diff --git a/atr/static/css/bootstrap.custom.css
b/atr/static/css/bootstrap.custom.css
index e36516d..295092b 100644
--- a/atr/static/css/bootstrap.custom.css
+++ b/atr/static/css/bootstrap.custom.css
@@ -6135,7 +6135,7 @@ textarea.form-control-lg {
--bs-table-color-state: initial;
--bs-table-bg-state: initial;
--bs-table-color: var(--bs-emphasis-color);
- --bs-table-bg: var(--bs-body-bg);
+ --bs-table-bg: transparent;
--bs-table-border-color: var(--bs-border-color);
--bs-table-accent-bg: transparent;
--bs-table-striped-color: var(--bs-emphasis-color);
@@ -11445,7 +11445,7 @@ textarea.form-control-lg {
}
}
th {
- color: #555;
+ color: #212529;
font-weight: 525;
}
diff --git a/atr/templates/candidate-create.html
b/atr/templates/candidate-create.html
index 8b4e490..1861070 100644
--- a/atr/templates/candidate-create.html
+++ b/atr/templates/candidate-create.html
@@ -8,15 +8,6 @@
Create a new release candidate.
{% endblock description %}
-{% block theme_css %}
- <link rel="stylesheet"
- href="{{ url_for('static', filename='css/atr-small.css') }}" />
-{% endblock theme_css %}
-
-{% block stylesheets %}
- {{ super() }}
-{% endblock stylesheets %}
-
{% block content %}
<h1>Create release candidate</h1>
<p>
diff --git a/atr/templates/candidate-review.html
b/atr/templates/candidate-review.html
index f0a094c..fe08b65 100644
--- a/atr/templates/candidate-review.html
+++ b/atr/templates/candidate-review.html
@@ -8,11 +8,6 @@
Release candidates to which you have access.
{% endblock description %}
-{% block theme_css %}
- <link rel="stylesheet"
- href="{{ url_for('static', filename='css/atr-small.css') }}" />
-{% endblock theme_css %}
-
{% block stylesheets %}
{{ super() }}
<style>
diff --git a/atr/templates/dev-send-email.html
b/atr/templates/dev-send-email.html
index 886d25a..1799724 100644
--- a/atr/templates/dev-send-email.html
+++ b/atr/templates/dev-send-email.html
@@ -8,15 +8,6 @@
Test email sending functionality.
{% endblock description %}
-{% block theme_css %}
- <link rel="stylesheet"
- href="{{ url_for('static', filename='css/atr-small.css') }}" />
-{% endblock theme_css %}
-
-{% block stylesheets %}
- {{ super() }}
-{% endblock stylesheets %}
-
{% block content %}
<h1>Test email sending</h1>
<p>
diff --git a/atr/templates/docs-verify.html b/atr/templates/docs-verify.html
index e51bf9f..d95cb0b 100644
--- a/atr/templates/docs-verify.html
+++ b/atr/templates/docs-verify.html
@@ -8,30 +8,6 @@
Instructions for verifying an artifact.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .verification-steps {
- margin-top: 1rem;
- }
-
- .verification-steps h3 {
- margin-top: 1.5rem;
- margin-bottom: 0.5rem;
- }
-
- .verification-steps pre {
- background-color: #f5f5f5;
- padding: 0.8rem;
- border-radius: 4px;
- overflow-x: auto;
- font-family: monospace;
- margin: 0.5rem 0;
- position: relative;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
<p>
<a href="{{ url_for('root_candidate_review') }}" class="back-link">← Back
to release candidates</a>
@@ -42,28 +18,28 @@
Follow these steps to verify the downloaded artifact <code>{{ filename
}}</code>.
</p>
- <div class="verification-steps">
- <h3>1. Download the artifact and signature</h3>
+ <div class="mt-3">
+ <h3 class="mt-4 mb-2">1. Download the artifact and signature</h3>
<p>Use the download buttons on the release candidate page to download both
the artifact and its signature.</p>
- <h3>2. Verify signature with GPG</h3>
+ <h3 class="mt-4 mb-2">2. Verify signature with GPG</h3>
<p>Run the following command to verify the signature:</p>
- <pre>{% if has_signature %}gpg --verify {{ filename }}.asc {{ filename
}}{% else %}No signature available for this artifact.{% endif %}</pre>
+ <pre class="bg-light p-3 rounded overflow-auto my-2">{% if has_signature
%}gpg --verify {{ filename }}.asc {{ filename }}{% else %}No signature
available for this artifact.{% endif %}</pre>
- <h3>3. Verify SHA3-256 hash</h3>
+ <h3 class="mt-4 mb-2">3. Verify SHA3-256 hash</h3>
<p>Run the following command and compare the output with the hash
displayed on the release candidate page:</p>
- <pre>sha3sum -a 256 {{ filename }}</pre>
+ <pre class="bg-light p-3 rounded overflow-auto my-2">sha3sum -a 256 {{
filename }}</pre>
{% if artifact_sha3 %}
<p>Expected SHA3-256 hash:</p>
- <pre>{{ artifact_sha3 }}</pre>
+ <pre class="bg-light p-3 rounded overflow-auto my-2">{{ artifact_sha3
}}</pre>
{% endif %}
- <h3>4. Verify SHA-512 hash</h3>
+ <h3 class="mt-4 mb-2">4. Verify SHA-512 hash</h3>
<p>Run the following command and compare the output with the hash
displayed on the release candidate page:</p>
- <pre>sha512sum {{ filename }}</pre>
+ <pre class="bg-light p-3 rounded overflow-auto my-2">sha512sum {{ filename
}}</pre>
{% if sha512 %}
<p>Expected SHA-512 hash:</p>
- <pre>{{ sha512 }}</pre>
+ <pre class="bg-light p-3 rounded overflow-auto my-2">{{ sha512 }}</pre>
{% endif %}
</div>
{% endblock content %}
diff --git a/atr/templates/error.html b/atr/templates/error.html
index 460d902..763881a 100644
--- a/atr/templates/error.html
+++ b/atr/templates/error.html
@@ -8,49 +8,11 @@
An error occurred while processing your request.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .error-container {
- margin: 20px 0;
- padding: 15px;
- border: 1px solid #dc3545;
- border-radius: 4px;
- background-color: #f8d7da;
- }
-
- .error-title {
- color: #721c24;
- margin-top: 0;
- }
-
- .error-details {
- margin-top: 20px;
- }
-
- .error-traceback {
- background-color: #f5f5f5;
- padding: 15px;
- overflow-x: auto;
- font-family: monospace;
- font-size: 13px;
- line-height: 1.5;
- color: #333;
- border: 1px solid #ccc;
- border-radius: 4px;
- }
-
- .actions {
- margin-top: 20px;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
<div>
<h1>Error</h1>
- <div class="error-container">
- <h2 class="error-title">{{ error }}</h2>
+ <div class="my-4 p-3 border border-danger rounded bg-danger-subtle">
+ <h2 class="text-danger-emphasis mt-0">{{ error }}</h2>
<p>An error occurred while processing your request. This has been logged
and will be addressed.</p>
{% if status_code %}
@@ -60,15 +22,15 @@
{% endif %}
{% if traceback %}
- <div class="error-details">
+ <div class="mt-4">
<h3>Technical Details</h3>
- <pre class="error-traceback">{{ traceback }}</pre>
+ <pre class="bg-light p-3 overflow-auto font-monospace border
rounded">{{ traceback }}</pre>
</div>
{% endif %}
</div>
- <div class="actions">
- <a href="{{ url_for('root') }}" class="button">Return to Home</a>
+ <div class="mt-4">
+ <a href="{{ url_for('root') }}" class="btn btn-primary">Return to
Home</a>
</div>
</div>
{% endblock content %}
diff --git a/atr/templates/keys-add.html b/atr/templates/keys-add.html
index 543c501..e2f2495 100644
--- a/atr/templates/keys-add.html
+++ b/atr/templates/keys-add.html
@@ -8,81 +8,6 @@
Add a public signing key to your account.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .form-group {
- margin-bottom: 1rem;
- }
-
- .form-group label {
- display: inline-block;
- margin-bottom: 1rem;
- padding: 0;
- }
-
- .error-message {
- color: #dc3545;
- margin-top: 0.25rem;
- }
-
- .key-info {
- margin-top: 1rem;
- padding: 1rem;
- background: #f8f9fa;
- border-radius: 4px;
- }
-
- .key-info h3 {
- margin-top: 0;
- }
-
- .key-info dl {
- margin: 0;
- display: grid;
- grid-template-columns: auto 1fr;
- gap: 0.5rem 1rem;
- }
-
- .key-info dt {
- font-weight: bold;
- }
-
- .key-info dd {
- margin: 0;
- }
-
- .navigation {
- margin-top: 2rem;
- }
-
- .pmc-checkboxes {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
- gap: 0.5rem;
- margin-top: 0.5rem;
- margin-bottom: 0.5rem;
- }
-
- .checkbox-item {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- }
-
- .checkbox-item label {
- border-bottom: none;
- margin-bottom: 0;
- font-weight: normal;
- }
-
- .checkbox-item input[type="checkbox"] {
- width: 1rem;
- height: 1rem;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
<h1>Add signing key</h1>
<p class="intro">Add your public key to use for signing release
artifacts.</p>
@@ -95,72 +20,79 @@
{% if key_info %}
<h2>Key results</h2>
- <div class="key-info">
- <h3>Success: Added Key</h3>
- <dl>
- <dt>Key ID</dt>
- <dd>
+ <div class="mt-3 p-3 bg-light rounded">
+ <h3 class="mt-0">Success: Added Key</h3>
+ <dl class="row mb-0">
+ <dt class="col-sm-3 fw-bold">Key ID</dt>
+ <dd class="col-sm-9 mb-2">
{{ key_info.key_id }}
</dd>
- <dt>Fingerprint</dt>
- <dd>
+ <dt class="col-sm-3 fw-bold">Fingerprint</dt>
+ <dd class="col-sm-9 mb-2">
{{ key_info.fingerprint }}
</dd>
- <dt>User ID</dt>
- <dd>
+ <dt class="col-sm-3 fw-bold">User ID</dt>
+ <dd class="col-sm-9 mb-2">
{{ key_info.user_id }}
</dd>
- <dt>Created</dt>
- <dd>
+ <dt class="col-sm-3 fw-bold">Created</dt>
+ <dd class="col-sm-9 mb-2">
{{ key_info.creation_date }}
</dd>
- <dt>Expires</dt>
- <dd>
+ <dt class="col-sm-3 fw-bold">Expires</dt>
+ <dd class="col-sm-9 mb-2">
{{ key_info.expiration_date or 'Never' }}
</dd>
- <dt>Key Data</dt>
- <dd>
- <pre>{{ key_info.data }}</pre>
+ <dt class="col-sm-3 fw-bold">Key Data</dt>
+ <dd class="col-sm-9 mb-2">
+ <pre class="mb-0">{{ key_info.data }}</pre>
</dd>
</dl>
</div>
{% endif %}
<form method="post" class="striking">
- <div class="form-group">
- <label for="public_key">Public Key:</label>
+ <div class="mb-4">
+ <div class="mb-3">
+ <label for="public_key" class="form-label">Public Key:</label>
+ </div>
<textarea id="public_key"
name="public_key"
+ class="form-control mb-2"
+ rows="8"
required
placeholder="Paste your public key here (in ASCII-armored
format)"
aria-describedby="key-help"></textarea>
- <small id="key-help">
+ <small id="key-help" class="form-text text-muted">
Your public key should be in ASCII-armored format, starting with
"-----BEGIN PGP PUBLIC KEY BLOCK-----"
</small>
</div>
{% if user_pmcs %}
- <div class="form-group">
- <label>Associate with projects:</label>
- <div class="pmc-checkboxes">
+ <div class="mb-4">
+ <div class="mb-3">
+ <label class="form-label">Associate with projects:</label>
+ </div>
+ <div class="d-flex flex-wrap gap-3 mb-2">
{% for pmc in user_pmcs|sort(attribute='name') %}
- <div class="checkbox-item">
+ <div class="form-check d-flex align-items-center gap-2">
<input type="checkbox"
+ class="form-check-input"
id="pmc_{{ pmc.name }}"
name="selected_pmcs"
value="{{ pmc.name }}" />
- <label for="pmc_{{ pmc.name }}">{{ pmc.display_name }}</label>
+ <label class="form-check-label mb-0" for="pmc_{{ pmc.name }}">{{
pmc.display_name }}</label>
</div>
{% endfor %}
</div>
- <small>You must associate your key with at least one PMC of which you
are a member.</small>
+ <small class="form-text text-muted">You must associate your key with
at least one PMC of which you are a member.</small>
</div>
{% else %}
- <div class="error-message">
+ <div class="text-danger mt-1">
<p>You must be a member of at least one PMC to add a signing key.</p>
</div>
{% endif %}
- <button type="submit">Add Key</button>
+ <button type="submit" class="btn btn-primary">Add Key</button>
</form>
{% endblock content %}
diff --git a/atr/templates/keys-review.html b/atr/templates/keys-review.html
index 30e27a3..a7a5b7a 100644
--- a/atr/templates/keys-review.html
+++ b/atr/templates/keys-review.html
@@ -8,136 +8,6 @@
Review your signing keys.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .existing-keys {
- margin-bottom: 2rem;
- padding: 1rem 2rem 2rem 2rem;
- background: #f8f9fa;
- border-radius: 4px;
- }
-
- .keys-grid {
- display: grid;
- gap: 1.5rem;
- }
-
- .key-card {
- background: white;
- border: 1px solid #d1d2d3;
- border-radius: 4px;
- overflow: hidden;
- padding: 1rem;
- }
-
- .key-card table {
- margin: 0;
- }
-
- .key-card td {
- word-break: break-all;
- }
-
- .key-card h3 {
- margin-top: 0;
- margin-bottom: 1rem;
- }
-
- .delete-key-form {
- margin-top: 1rem;
- }
-
- .delete-button {
- background: #dc3545;
- color: white;
- border: none;
- padding: 0.5rem 1rem;
- border-radius: 4px;
- cursor: pointer;
- }
-
- .delete-button:hover {
- background: #c82333;
- }
-
- .navigation {
- margin-top: 2rem;
- }
-
- .success-message {
- color: #28a745;
- margin: 1rem 0;
- padding: 1rem;
- background: #d4edda;
- border-radius: 4px;
- }
-
- .key-details {
- margin-top: 1rem;
- padding: 1rem;
- background: #f8f9fa;
- border-radius: 4px;
- }
-
- .key-details summary {
- font-weight: bold;
- cursor: pointer;
- }
-
- .key-details pre {
- margin-top: 1rem;
- white-space: pre-wrap;
- }
-
- .status-message {
- margin-top: 2rem;
- padding: 1rem;
- background: #f8f9fa;
- border-radius: 4px;
- }
-
- .status-message.success {
- background: #d4edda;
- }
-
- .status-message.error {
- background: #f8d7da;
- }
-
- .expiry-warning {
- color: #e67e22;
- font-weight: bold;
- }
-
- .expiry-error {
- color: #e74c3c;
- font-weight: bold;
- }
-
- .notice-badge {
- display: inline-block;
- padding: 0.25rem 0.5rem;
- border-radius: 3px;
- font-size: 0.8em;
- margin-left: 0.5rem;
- font-weight: 500;
- }
-
- .notice-badge.warning {
- background-color: #fff3cd;
- color: #856404;
- border: 1px solid #ffeeba;
- }
-
- .notice-badge.error {
- background-color: #f8d7da;
- color: #721c24;
- border: 1px solid #f5c6cb;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
<h1>Your signing keys</h1>
<p class="intro">Review your public keys used for signing release
artifacts.</p>
@@ -149,38 +19,38 @@
</div>
{% if user_keys %}
- <div class="existing-keys">
- <div class="keys-grid">
+ <div class="mb-5 p-4 bg-light rounded">
+ <div class="d-grid gap-4">
{% for key in user_keys %}
- <div class="key-card">
- <table>
+ <div class="card p-3 border">
+ <table class="mb-0">
<tbody>
<tr>
- <th>Fingerprint</th>
- <td>{{ key.fingerprint }}</td>
+ <th class="p-2 text-dark">Fingerprint</th>
+ <td class="text-break">{{ key.fingerprint }}</td>
</tr>
<tr>
- <th>Key Type</th>
- <td>{{ algorithms[key.algorithm] }} ({{ key.length }}
bits)</td>
+ <th class="p-2 text-dark">Key Type</th>
+ <td class="text-break">{{ algorithms[key.algorithm] }} ({{
key.length }} bits)</td>
</tr>
<tr>
- <th>Created</th>
- <td>{{ key.created.strftime("%Y-%m-%d %H:%M:%S") }}</td>
+ <th class="p-2 text-dark">Created</th>
+ <td class="text-break">{{ key.created.strftime("%Y-%m-%d
%H:%M:%S") }}</td>
</tr>
<tr>
- <th>Expires</th>
- <td>
+ <th class="p-2 text-dark">Expires</th>
+ <td class="text-break">
{% if key.expires %}
{% set days_until_expiry = (key.expires - now).days %}
{% if days_until_expiry < 0 %}
- <span class="expiry-error">
+ <span class="text-danger fw-bold">
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
- <span class="notice-badge error">Expired</span>
+ <span class="badge bg-danger text-white
ms-2">Expired</span>
</span>
{% elif days_until_expiry <= 30 %}
- <span class="expiry-warning">
+ <span class="text-warning fw-bold">
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
- <span class="notice-badge warning">Expires in {{
days_until_expiry }} days</span>
+ <span class="badge bg-warning text-dark
ms-2">Expires in {{ days_until_expiry }} days</span>
</span>
{% else %}
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
@@ -191,12 +61,12 @@
</td>
</tr>
<tr>
- <th>User ID</th>
- <td>{{ key.declared_uid or 'Not specified' }}</td>
+ <th class="p-2 text-dark">User ID</th>
+ <td class="text-break">{{ key.declared_uid or 'Not
specified' }}</td>
</tr>
<tr>
- <th>Associated projects</th>
- <td>
+ <th class="p-2 text-dark">Associated projects</th>
+ <td class="text-break">
{% if key.pmcs %}
{{ key.pmcs|map(attribute='name') |join(', ') }}
{% else %}
@@ -208,16 +78,14 @@
</table>
<!-- TODO: We could link to a downloadable version of the key
instead -->
- <details class="key-details">
- <summary>View whole key</summary>
- <pre class="key-text">{{ key.ascii_armored_key }}</pre>
+ <details class="mt-3 p-3 bg-light rounded">
+ <summary class="fw-bold">View whole key</summary>
+ <pre class="mt-3">{{ key.ascii_armored_key }}</pre>
</details>
- <form method="post"
- action="{{ url_for('root_keys_delete') }}"
- class="delete-key-form">
+ <form method="post" action="{{ url_for('root_keys_delete') }}"
class="mt-3">
<input type="hidden" name="fingerprint" value="{{
key.fingerprint }}" />
- <button type="submit" class="delete-button">Delete key</button>
+ <button type="submit" class="btn btn-danger">Delete key</button>
</form>
</div>
{% endfor %}
diff --git a/atr/templates/layouts/base.html b/atr/templates/layouts/base.html
index bfcd2d5..20bbe9d 100644
--- a/atr/templates/layouts/base.html
+++ b/atr/templates/layouts/base.html
@@ -13,10 +13,8 @@
{% block stylesheets %}
<link rel="stylesheet"
href="{{ url_for('static', filename='css/normalize.css') }}" />
- {% block theme_css %}
- <link rel="stylesheet"
- href="{{ url_for('static', filename='css/atr.css') }}" />
- {% endblock theme_css %}
+ <link rel="stylesheet"
+ href="{{ url_for('static', filename='css/atr.css') }}" />
<link rel="stylesheet"
href="{{ url_for('static', filename='css/fontawesome.all.min.css')
}}" />
<link rel="stylesheet"
diff --git a/atr/templates/notfound.html b/atr/templates/notfound.html
index 86077f7..5b3f925 100644
--- a/atr/templates/notfound.html
+++ b/atr/templates/notfound.html
@@ -8,33 +8,11 @@
An error occurred while processing your request.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .error-container {
- margin: 20px 0;
- padding: 15px;
- border: 1px solid #dc3545;
- border-radius: 4px;
- background-color: #f8d7da;
- }
-
- .error-title {
- color: #721c24;
- margin-top: 0;
- }
-
- .actions {
- margin-top: 20px;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
<div>
<h1>Error</h1>
- <div class="error-container">
- <h2 class="error-title">{{ error }}</h2>
+ <div class="my-4 p-3 border border-danger rounded bg-danger-subtle">
+ <h2 class="text-danger-emphasis mt-0">{{ error }}</h2>
<p>
The requested URL was not found on the server. If you entered the URL
manually please check your spelling and try again.
</p>
@@ -46,8 +24,8 @@
{% endif %}
</div>
- <div class="actions">
- <a href="{{ url_for('root') }}" class="button">Return to Home</a>
+ <div class="mt-4">
+ <a href="{{ url_for('root') }}" class="btn btn-primary">Return to
Home</a>
</div>
</div>
{% endblock content %}
diff --git a/atr/templates/package-add.html b/atr/templates/package-add.html
index 655dfe3..1a76ce3 100644
--- a/atr/templates/package-add.html
+++ b/atr/templates/package-add.html
@@ -8,121 +8,6 @@
Add a package to an existing release.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .form-table {
- width: 100%;
- }
-
- .form-table th {
- width: 200px;
- text-align: right;
- padding-right: 1rem;
- vertical-align: top;
- font-weight: 500;
- }
-
- .form-table td {
- vertical-align: top;
- }
-
- .form-table label {
- border-bottom: none;
- padding-bottom: 0;
- }
-
- select,
- input[type="file"] {
- display: block;
- margin-bottom: 0.5rem;
- }
-
- .help-text {
- color: #666;
- font-size: 0.9em;
- display: block;
- margin-top: 0.25rem;
- }
-
- .error-message {
- color: #dc3545;
- margin-top: 0.25rem;
- }
-
- button {
- margin-top: 1rem;
- }
-
- button:disabled {
- opacity: 0.5;
- cursor: not-allowed;
- }
-
- .radio-group {
- margin-bottom: 0.5rem;
- }
-
- .radio-group div {
- margin: 0.5rem 0;
- }
-
- .radio-group label {
- margin-left: 0.5rem;
- cursor: pointer;
- }
-
- .radio-group input[type="radio"] {
- cursor: pointer;
- }
-
- .checkbox-group {
- margin-bottom: 0.5rem;
- }
-
- .checkbox-group div {
- margin: 0.5rem 0;
- }
-
- .checkbox-group label {
- margin-left: 0.5rem;
- cursor: pointer;
- }
-
- .checkbox-group input[type="checkbox"] {
- cursor: pointer;
- }
-
- .form-separator {
- margin: 2rem 0;
- border-top: 1px solid #ddd;
- text-align: center;
- position: relative;
- }
-
- .form-separator span {
- background: #fff;
- padding: 0 1rem;
- color: #666;
- position: relative;
- top: -0.75rem;
- }
-
- input[type="url"],
- input[type="number"] {
- width: 100%;
- max-width: 600px;
- padding: 0.375rem;
- border: 1px solid #ced4da;
- border-radius: 0.25rem;
- }
-
- input[type="number"] {
- width: 100px;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
<h1>Add package</h1>
<p class="intro">
@@ -132,14 +17,14 @@
<form method="post" enctype="multipart/form-data" class="striking">
<input type="hidden" name="form_type" value="single" />
- <table class="form-table">
+ <table class="table">
<tbody>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label for="release_key">Release:</label>
</th>
- <td>
- <select id="release_key" name="release_key" required>
+ <td class="align-top">
+ <select id="release_key" name="release_key" class="form-select
mb-2" required>
<option value="">Select a release...</option>
{% for release in releases %}
<option value="{{ release.storage_key }}"
@@ -148,107 +33,124 @@
</option>
{% endfor %}
</select>
- {% if not releases %}<p class="error-message">No releases found
that you can add a package to.</p>{% endif %}
+ {% if not releases %}<p class="text-danger mt-1">No releases found
that you can add a package to.</p>{% endif %}
</td>
</tr>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label for="release_artifact">Release candidate artifact:</label>
</th>
- <td>
+ <td class="align-top">
<input type="file"
id="release_artifact"
name="release_artifact"
+ class="form-control mb-2"
required
accept="application/gzip,application/x-gzip,application/x-tar,application/zip,application/java-archive,.tar.gz,.tgz,.zip,.jar"
aria-describedby="artifact-help" />
- <span id="artifact-help" class="help-text">Upload the release
candidate archive (tar.gz, zip, or jar)</span>
+ <span id="artifact-help" class="form-text text-muted">Upload the
release candidate archive (tar.gz, zip, or jar)</span>
</td>
</tr>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label>Artifact type:</label>
</th>
- <td>
- <div class="radio-group">
- <div>
+ <td class="align-top">
+ <div class="mb-2">
+ <div class="form-check my-2">
<input type="radio"
+ class="form-check-input"
id="source"
name="artifact_type"
value="source"
required
checked />
- <label for="source">Source archive</label>
+ <label class="form-check-label" for="source">Source
archive</label>
</div>
- <div>
- <input type="radio" id="binary" name="artifact_type"
value="binary" />
- <label for="binary">Binary archive</label>
+ <div class="form-check my-2">
+ <input type="radio"
+ class="form-check-input"
+ id="binary"
+ name="artifact_type"
+ value="binary" />
+ <label class="form-check-label" for="binary">Binary
archive</label>
</div>
- <div>
+ <div class="form-check my-2">
<input type="radio"
+ class="form-check-input"
id="reproducible"
name="artifact_type"
value="reproducible" />
- <label for="reproducible">Reproducible binary archive</label>
+ <label class="form-check-label"
for="reproducible">Reproducible binary archive</label>
</div>
</div>
</td>
</tr>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label for="release_checksum">SHA2-512 hash file:</label>
</th>
- <td>
+ <td class="align-top">
<input type="file"
id="release_checksum"
name="release_checksum"
+ class="form-control mb-2"
accept=".sha512,.sha,.txt"
aria-describedby="checksum-help" />
- <span id="checksum-help" class="help-text">Optional: Upload the
SHA-512 checksum file for verification</span>
+ <span id="checksum-help" class="form-text text-muted">Optional:
Upload the SHA-512 checksum file for verification</span>
</td>
</tr>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label for="release_signature">Detached signature:</label>
</th>
- <td>
+ <td class="align-top">
<input type="file"
id="release_signature"
name="release_signature"
+ class="form-control mb-2"
accept="application/pgp-signature,.asc"
aria-describedby="signature-help" />
- <span id="signature-help" class="help-text">Optional: Upload the
detached GPG signature (.asc) file</span>
+ <span id="signature-help" class="form-text text-muted">Optional:
Upload the detached GPG signature (.asc) file</span>
</td>
</tr>
<tr>
<td></td>
<td>
- <button type="submit" {% if not releases %}disabled{% endif %}>Add
package</button>
+ <button type="submit"
+ class="btn btn-primary mt-3"
+ {% if not releases %}disabled{% endif %}>Add
package</button>
</td>
</tr>
</tbody>
</table>
</form>
- <div class="form-separator">
- <span>Or add multiple packages from a URL</span>
+ <div class="my-5 position-relative">
+ <hr class="border-2 opacity-25" />
+ <div class="position-absolute top-50 start-50 translate-middle bg-white
px-4 fs-5">
+ Or add multiple packages from a URL
+ </div>
</div>
<form method="post" class="striking">
<input type="hidden" name="form_type" value="bulk" />
- <table class="form-table">
+ <table class="table">
<tbody>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label for="bulk_release_key">Release:</label>
</th>
- <td>
- <select id="bulk_release_key" name="release_key" required>
+ <td class="align-top">
+ <select id="bulk_release_key"
+ name="release_key"
+ class="form-select mb-2"
+ required>
<option value="">Select a release...</option>
{% for release in releases %}
<option value="{{ release.storage_key }}"
@@ -257,84 +159,103 @@
</option>
{% endfor %}
</select>
- {% if not releases %}<p class="error-message">No releases found
that you can add packages to.</p>{% endif %}
+ {% if not releases %}<p class="text-danger mt-1">No releases found
that you can add packages to.</p>{% endif %}
</td>
</tr>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label for="bulk_url">URL:</label>
</th>
- <td>
+ <td class="align-top">
<input type="url"
id="bulk_url"
name="url"
+ class="form-control mb-2"
required
placeholder="https://example.org/path/to/packages/"
aria-describedby="url-help" />
- <span id="url-help" class="help-text">Enter the URL of the
directory containing release packages</span>
+ <span id="url-help" class="form-text text-muted">Enter the URL of
the directory containing release packages</span>
</td>
</tr>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label>File types:</label>
</th>
- <td>
- <div class="checkbox-group">
- <div>
+ <td class="align-top">
+ <div class="mb-2">
+ <div class="form-check my-2">
<input type="checkbox"
+ class="form-check-input"
id="type_targz"
name="file_types"
value=".tar.gz"
checked />
- <label for="type_targz">.tar.gz files</label>
+ <label class="form-check-label" for="type_targz">.tar.gz
files</label>
</div>
- <div>
- <input type="checkbox" id="type_tgz" name="file_types"
value=".tgz" checked />
- <label for="type_tgz">.tgz files</label>
+ <div class="form-check my-2">
+ <input type="checkbox"
+ class="form-check-input"
+ id="type_tgz"
+ name="file_types"
+ value=".tgz"
+ checked />
+ <label class="form-check-label" for="type_tgz">.tgz
files</label>
</div>
- <div>
- <input type="checkbox" id="type_zip" name="file_types"
value=".zip" />
- <label for="type_zip">.zip files</label>
+ <div class="form-check my-2">
+ <input type="checkbox"
+ class="form-check-input"
+ id="type_zip"
+ name="file_types"
+ value=".zip" />
+ <label class="form-check-label" for="type_zip">.zip
files</label>
</div>
- <div>
- <input type="checkbox" id="type_jar" name="file_types"
value=".jar" />
- <label for="type_jar">.jar files</label>
+ <div class="form-check my-2">
+ <input type="checkbox"
+ class="form-check-input"
+ id="type_jar"
+ name="file_types"
+ value=".jar" />
+ <label class="form-check-label" for="type_jar">.jar
files</label>
</div>
</div>
</td>
</tr>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label for="bulk_max_depth">Maximum depth:</label>
</th>
- <td>
+ <td class="align-top">
<input type="number"
id="bulk_max_depth"
name="max_depth"
+ class="form-control mb-2 w-25"
value="1"
min="1"
max="10"
required
aria-describedby="depth-help" />
- <span id="depth-help" class="help-text">Maximum request depth to
search for packages (1-10)</span>
+ <span id="depth-help" class="form-text text-muted">Maximum request
depth to search for packages (1-10)</span>
</td>
</tr>
<tr>
- <th>
+ <th class="text-end pe-3 align-top">
<label for="bulk_require_signatures">Require signatures:</label>
</th>
- <td>
- <div class="checkbox-group">
- <div>
+ <td class="align-top">
+ <div class="mb-2">
+ <div class="form-check my-2">
<input type="checkbox"
+ class="form-check-input"
id="bulk_require_signatures"
name="require_signatures"
checked />
- <label for="bulk_require_signatures">Only download packages
that have corresponding .asc signature files</label>
+ <label class="form-check-label" for="bulk_require_signatures">
+ Only download packages that have corresponding .asc
signature files
+ </label>
</div>
</div>
</td>
@@ -343,7 +264,9 @@
<tr>
<td></td>
<td>
- <button type="submit" {% if not releases %}disabled{% endif %}>Add
packages from URL</button>
+ <button type="submit"
+ class="btn btn-primary mt-3"
+ {% if not releases %}disabled{% endif %}>Add packages from
URL</button>
</td>
</tr>
</tbody>
diff --git a/atr/templates/package-check.html b/atr/templates/package-check.html
index 6e103ff..5cf3e53 100644
--- a/atr/templates/package-check.html
+++ b/atr/templates/package-check.html
@@ -8,233 +8,11 @@
View the status and results of package verification tasks.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .task-actions {
- display: flex;
- gap: 1rem;
- margin-bottom: 1rem;
- }
-
- .task-summary {
- display: flex;
- gap: 1rem;
- align-items: center;
- padding: 1rem;
- background: #f8f9fa;
- border: 1px solid #dee2e6;
- border-radius: 4px;
- margin-bottom: 1rem;
- }
-
- .summary-title {
- font-weight: 600;
- margin-right: 1rem;
- }
-
- .summary-stats {
- display: flex;
- gap: 1rem;
- flex-wrap: wrap;
- }
-
- .summary-stat {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- padding: 0.5rem 1rem;
- border-radius: 4px;
- font-weight: 500;
- }
-
- .summary-stat .count {
- font-size: 1.2em;
- /* font-weight: 600; */
- }
-
- .summary-stat.completed {
- background: #d1e7dd;
- border: 1px solid #a3cfbb;
- }
-
- .summary-stat.failed {
- background: #f8d7da;
- border: 1px solid #f5c6cb;
- }
-
- .summary-stat.active {
- background: #cff4fc;
- border: 1px solid #9eeaf9;
- }
-
- .summary-stat.queued {
- background: #f8f9fa;
- border: 1px solid #dee2e6;
- }
-
- .task-actions .button-form {
- margin: 0;
- }
-
- .task-list {
- margin: 1rem 0;
- }
-
- .task-item {
- border: 2px solid #d1d2d3;
- border-radius: 4px;
- padding: 1rem;
- margin-bottom: 1rem;
- }
-
- .task-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 0.5rem;
- }
-
- .task-type {
- font-weight: 600;
- }
-
- .task-status {
- padding: 0.25rem 0.5rem;
- border-radius: 4px;
- font-size: 0.9em;
- }
-
- .status-queued {
- background: #f8f9fa;
- border: 1px solid #dee2e6;
- }
-
- .status-active {
- background: #cff4fc;
- border: 1px solid #9eeaf9;
- }
-
- .status-completed {
- background: #d1e7dd;
- border: 1px solid #a3cfbb;
- }
-
- .status-failed {
- background: #f8d7da;
- border: 1px solid #f5c6cb;
- }
-
- .task-details {
- margin-top: 0.5rem;
- font-size: 0.9em;
- }
-
- .task-result {
- margin-top: 0.5rem;
- padding: 0.5rem;
- background: #f8f9fa;
- border-radius: 4px;
- }
-
- .task-result details {
- margin-top: 0.5rem;
- }
-
- /* TODO: Move to atr.css */
- details summary {
- padding: 0.5rem;
- cursor: pointer;
- user-select: none;
- }
-
- .task-result summary:hover {
- background: #e9ecef;
- }
-
- .task-result table {
- margin-top: 0.5rem;
- width: 100%;
- border-collapse: collapse;
- }
-
- .task-result th,
- .task-result td {
- padding: 0.5rem;
- text-align: left;
- border: 1px solid #dee2e6;
- }
-
- .task-result th {
- background: #f8f9fa;
- font-weight: 600;
- }
-
- .file-list-table {
- width: 100%;
- border-collapse: collapse;
- margin-top: 0.5rem;
- }
-
- .file-list-table th,
- .file-list-table td {
- padding: 0.25rem 0.5rem;
- text-align: left;
- border: 1px solid #dee2e6;
- font-size: 0.9em;
- }
-
- .file-list-table th {
- background: #e9ecef;
- font-weight: 600;
- }
-
- .license-summary {
- display: flex;
- gap: 1rem;
- margin-bottom: 0.5rem;
- }
-
- .license-stat {
- padding: 0.25rem 0.5rem;
- border-radius: 4px;
- font-size: 0.9em;
- }
-
- .license-approved {
- background: #d1e7dd;
- border: 1px solid #a3cfbb;
- }
-
- .license-unapproved {
- background: #f8d7da;
- border: 1px solid #f5c6cb;
- }
-
- .license-unknown {
- background: #fff3cd;
- border: 1px solid #ffecb5;
- }
-
- .package-info {
- margin-bottom: 2rem;
- padding: 1rem;
- background: #f8f9fa;
- border: 1px solid #dee2e6;
- border-radius: 4px;
- }
-
- .package-info h2 {
- margin-top: 0;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
<a href="{{ url_for('root_candidate_review') }}" class="back-link">← Back to
Release Candidates</a>
- <div class="package-info">
- <h2>Package details</h2>
+ <div class="p-3 mb-4 bg-light border rounded">
+ <h2 class="mt-0">Package details</h2>
<p>
<strong>Filename:</strong> {{ package.filename }}
</p>
@@ -244,7 +22,7 @@
<p>
<strong>Size:</strong> {{ format_file_size(package.bytes_size) }}
</p>
- <p>
+ <p class="mb-0">
<strong>Uploaded:</strong> {{ package.uploaded.strftime("%Y-%m-%d %H:%M
UTC") }}
</p>
</div>
@@ -256,9 +34,9 @@
<h2>Verification tasks</h2>
{% if tasks %}
- <div class="task-summary">
- <span class="summary-title">Status summary:</span>
- <div class="summary-stats">
+ <div class="d-flex align-items-center p-3 mb-3 bg-light border rounded">
+ <span class="fw-bold me-3">Status summary:</span>
+ <div class="d-flex flex-wrap gap-3">
{% with %}
{% set status_counts = {
'completed': tasks|selectattr("status.value", "equalto",
"completed")|list|length,
@@ -269,9 +47,9 @@
{% for status, count in status_counts.items() %}
{% if count > 0 %}
- <div class="summary-stat {{ status }}">
- <span class="count">{{ count }}</span>
- <span class="label">
+ <div class="d-flex align-items-center gap-2 px-3 py-2 rounded
fw-medium {% if status == 'completed' %}bg-success-subtle border
border-success-subtle {% elif status == 'failed' %}bg-danger-subtle border
border-danger-subtle {% elif status == 'active' %}bg-info-subtle border
border-info-subtle {% else %}bg-light border{% endif %}">
+ <span class="fs-5">{{ count }}</span>
+ <span>
{%- if status == "queued" -%}
Pending
{%- elif status == "active" -%}
@@ -292,28 +70,28 @@
</div>
{% endif %}
- <div class="task-actions">
- <button type="button" onclick="toggleAllDetails()"
class="verify-link">Toggle all details</button>
+ <div class="d-flex gap-3 mb-3">
+ <button type="button" onclick="toggleAllDetails()" class="btn
btn-secondary">Toggle all details</button>
{% if tasks and all_tasks_completed %}
<form method="post"
action="{{ url_for('root_package_check_restart') }}"
- class="button-form">
+ class="m-0">
<input type="hidden"
name="artifact_sha3"
value="{{ package.artifact_sha3 }}" />
<input type="hidden" name="release_key" value="{{ release.storage_key
}}" />
- <button type="submit" class="verify-link">Restart all checks</button>
+ <button type="submit" class="btn btn-primary">Restart all
checks</button>
</form>
{% endif %}
</div>
- <div class="task-list">
+ <div class="mb-3">
{% if tasks %}
{% for task in tasks %}
- <div class="task-item">
- <div class="task-header">
- <span class="task-type">{{ task.task_type.replace('_', '
').replace("verify ", "").title() }}</span>
- <span class="task-status status-{{ task.status.value.lower() }}">
+ <div class="border border-2 rounded p-3 mb-3">
+ <div class="d-flex justify-content-between align-items-center mb-2">
+ <span class="fw-bold">{{ task.task_type.replace('_', '
').replace("verify ", "").title() }}</span>
+ <span class="badge rounded-pill {% if task.status.value ==
'queued' %}bg-secondary {% elif task.status.value == 'active' %}bg-info {% elif
task.status.value == 'completed' %}bg-success {% elif task.status.value ==
'failed' %}bg-danger {% else %}bg-secondary{% endif %}">
{%- if task.status.value == "queued" -%}
Pending
{%- elif task.status.value == "active" -%}
@@ -327,60 +105,60 @@
{%- endif -%}
</span>
</div>
- <div class="task-details">
+ <div class="small">
<div>Started: {{ task.started.strftime("%Y-%m-%d %H:%M UTC") if
task.started else "Not started" }}</div>
<div>Completed: {{ task.completed.strftime("%Y-%m-%d %H:%M UTC")
if task.completed else "Not completed" }}</div>
{% if task.result and task.result[0] is mapping and
task.result|length == 1 %}
- <details class="task-result">
+ <details class="mt-2 p-2 bg-light rounded">
{% if task.error %}
- <summary>Issue: {{ task.error }}</summary>
+ <summary class="cursor-pointer user-select-none p-2">Issue:
{{ task.error }}</summary>
{% else %}
- <summary>View detailed results</summary>
+ <summary class="cursor-pointer user-select-none p-2">View
detailed results</summary>
{% endif %}
{% if task.task_type == 'verify_rat_license' and
task.result[0] is mapping %}
- <div class="license-summary">
- <span class="license-stat license-approved">
+ <div class="d-flex gap-3 mb-2">
+ <span class="badge bg-success-subtle text-success-emphasis
border border-success-subtle px-2 py-1">
<strong>{{ task.result[0].approved_licenses }}</strong>
files with approved licenses
</span>
{% if task.result[0].unapproved_licenses > 0 %}
- <span class="license-stat license-unapproved">
+ <span class="badge bg-danger-subtle text-danger-emphasis
border border-danger-subtle px-2 py-1">
<strong>{{ task.result[0].unapproved_licenses
}}</strong> files with unapproved licenses
</span>
{% endif %}
{% if task.result[0].unknown_licenses > 0 %}
- <span class="license-stat license-unknown">
+ <span class="badge bg-warning-subtle
text-warning-emphasis border border-warning-subtle px-2 py-1">
<strong>{{ task.result[0].unknown_licenses }}</strong>
files with unknown licenses
</span>
{% endif %}
</div>
{% endif %}
- <table class="result-table">
+ <table class="table table-bordered mt-2">
<tbody>
{% for key, value in task.result[0].items() %}
{% if key != "debug_info" %}
<tr>
- <th>{{ key|replace('_', ' ') |title }}</th>
+ <th class="bg-light fw-bold">{{ key|replace('_', '
') |title }}</th>
<td>
{% if value is boolean %}
{{ "Yes" if value else "No" }}
{% elif value is mapping %}
- <table class="nested-table">
+ <table class="table table-sm mb-0">
{% for k, v in value.items() %}
<tr>
- <th>{{ k|replace('_', ' ') |title }}</th>
+ <th class="bg-light fw-bold">{{
k|replace('_', ' ') |title }}</th>
<td>{{ v }}</td>
</tr>
{% endfor %}
</table>
{% elif key == "unapproved_files" or key ==
"unknown_license_files" %}
{% if value|length > 0 %}
- <table class="file-list-table">
+ <table class="table table-sm table-bordered
mb-0">
<thead>
<tr>
- <th>File</th>
- <th>License</th>
+ <th class="bg-light fw-bold">File</th>
+ <th class="bg-light fw-bold">License</th>
</tr>
</thead>
<tbody>
@@ -408,10 +186,10 @@
</table>
</details>
{% else %}
- {% if task.error %}<div class="task-result">Issue: {{ task.error
}}</div>{% endif %}
+ {% if task.error %}<div class="mt-2 p-2 bg-light rounded">Issue:
{{ task.error }}</div>{% endif %}
{% if task.result %}
- <div class="task-result">
- <pre>{{ task.result }}</pre>
+ <div class="mt-2 p-2 bg-light rounded">
+ <pre class="mb-0">{{ task.result }}</pre>
</div>
{% endif %}
{% endif %}
diff --git a/atr/templates/project-directory.html
b/atr/templates/project-directory.html
index 6c7404f..903940c 100644
--- a/atr/templates/project-directory.html
+++ b/atr/templates/project-directory.html
@@ -8,91 +8,50 @@
List of all ASF projects and their latest releases.
{% endblock description %}
-{% block head_extra %}
- <style>
- .pmc-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
- gap: 1.5rem;
- margin: 2rem 0;
- }
-
- .pmc-card {
- background: white;
- border: 1px solid #e0e0e0;
- border-radius: 8px;
- padding: 1.5rem;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
- transition: transform 0.2s ease, box-shadow 0.2s ease;
- }
-
- .pmc-card:hover {
- transform: translateY(-2px);
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
- }
-
- .pmc-name {
- font-size: 1.5rem;
- font-weight: 500;
- color: #333;
- margin-bottom: 1rem;
- }
-
- .pmc-stats {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
- gap: 1rem;
- }
-
- .stat-item {
- background: #f8f9fa;
- padding: 0.75rem;
- border-radius: 6px;
- text-align: center;
- }
-
- .stat-label {
- display: block;
- color: #666;
- font-size: 0.9rem;
- margin-bottom: 0.25rem;
- }
-
- .stat-value {
- display: block;
- font-weight: 500;
- font-size: 1.25rem;
- color: #333;
- }
- </style>
-{% endblock head_extra %}
-
{% block content %}
<h1>Project directory</h1>
<p class="intro">Current ASF projects and their releases:</p>
- <input type="text"
- id="pmc-filter"
- onkeydown="if (event.key == 'Enter'){ filter() }" />
- <button type="button" onclick="filter()">Filter</button>
+ <div class="mb-3">
+ <input type="text"
+ id="pmc-filter"
+ class="form-control d-inline-block w-auto" />
+ <button type="button" class="btn btn-primary"
id="filter-button">Filter</button>
+ </div>
- <div class="pmc-grid">
+ <div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{% for pmc in projects %}
- <div class="pmc-card"
- onclick="location.href='{{ url_for("root_project_view",
project_name=pmc.name) }}';">
- <div class="pmc-name">{{ pmc.display_name }}</div>
- <div class="pmc-stats">
- <div class="stat-item">
- <span class="stat-label">PMC Members</span>
- <span class="stat-value">{{ pmc.pmc_members|length }}</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">Committers</span>
- <span class="stat-value">{{ pmc.committers|length }}</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">Release Managers</span>
- <span class="stat-value">{{ pmc.release_managers|length }}</span>
+ <div class="col">
+ <div class="card h-100 shadow-sm cursor-pointer hover-lift
project-card"
+ data-project-url="{{ url_for('root_project_view', name=pmc.name)
}}">
+ <div class="card-body">
+ <h2 class="card-title fs-4 mb-3">{{ pmc.display_name }}</h2>
+ <div class="row g-3">
+ <div class="col-4">
+ <div class="card h-100 bg-light border-0">
+ <div class="card-body p-2 d-flex flex-column
justify-content-between text-center">
+ <small class="text-secondary">PMC Members</small>
+ <span class="fs-4 fw-medium mt-2">{{
pmc.pmc_members|length }}</span>
+ </div>
+ </div>
+ </div>
+ <div class="col-4">
+ <div class="card h-100 bg-light border-0">
+ <div class="card-body p-2 d-flex flex-column
justify-content-between text-center">
+ <small class="text-secondary">Committers</small>
+ <span class="fs-4 fw-medium mt-2">{{ pmc.committers|length
}}</span>
+ </div>
+ </div>
+ </div>
+ <div class="col-4">
+ <div class="card h-100 bg-light border-0">
+ <div class="card-body p-2 d-flex flex-column
justify-content-between text-center">
+ <small class="text-secondary">Release Managers</small>
+ <span class="fs-4 fw-medium mt-2">{{
pmc.release_managers|length }}</span>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
</div>
</div>
@@ -105,16 +64,32 @@
<script>
function filter() {
const pmcFilter = document.getElementById("pmc-filter").value;
- const cards = document.getElementsByClassName("pmc-card");
+ const cards = document.querySelectorAll(".project-card");
for (let card of cards) {
- const nameElement = card.getElementsByClassName("pmc-name");
- const name = nameElement[0].innerHTML;
+ const nameElement = card.querySelector(".card-title");
+ const name = nameElement.innerHTML;
if (!pmcFilter) {
- card.hidden = false;
+ card.parentElement.hidden = false;
} else {
- card.hidden = !name.match(new RegExp(pmcFilter, 'i'));
+ card.parentElement.hidden = !name.match(new
RegExp(pmcFilter, 'i'));
}
}
}
+
+ // Add event listeners
+ document.getElementById("filter-button").addEventListener("click",
filter);
+ document.getElementById("pmc-filter").addEventListener("keydown",
function(event) {
+ if (event.key === "Enter") {
+ filter();
+ event.preventDefault();
+ }
+ });
+
+ // Add click handlers for project cards
+ document.querySelectorAll(".project-card").forEach(function(card) {
+ card.addEventListener("click", function() {
+ window.location.href = this.getAttribute("data-project-url");
+ });
+ });
</script>
{% endblock javascripts %}
diff --git a/atr/templates/project-view.html b/atr/templates/project-view.html
index adfcd58..9945c52 100644
--- a/atr/templates/project-view.html
+++ b/atr/templates/project-view.html
@@ -8,193 +8,116 @@
Release candidates to which you have access.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .card-header {
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 1rem;
- margin-bottom: 1rem;
- background-color: #f8f8f8;
- }
-
- .card-header h3 {
- margin: 0 0 0.5rem 0;
- }
-
- .card-meta {
- color: #666;
- font-size: 0.9em;
- display: flex;
- flex-wrap: wrap;
- gap: 1rem;
- margin-bottom: .5rem;
- padding-bottom: 1rem;
- border-bottom: 1px solid #ddd;
- }
-
- .card-meta-item::after {
- content: "•";
- margin-left: 1rem;
- color: #ccc;
- }
-
- .card-meta-item:last-child::after {
- content: none;
- }
-
- .card-body {
- display: flex;
- gap: 1rem;
- align-items: center;
- }
-
- .keys-grid {
- display: grid;
- gap: 1.5rem;
- }
-
- .key-card {
- background: white;
- border: 1px solid #d1d2d3;
- border-radius: 4px;
- overflow: hidden;
- padding: 1rem;
- }
-
- .key-card table {
- margin: 0;
- }
-
- .key-card td {
- word-break: break-all;
- }
-
- .key-card h3 {
- margin-top: 0;
- margin-bottom: 1rem;
- }
-
- .actions {
- display: flex;
- gap: 1rem;
- align-items: center;
- }
-
- .add-link {
- display: inline-block;
- padding: 0.5rem 1rem;
- background: #004477;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- font-weight: 500;
- text-decoration: none;
- font-size: 17px;
- }
-
- .add-link:hover {
- background: #003366;
- color: white;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
<h1>{{ project.display_name | capitalize }}</h1>
- <div class="card-header">
- <h3>Members</h3>
- <div class="card-meta">
- <span class="card-meta-item">PMC: {{ project.pmc_members|length }}</span>
- <span class="card-meta-item">Committers: {{ project.committers|length
}}</span>
+ <div class="card mb-4">
+ <div class="card-header bg-light">
+ <h3 class="mb-2">Members</h3>
+ </div>
+ <div class="card-body">
+ <div class="d-flex flex-wrap gap-3 small mb-1">
+ <span>PMC: {{ project.pmc_members|length }}</span>
+ <span class="d-flex align-items-center">
+ <span>Committers: {{ project.committers|length }}</span>
+ </span>
+ </div>
</div>
</div>
- <div class="card-header">
- <h3>Signing Keys</h3>
- <div class="card-meta"></div>
- <div class="keys-grid">
- {% for key in project.public_signing_keys %}
- <div class="key-card">
- <table>
- <tbody>
- <tr>
- <th>Fingerprint</th>
- <td>{{ key.fingerprint }}</td>
- </tr>
- <tr>
- <th>Key Type</th>
- <td>{{ algorithms[key.algorithm] }} ({{ key.length }}
bits)</td>
- </tr>
- <tr>
- <th>Created</th>
- <td>{{ key.created.strftime("%Y-%m-%d %H:%M:%S") }}</td>
- </tr>
- <tr>
- <th>Expires</th>
- <td>
- {% if key.expires %}
- {% set days_until_expiry = (key.expires - now).days %}
- {% if days_until_expiry < 0 %}
- <span class="expiry-error">
- {{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
- <span class="notice-badge error">Expired</span>
- </span>
- {% elif days_until_expiry <= 30 %}
- <span class="expiry-warning">
- {{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
- <span class="notice-badge warning">Expires in {{
days_until_expiry }} days</span>
- </span>
- {% else %}
- {{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
- {% endif %}
- {% else %}
- Never
- {% endif %}
- </td>
- </tr>
- <tr>
- <th>User ID</th>
- <td>{{ key.declared_uid or 'Not specified' }}</td>
- </tr>
- </tbody>
- </table>
- </div>
- {% endfor %}
+ <div class="card mb-4">
+ <div class="card-header bg-light">
+ <h3 class="mb-2">Signing Keys</h3>
+ </div>
+ <div class="card-body">
+ <div class="row row-cols-1 g-4">
+ {% for key in project.public_signing_keys %}
+ <div class="col">
+ <div class="card h-100 border">
+ <div class="card-body">
+ <table class="table mb-0">
+ <tbody>
+ <tr>
+ <th class="border-0">Fingerprint</th>
+ <td class="text-break border-0">{{ key.fingerprint
}}</td>
+ </tr>
+ <tr>
+ <th class="border-0">Key Type</th>
+ <td class="text-break border-0">{{
algorithms[key.algorithm] }} ({{ key.length }} bits)</td>
+ </tr>
+ <tr>
+ <th class="border-0">Created</th>
+ <td class="text-break border-0">{{
key.created.strftime("%Y-%m-%d %H:%M:%S") }}</td>
+ </tr>
+ <tr>
+ <th class="border-0">Expires</th>
+ <td class="text-break border-0">
+ {% if key.expires %}
+ {% set days_until_expiry = (key.expires - now).days
%}
+ {% if days_until_expiry < 0 %}
+ <span class="text-danger fw-bold">
+ {{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
+ <span class="badge bg-danger text-white
ms-2">Expired</span>
+ </span>
+ {% elif days_until_expiry <= 30 %}
+ <span class="text-warning fw-bold">
+ {{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
+ <span class="badge bg-warning text-dark
ms-2">Expires in {{ days_until_expiry }} days</span>
+ </span>
+ {% else %}
+ {{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
+ {% endif %}
+ {% else %}
+ Never
+ {% endif %}
+ </td>
+ </tr>
+ <tr>
+ <th class="border-0">User ID</th>
+ <td class="text-break border-0">{{ key.declared_uid or
'Not specified' }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
</div>
</div>
- <div class="card-header">
- <h3>Voting Policy</h3>
- <div class="actions">
- <!-- TODO: This is a PMC, not a project -->
- <a class="add-link"
- href="{{ url_for('root_project_voting_policy_add', name=project.name)
}}"><i class="fa-solid fa-plus"></i></a>
+ <div class="card mb-4">
+ <div class="card-header bg-light d-flex justify-content-between
align-items-center">
+ <h3 class="mb-0">Voting Policy</h3>
+ <div>
+ <!-- TODO: This is a PMC, not a project -->
+ <a class="btn btn-primary"
+ href="{{ url_for('root_project_voting_policy_add',
name=project.name) }}"><i class="fa-solid fa-plus"></i></a>
+ </div>
</div>
- <!-- <div class="card-meta"></div>-->
<div class="card-body">
{% if project.vote_policy %}
{% set vp = project.vote_policy %}
- <div class="key-card">
- <table>
- <tbody>
- <tr>
- <th>Mailto Addresses</th>
- <td>{{ vp.mailto_addresses }}</td>
- </tr>
- </tbody>
- </table>
+ <div class="card border">
+ <div class="card-body">
+ <table class="table mb-0">
+ <tbody>
+ <tr>
+ <th class="border-0">Mailto Addresses</th>
+ <td class="text-break border-0">{{ vp.mailto_addresses
}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
{% endif %}
</div>
</div>
- <div class="card-header">
- <h3>Product Lines</h3>
- <div class="card-meta"></div>
+ <div class="card mb-4">
+ <div class="card-header bg-light">
+ <h3 class="mb-0">Product Lines</h3>
+ </div>
<div class="card-body"></div>
</div>
diff --git a/atr/templates/release-bulk.html b/atr/templates/release-bulk.html
index b969005..c77d858 100644
--- a/atr/templates/release-bulk.html
+++ b/atr/templates/release-bulk.html
@@ -14,166 +14,26 @@
{% endif %}
{% endblock head_extra %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- nav.breadcrumbs {
- margin-bottom: 1rem;
- }
-
- nav.breadcrumbs a {
- text-decoration: none;
- }
-
- .task-container {
- margin: 1rem 0;
- }
-
- .task-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 1rem;
- padding: 1rem;
- background: #f8f9fa;
- border: 1px solid #dee2e6;
- border-radius: 4px;
- }
-
- .task-title {
- font-weight: 600;
- }
-
- .task-status {
- padding: 0.25rem 0.5rem;
- border-radius: 4px;
- font-size: 0.9em;
- }
-
- .status-queued {
- background: #f8f9fa;
- border: 1px solid #dee2e6;
- }
-
- .status-active {
- background: #cff4fc;
- border: 1px solid #9eeaf9;
- }
-
- .status-completed {
- background: #d1e7dd;
- border: 1px solid #a3cfbb;
- }
-
- .status-failed {
- background: #f8d7da;
- border: 1px solid #f5c6cb;
- }
-
- .task-details {
- margin-top: 1rem;
- padding: 1rem;
- background: #fff;
- border: 1px solid #dee2e6;
- border-radius: 4px;
- }
-
- .detail-row {
- display: flex;
- margin-bottom: 1rem;
- }
-
- .detail-label {
- width: 150px;
- font-weight: 600;
- }
-
- .progress-container {
- margin: 1rem 0;
- }
-
- /* Progress bar styling */
- progress {
- width: 100%;
- height: 20px;
- border-radius: 4px;
- overflow: hidden;
- border: 1px solid #dee2e6;
- }
-
- /* Styling the progress bar for different browsers */
- progress::-webkit-progress-bar {
- background-color: #f8f9fa;
- border-radius: 4px;
- }
-
- progress::-webkit-progress-value {
- background-color: #0366d6;
- transition: width 0.3s ease;
- }
-
- progress::-moz-progress-bar {
- background-color: #0366d6;
- border-radius: 4px;
- }
-
- .progress-text {
- margin-top: 0.5rem;
- font-size: 0.9em;
- color: #666;
- }
-
- .message-box {
- margin-top: 1rem;
- margin-bottom: 1rem;
- padding: 1rem;
- background: #f8f9fa;
- border: 1px solid #dee2e6;
- border-radius: 4px;
- }
-
- .error-box {
- margin-top: 1rem;
- padding: 1rem;
- background: #f8d7da;
- border: 1px solid #f5c6cb;
- border-radius: 4px;
- color: #842029;
- }
-
- .info-box {
- margin-top: 1rem;
- padding: 1rem;
- background: #cff4fc;
- border: 1px solid #9eeaf9;
- border-radius: 4px;
- }
-
- .full-width {
- width: 100%;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
- <div class="task-container">
- <nav class="breadcrumbs">
- <a href="{{ url_for('root_candidate_review') }}">Release candidates</a>
- {% if release %}
- <span>→</span>
- <span>{{ release.pmc.display_name }}</span>
- <span>→</span>
- <span>{{ release.product.product_name if release.product else "Unknown
product" }}</span>
- <span>→</span>
- <span>{{ release.version }}</span>
- {% endif %}
- <span>→</span>
- <span>Bulk download status</span>
+ <div class="my-3">
+ <nav class="mb-3">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item">
+ <a href="{{ url_for('root_candidate_review') }}"
+ class="text-decoration-none">Release candidates</a>
+ </li>
+ {% if release %}
+ <li class="breadcrumb-item">{{ release.pmc.display_name }}</li>
+ <li class="breadcrumb-item">{{ release.product.product_name if
release.product else "Unknown product" }}</li>
+ <li class="breadcrumb-item">{{ release.version }}</li>
+ {% endif %}
+ <li class="breadcrumb-item active">Bulk download status</li>
+ </ol>
</nav>
- <div class="task-header">
- <div class="task-title">Task status</div>
- <div class="task-status status-{{ task.status.value.lower() }}">
+ <div class="d-flex justify-content-between align-items-center p-3 mb-3
bg-light border rounded">
+ <div class="fw-medium">Task status</div>
+ <div class="badge rounded-pill {% if task.status.value == 'queued'
%}bg-secondary {% elif task.status.value == 'active' %}bg-info {% elif
task.status.value == 'completed' %}bg-success {% elif task.status.value ==
'failed' %}bg-danger {% else %}bg-secondary{% endif %}">
{%- if task.status.value == "queued" -%}
Pending
{%- elif task.status.value == "active" -%}
@@ -188,76 +48,105 @@
</div>
</div>
- <div class="task-details">
- <div class="detail-row">
- <div class="detail-label">Task ID</div>
- <div>{{ task.id }}</div>
- </div>
-
- <div class="detail-row">
- <div class="detail-label">Started</div>
- <div>
- {% if task.started %}
- {{ task.started.strftime("%Y-%m-%d %H:%M:%S UTC") }}
- {% else %}
- Not started
- {% endif %}
+ <div class="card mb-3">
+ <div class="card-body">
+ <div class="row mb-3">
+ <div class="col-md-3 fw-medium">Task ID</div>
+ <div class="col-md-9">{{ task.id }}</div>
</div>
- </div>
- {% if task.completed %}
- <div class="detail-row">
- <div class="detail-label">Completed</div>
- <div>{{ task.completed.strftime("%Y-%m-%d %H:%M:%S UTC") }}</div>
+ <div class="row mb-3">
+ <div class="col-md-3 fw-medium">Started</div>
+ <div class="col-md-9">
+ {% if task.started %}
+ {{ task.started.strftime("%Y-%m-%d %H:%M:%S UTC") }}
+ {% else %}
+ Not started
+ {% endif %}
+ </div>
</div>
- {% endif %}
- {% if task.result %}
- {% if task.result.progress is defined %}
- <div class="progress-container">
- <progress value="{{ task.result.progress }}" max="100"></progress>
- <div class="progress-text">{{ task.result.progress }}%
complete</div>
+ {% if task.completed %}
+ <div class="row mb-3">
+ <div class="col-md-3 fw-medium">Completed</div>
+ <div class="col-md-9">{{ task.completed.strftime("%Y-%m-%d
%H:%M:%S UTC") }}</div>
</div>
{% endif %}
- {% if task.result.message %}<div class="message-box">{{
task.result.message }}</div>{% endif %}
+ {% if task.result %}
+ {% if task.result.progress is defined %}
+ <div class="mb-3">
+ <div class="progress mb-2 progress-lg">
+ <div class="progress-bar py-2 fs-6"
+ role="progressbar"
+ aria-valuenow="{{ task.result.progress }}"
+ aria-valuemin="0"
+ aria-valuemax="100"
+ data-progress="{{ task.result.progress }}">{{
task.result.progress }}%</div>
+ </div>
+ <div class="text-muted small">{{ task.result.progress }}%
complete</div>
+ </div>
+ {% endif %}
- {% if task.status == TaskStatus.COMPLETED %}
- <div class="detail-row">
- <div class="detail-label">Summary</div>
- <div class="full-width">
- <table>
- <tbody>
- {% if task.result.url %}
- <tr>
- <th>URL</th>
- <td>{{ task.result.url }}</td>
- </tr>
- {% endif %}
- {% if task.result.file_types %}
- <tr>
- <th>File types</th>
- <td>{{ task.result.file_types|join(", ") }}</td>
- </tr>
- {% endif %}
- {% if task.result.files_downloaded %}
- <tr>
- <th>Files downloaded</th>
- <td>{{ task.result.files_downloaded }}</td>
- </tr>
- {% endif %}
- </tbody>
- </table>
+ {% if task.result.message %}<div class="p-3 mb-3 bg-light border
rounded">{{ task.result.message }}</div>{% endif %}
+
+ {% if task.status == TaskStatus.COMPLETED %}
+ <div class="row mb-3">
+ <div class="col-md-3 fw-medium">Summary</div>
+ <div class="col-md-9">
+ <div class="border">
+ <table class="table table-bordered mb-0">
+ <tbody>
+ {% if task.result.url %}
+ <tr>
+ <th class="bg-light">URL</th>
+ <td>{{ task.result.url }}</td>
+ </tr>
+ {% endif %}
+ {% if task.result.file_types %}
+ <tr>
+ <th class="bg-light">File types</th>
+ <td>{{ task.result.file_types|join(", ") }}</td>
+ </tr>
+ {% endif %}
+ {% if task.result.files_downloaded %}
+ <tr>
+ <th class="bg-light">Files downloaded</th>
+ <td>{{ task.result.files_downloaded }}</td>
+ </tr>
+ {% endif %}
+ </tbody>
+ </table>
+ </div>
+ </div>
</div>
- </div>
+ {% endif %}
{% endif %}
- {% endif %}
- {% if task.error %}<div class="error-box">{{ task.error }}</div>{% endif
%}
+ {% if task.error %}
+ <div class="p-3 mb-3 bg-danger-subtle border border-danger rounded
text-danger">{{ task.error }}</div>
+ {% endif %}
+ </div>
</div>
{% if task.status in [TaskStatus.QUEUED, TaskStatus.ACTIVE] %}
- <div class="info-box">This page will automatically refresh every 2
seconds to show the latest status.</div>
+ <div class="p-3 mb-3 bg-info-subtle border border-info rounded">
+ This page will automatically refresh every 2 seconds to show the
latest status.
+ </div>
{% endif %}
</div>
{% endblock content %}
+
+{% block javascripts %}
+ {{ super() }}
+ <script>
+ // Set progress bar width
+ document.addEventListener("DOMContentLoaded", function() {
+ const progressBar = document.querySelector(".progress-bar");
+ if (progressBar) {
+ const progress = progressBar.getAttribute("data-progress");
+ progressBar.style.width = progress + "%";
+ }
+ });
+ </script>
+{% endblock javascripts %}
diff --git a/atr/templates/release-vote.html b/atr/templates/release-vote.html
index f28b178..0ea117e 100644
--- a/atr/templates/release-vote.html
+++ b/atr/templates/release-vote.html
@@ -8,204 +8,106 @@
Initiate a vote for a release candidate.
{% endblock description %}
-{% block stylesheets %}
- {{ super() }}
- <style>
- .form-group {
- margin-bottom: 1.5rem;
- }
-
- .form-table {
- width: 100%;
- margin-bottom: 1.5rem;
- }
-
- .form-table th {
- width: 200px;
- text-align: right;
- padding-right: 1rem;
- vertical-align: top;
- font-weight: 500;
- }
-
- .form-table td {
- vertical-align: top;
- }
-
- .form-table label {
- border-bottom: none;
- padding-bottom: 0;
- }
-
- .radio-group {
- display: flex;
- gap: 1.5rem;
- }
-
- .radio-option {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- }
-
- .submit-button {
- padding: 0.5rem 1rem;
- background: #004477;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- font-weight: 500;
- font-size: 1rem;
- margin-top: 1rem;
- }
-
- .submit-button:hover {
- background: #003366;
- }
-
- .cancel-link {
- margin-left: 1rem;
- color: #666;
- text-decoration: none;
- }
-
- .cancel-link:hover {
- text-decoration: underline;
- }
-
- .release-info {
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 1rem;
- margin-bottom: 1.5rem;
- background-color: #f8f8f8;
- }
-
- .email-preview {
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 1rem;
- margin-top: 1rem;
- background-color: #fff;
- font-family: monospace;
- white-space: pre-wrap;
- overflow-x: auto;
- }
-
- .preview-header {
- font-weight: 600;
- margin-bottom: 0.5rem;
- }
-
- .warning-text {
- color: #856404;
- background-color: #fff3cd;
- border: 1px solid #ffeeba;
- border-radius: 4px;
- padding: 1rem;
- margin-bottom: 1.5rem;
- }
-
- select,
- input[type="text"] {
- padding: 0.375rem;
- border: 1px solid #ced4da;
- border-radius: 0.25rem;
- width: 100%;
- max-width: 600px;
- }
- </style>
-{% endblock stylesheets %}
-
{% block content %}
- <h1>Start release vote</h1>
-
- <div class="release-info">
- <h3>
- {{ release.pmc.display_name }} - {{ release.product.product_name if
release.product else "Unknown" }} {{ release.version }}
- </h3>
- <p>Initiating a vote for this release candidate will prepare an email to
be sent to the appropriate mailing list.</p>
- </div>
+ <div class="my-4">
+ <h1 class="mb-4">Start release vote</h1>
+
+ <div class="px-3 pb-4 mb-4 bg-light border rounded">
+ <h2 class="mt-4 mb-3 fs-5 border-0">
+ {{ release.pmc.display_name }} - {{ release.product.product_name if
release.product else "Unknown" }} {{ release.version }}
+ </h2>
+ <p class="mb-0">
+ Initiating a vote for this release candidate will prepare an email to
be sent to the appropriate mailing list.
+ </p>
+ </div>
- <div class="warning-text">
- <strong>Note:</strong> This feature is currently in development. The form
below only sends email to a test account.
- </div>
+ <div class="p-3 mb-4 bg-warning-subtle border border-warning rounded">
+ <strong>Note:</strong> This feature is currently in development. The
form below only sends email to a test account.
+ </div>
- <form method="post"
- action="{{ url_for('root_release_vote') }}"
- class="striking">
- <input type="hidden" name="release_key" value="{{ release.storage_key }}"
/>
+ <form method="post"
+ class="striking py-4 px-5"
+ action="{{ url_for('root_release_vote') }}">
+ <input type="hidden" name="release_key" value="{{ release.storage_key
}}" />
- <table class="form-table">
- <tbody>
- <tr>
- <th>
+ <div class="mb-4">
+ <div class="row mb-3 pb-3 border-bottom">
+ <div class="col-md-3 text-md-end fw-medium pt-2">
<label>Send vote email to:</label>
- </th>
- <td>
- <div class="radio-group">
- <div class="radio-option">
- <input type="radio" id="list_dev" name="mailing_list"
value="dev" checked />
- <label for="list_dev">dev@{{ release.pmc.name
}}.apache.org</label>
+ </div>
+ <div class="col-md-9">
+ <div class="d-flex gap-4">
+ <div class="form-check">
+ <input type="radio"
+ class="form-check-input"
+ id="list_dev"
+ name="mailing_list"
+ value="dev"
+ checked />
+ <label class="form-check-label" for="list_dev">dev@{{
release.pmc.name }}.apache.org</label>
</div>
- <div class="radio-option">
- <input type="radio" id="list_private" name="mailing_list"
value="private" />
- <label for="list_private">private@{{ release.pmc.name
}}.apache.org</label>
+ <div class="form-check">
+ <input type="radio"
+ class="form-check-input"
+ id="list_private"
+ name="mailing_list"
+ value="private" />
+ <label class="form-check-label" for="list_private">private@{{
release.pmc.name }}.apache.org</label>
</div>
</div>
- </td>
- </tr>
+ </div>
+ </div>
- <tr>
- <th>
+ <div class="row mb-3 pb-3 border-bottom">
+ <div class="col-md-3 text-md-end fw-medium pt-2">
<label for="vote_duration">Vote duration:</label>
- </th>
- <td>
- <select id="vote_duration" name="vote_duration"
class="form-select">
+ </div>
+ <div class="col-md-9">
+ <select id="vote_duration" name="vote_duration" class="form-select
w-75">
<option value="72">72 hours (minimum)</option>
<option value="120">5 days</option>
<option value="168">7 days</option>
</select>
- </td>
- </tr>
+ </div>
+ </div>
- <tr>
- <th>
+ <div class="row mb-3 pb-3 border-bottom">
+ <div class="col-md-3 text-md-end fw-medium pt-2">
<label for="gpg_key_id">Your GPG key ID:</label>
- </th>
- <td>
+ </div>
+ <div class="col-md-9">
<input type="text"
id="gpg_key_id"
name="gpg_key_id"
- class="form-control"
+ class="form-control w-75"
placeholder="e.g., 0x1A2B3C4D" />
- </td>
- </tr>
+ </div>
+ </div>
- <tr>
- <th>
+ <div class="row mb-3 pb-3 border-bottom">
+ <div class="col-md-3 text-md-end fw-medium pt-2">
<label for="commit_hash">Commit hash:</label>
- </th>
- <td>
+ </div>
+ <div class="col-md-9">
<input type="text"
id="commit_hash"
name="commit_hash"
- class="form-control"
+ class="form-control w-75"
placeholder="Git commit hash used for this release" />
- </td>
- </tr>
- </tbody>
- </table>
-
- <div class="form-group">
- <label class="preview-header">Email Preview:</label>
- <div class="email-preview">{{ email_preview }}</div>
- </div>
-
- <div class="form-actions">
- <button type="submit" class="submit-button">Prepare Vote Email</button>
- <a href="{{ url_for('root_candidate_review') }}"
class="cancel-link">Cancel</a>
- </div>
- </form>
+ </div>
+ </div>
+ </div>
+
+ <div class="mb-4">
+ <label class="fw-medium mb-2">Email Preview:</label>
+ <pre class="p-3 bg-white border rounded font-monospace
overflow-auto">{{ email_preview }}</pre>
+ </div>
+
+ <div class="mt-4">
+ <button type="submit" class="btn btn-primary">Prepare Vote
Email</button>
+ <a href="{{ url_for('root_candidate_review') }}"
+ class="btn btn-link text-secondary">Cancel</a>
+ </div>
+ </form>
+ </div>
{% endblock content %}
diff --git a/atr/templates/vote-policy-add.html
b/atr/templates/vote-policy-add.html
index eff43f7..a2aa369 100644
--- a/atr/templates/vote-policy-add.html
+++ b/atr/templates/vote-policy-add.html
@@ -8,15 +8,6 @@
Create a new vote policy.
{% endblock description %}
-{% block theme_css %}
- <link rel="stylesheet"
- href="{{ url_for('static', filename='css/atr-small.css') }}" />
-{% endblock theme_css %}
-
-{% block stylesheets %}
- {{ super() }}
-{% endblock stylesheets %}
-
{% block content %}
<h1>Add vote policy</h1>
<p>
diff --git a/atr/templates/vote-policy-edit.html
b/atr/templates/vote-policy-edit.html
index 4977714..0e66997 100644
--- a/atr/templates/vote-policy-edit.html
+++ b/atr/templates/vote-policy-edit.html
@@ -8,15 +8,6 @@
Edit a vote policy.
{% endblock description %}
-{% block theme_css %}
- <link rel="stylesheet"
- href="{{ url_for('static', filename='css/atr-small.css') }}" />
-{% endblock theme_css %}
-
-{% block stylesheets %}
- {{ super() }}
-{% endblock stylesheets %}
-
{% block content %}
<h1>Edit vote policy</h1>
<p>
diff --git a/bootstrap/custom.scss b/bootstrap/custom.scss
index 369be6d..5c076f4 100644
--- a/bootstrap/custom.scss
+++ b/bootstrap/custom.scss
@@ -6,6 +6,8 @@ $font-family-sans-serif: "Inter", sans-serif;
$headings-font-family: $font-family-sans-serif;
$table-cell-padding-y: 0.75rem;
$table-cell-padding-x: 0.75rem;
+$table-bg: transparent;
+$table-accent-bg: transparent;
// Variables
@import "../node_modules/bootstrap/scss/variables";
@@ -62,7 +64,7 @@ table.atr-data th {
}
th {
- color: #555;
+ color: $dark;
font-weight: 525;
@extend .align-middle;
@extend .w-25;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]