fix askama template shenanigans
This commit is contained in:
parent
f531730a6b
commit
4a1ff75ea9
11 changed files with 897 additions and 962 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -11,6 +11,7 @@ node_modules
|
|||
**/node_modules
|
||||
Cargo.toml
|
||||
Cargo.lock
|
||||
/askama.toml
|
||||
databuild/databuild.rs
|
||||
generated_number
|
||||
target
|
||||
|
|
|
|||
|
|
@ -24,8 +24,11 @@ rust_library(
|
|||
srcs = glob(["**/*.rs"]) + [
|
||||
":generate_databuild_rust",
|
||||
],
|
||||
compile_data = glob(["web/templates/**"]) + ["askama.toml"],
|
||||
crate_root = "lib.rs",
|
||||
edition = "2021",
|
||||
# This is required to point to the `askama.toml`, which then points to the appropriate place for templates
|
||||
rustc_env = {"CARGO_MANIFEST_DIR": "$(BINDIR)/" + package_name()},
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@crates//:askama",
|
||||
|
|
|
|||
2
databuild/askama.toml
Normal file
2
databuild/askama.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[general]
|
||||
dirs = ["web/templates"]
|
||||
File diff suppressed because it is too large
Load diff
83
databuild/web/templates/home.html
Normal file
83
databuild/web/templates/home.html
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
{% macro head(title) %}<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@600&display=swap" rel="stylesheet">
|
||||
<title>{{ title }}</title>
|
||||
<style>
|
||||
@view-transition{navigation:auto}
|
||||
:root{--color-brand:#F2994A;--color-brand-light:#fef3e2;--color-text:#333;--color-text-muted:#6b7280;--color-bg:#f9fafb;--color-surface:#fff;--color-border:#e5e7eb}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--color-bg);color:var(--color-text);line-height:1.5}
|
||||
nav{background:var(--color-surface);border-bottom:1px solid var(--color-border);padding:.75rem 1.5rem;display:flex;align-items:center;gap:2rem;view-transition-name:nav}
|
||||
.logo{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--color-text)}
|
||||
.logo svg{width:28px;height:25px}
|
||||
.logo span{font-family:'Roboto Slab',serif;font-weight:600;font-size:1.125rem}
|
||||
nav .links{display:flex;gap:1.5rem}
|
||||
nav .links a{color:var(--color-text-muted);text-decoration:none;font-size:.875rem;padding:.25rem 0;border-bottom:2px solid transparent}
|
||||
nav .links a:hover{color:var(--color-text)}
|
||||
nav .links a.active{color:var(--color-brand);border-bottom-color:var(--color-brand)}
|
||||
nav .graph-label{margin-left:auto;color:var(--color-text-muted);font-size:.875rem}
|
||||
main{max-width:1200px;margin:0 auto;padding:1.5rem}
|
||||
h1{font-size:1.5rem;font-weight:600;margin-bottom:1rem}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1rem;margin-bottom:1.5rem}
|
||||
.stat-card{background:var(--color-surface);border:1px solid var(--color-border);border-radius:.5rem;padding:1rem;text-decoration:none;color:inherit}
|
||||
.stat-card:hover{border-color:var(--color-brand)}
|
||||
.stat-card .value{font-size:1.5rem;font-weight:600}
|
||||
.stat-card .label{font-size:.75rem;color:var(--color-text-muted)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro nav(active, graph_label) %}
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<svg viewBox="0 0 243 215" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M123.5 77L149.048 121.25H97.9523L123.5 77Z" fill="#F2994A"/>
|
||||
<path d="M224.772 125.035L155.772 125.035L109.52 45.3147L86.7722 45.3147L40.2722 124.463L16.7725 124.463" stroke="#333" stroke-width="20"/>
|
||||
<path d="M86.6196 5.18886L121.12 64.9444L75.2062 144.86L86.58 164.56L178.375 165.256L190.125 185.608" stroke="#333" stroke-width="20"/>
|
||||
<path d="M51.966 184.847L86.4659 125.092L178.632 124.896L190.006 105.196L144.711 25.3514L156.461 5.00002" stroke="#333" stroke-width="20"/>
|
||||
</svg>
|
||||
<span>DataBuild</span>
|
||||
</a>
|
||||
<div class="links">
|
||||
<a href="/wants"{% if active == "wants" %} class="active"{% endif %}>Wants</a>
|
||||
<a href="/partitions"{% if active == "partitions" %} class="active"{% endif %}>Partitions</a>
|
||||
<a href="/job_runs"{% if active == "job_runs" %} class="active"{% endif %}>Job Runs</a>
|
||||
</div>
|
||||
<span class="graph-label">{{ graph_label }}</span>
|
||||
</nav>
|
||||
<main>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro footer() %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{% endmacro %}
|
||||
|
||||
{% call head("Home - DataBuild") %}
|
||||
{% call nav("", base.graph_label) %}
|
||||
|
||||
<h1>Dashboard</h1>
|
||||
|
||||
<div class="stats-grid">
|
||||
<a href="/wants" class="stat-card">
|
||||
<div class="value">{{ active_wants_count }}</div>
|
||||
<div class="label">Active Wants</div>
|
||||
</a>
|
||||
<a href="/job_runs" class="stat-card">
|
||||
<div class="value">{{ active_job_runs_count }}</div>
|
||||
<div class="label">Active Job Runs</div>
|
||||
</a>
|
||||
<a href="/partitions" class="stat-card">
|
||||
<div class="value">{{ live_partitions_count }}</div>
|
||||
<div class="label">Live Partitions</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% call footer() %}
|
||||
127
databuild/web/templates/job_runs/detail.html
Normal file
127
databuild/web/templates/job_runs/detail.html
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
{% macro head(title) %}<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@600&display=swap" rel="stylesheet">
|
||||
<title>{{ title }}</title>
|
||||
<style>
|
||||
@view-transition{navigation:auto}
|
||||
:root{--color-brand:#F2994A;--color-brand-light:#fef3e2;--color-text:#333;--color-text-muted:#6b7280;--color-bg:#f9fafb;--color-surface:#fff;--color-border:#e5e7eb}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--color-bg);color:var(--color-text);line-height:1.5}
|
||||
nav{background:var(--color-surface);border-bottom:1px solid var(--color-border);padding:.75rem 1.5rem;display:flex;align-items:center;gap:2rem;view-transition-name:nav}
|
||||
.logo{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--color-text)}
|
||||
.logo svg{width:28px;height:25px}
|
||||
.logo span{font-family:'Roboto Slab',serif;font-weight:600;font-size:1.125rem}
|
||||
nav .links{display:flex;gap:1.5rem}
|
||||
nav .links a{color:var(--color-text-muted);text-decoration:none;font-size:.875rem;padding:.25rem 0;border-bottom:2px solid transparent}
|
||||
nav .links a:hover{color:var(--color-text)}
|
||||
nav .links a.active{color:var(--color-brand);border-bottom-color:var(--color-brand)}
|
||||
nav .graph-label{margin-left:auto;color:var(--color-text-muted);font-size:.875rem}
|
||||
main{max-width:1200px;margin:0 auto;padding:1.5rem}
|
||||
h1{font-size:1.5rem;font-weight:600;margin-bottom:1rem}
|
||||
.status{display:inline-block;padding:.25rem .5rem;border-radius:.25rem;font-size:.75rem;font-weight:500}
|
||||
.status-succeeded,.status-jobrunsucceeded{background:#dcfce7;color:#166534}
|
||||
.status-running,.status-jobrunrunning{background:#ede9fe;color:#5b21b6}
|
||||
.status-failed,.status-jobrunfailed,.status-depmiss,.status-jobrundepmiss{background:#fee2e2;color:#991b1b}
|
||||
.status-new,.status-jobrunnew{background:var(--color-brand-light);color:#92400e}
|
||||
.detail-header{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem}
|
||||
.detail-header h1{margin-bottom:0}
|
||||
.detail-section{background:var(--color-surface);border:1px solid var(--color-border);border-radius:.5rem;padding:1rem;margin-bottom:1rem}
|
||||
.detail-section h2{font-size:.875rem;font-weight:500;color:var(--color-text-muted);margin-bottom:.75rem}
|
||||
.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1rem}
|
||||
.detail-item label{display:block;font-size:.75rem;color:var(--color-text-muted);margin-bottom:.25rem}
|
||||
.partition-list{list-style:none;font-family:monospace;font-size:.8125rem}
|
||||
.partition-list li{padding:.25rem 0}
|
||||
.partition-list a{color:var(--color-brand);text-decoration:none}
|
||||
.partition-list a:hover{text-decoration:underline}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro nav(active, graph_label) %}
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<svg viewBox="0 0 243 215" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M123.5 77L149.048 121.25H97.9523L123.5 77Z" fill="#F2994A"/>
|
||||
<path d="M224.772 125.035L155.772 125.035L109.52 45.3147L86.7722 45.3147L40.2722 124.463L16.7725 124.463" stroke="#333" stroke-width="20"/>
|
||||
<path d="M86.6196 5.18886L121.12 64.9444L75.2062 144.86L86.58 164.56L178.375 165.256L190.125 185.608" stroke="#333" stroke-width="20"/>
|
||||
<path d="M51.966 184.847L86.4659 125.092L178.632 124.896L190.006 105.196L144.711 25.3514L156.461 5.00002" stroke="#333" stroke-width="20"/>
|
||||
</svg>
|
||||
<span>DataBuild</span>
|
||||
</a>
|
||||
<div class="links">
|
||||
<a href="/wants"{% if active == "wants" %} class="active"{% endif %}>Wants</a>
|
||||
<a href="/partitions"{% if active == "partitions" %} class="active"{% endif %}>Partitions</a>
|
||||
<a href="/job_runs"{% if active == "job_runs" %} class="active"{% endif %}>Job Runs</a>
|
||||
</div>
|
||||
<span class="graph-label">{{ graph_label }}</span>
|
||||
</nav>
|
||||
<main>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro footer() %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{% endmacro %}
|
||||
|
||||
{% call head("Job Run - DataBuild") %}
|
||||
{% call nav("job_runs", base.graph_label) %}
|
||||
|
||||
<div class="detail-header" style="view-transition-name:job-run-header">
|
||||
<h1>Job Run: {{ job_run.id }}</h1>
|
||||
{% match job_run.status %}
|
||||
{% when Some with (s) %}<span class="status status-{{ s.name_lowercase }}">{{ s.name }}</span>
|
||||
{% when None %}
|
||||
{% endmatch %}
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<h2>Details</h2>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label>Last Heartbeat</label>
|
||||
<span>
|
||||
{% match job_run.last_heartbeat_at %}
|
||||
{% when Some with (ts) %}{{ ts }}
|
||||
{% when None %}-
|
||||
{% endmatch %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<h2>Building Partitions ({{ job_run.building_partitions.len() }})</h2>
|
||||
<ul class="partition-list">
|
||||
{% for p in job_run.building_partitions %}
|
||||
<li><a href="/partitions/{{ p.partition_ref_encoded }}">{{ p.partition_ref }}</a></li>
|
||||
{% endfor %}
|
||||
{% if job_run.building_partitions.is_empty() %}
|
||||
<li style="color:var(--color-text-muted)">No partitions</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if !job_run.servicing_wants.is_empty() %}
|
||||
<div class="detail-section">
|
||||
<h2>Servicing Wants ({{ job_run.servicing_wants.len() }})</h2>
|
||||
{% for sw in job_run.servicing_wants %}
|
||||
<div style="margin-bottom:.75rem">
|
||||
<div><a href="/wants/{{ sw.want_id }}">{{ sw.want_id }}</a></div>
|
||||
<ul class="partition-list" style="margin-left:1rem">
|
||||
{% for p in sw.partitions %}
|
||||
<li>{{ p.partition_ref }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% call footer() %}
|
||||
129
databuild/web/templates/job_runs/list.html
Normal file
129
databuild/web/templates/job_runs/list.html
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
{% macro head(title) %}<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@600&display=swap" rel="stylesheet">
|
||||
<title>{{ title }}</title>
|
||||
<style>
|
||||
@view-transition{navigation:auto}
|
||||
:root{--color-brand:#F2994A;--color-brand-light:#fef3e2;--color-text:#333;--color-text-muted:#6b7280;--color-bg:#f9fafb;--color-surface:#fff;--color-border:#e5e7eb}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--color-bg);color:var(--color-text);line-height:1.5}
|
||||
nav{background:var(--color-surface);border-bottom:1px solid var(--color-border);padding:.75rem 1.5rem;display:flex;align-items:center;gap:2rem;view-transition-name:nav}
|
||||
.logo{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--color-text)}
|
||||
.logo svg{width:28px;height:25px}
|
||||
.logo span{font-family:'Roboto Slab',serif;font-weight:600;font-size:1.125rem}
|
||||
nav .links{display:flex;gap:1.5rem}
|
||||
nav .links a{color:var(--color-text-muted);text-decoration:none;font-size:.875rem;padding:.25rem 0;border-bottom:2px solid transparent}
|
||||
nav .links a:hover{color:var(--color-text)}
|
||||
nav .links a.active{color:var(--color-brand);border-bottom-color:var(--color-brand)}
|
||||
nav .graph-label{margin-left:auto;color:var(--color-text-muted);font-size:.875rem}
|
||||
main{max-width:1200px;margin:0 auto;padding:1.5rem}
|
||||
h1{font-size:1.5rem;font-weight:600;margin-bottom:1rem}
|
||||
table{width:100%;background:var(--color-surface);border:1px solid var(--color-border);border-radius:.5rem;border-collapse:collapse;font-size:.875rem}
|
||||
th,td{padding:.75rem 1rem;text-align:left;border-bottom:1px solid var(--color-border)}
|
||||
th{background:var(--color-bg);font-weight:500;color:var(--color-text-muted)}
|
||||
tr:last-child td{border-bottom:none}
|
||||
tr:hover{background:var(--color-bg)}
|
||||
td a{color:var(--color-brand);text-decoration:none}
|
||||
td a:hover{text-decoration:underline}
|
||||
.status{display:inline-block;padding:.25rem .5rem;border-radius:.25rem;font-size:.75rem;font-weight:500}
|
||||
.status-succeeded,.status-jobrunsucceeded{background:#dcfce7;color:#166534}
|
||||
.status-running,.status-jobrunrunning{background:#ede9fe;color:#5b21b6}
|
||||
.status-failed,.status-jobrunfailed,.status-depmiss,.status-jobrundepmiss{background:#fee2e2;color:#991b1b}
|
||||
.status-new,.status-jobrunnew{background:var(--color-brand-light);color:#92400e}
|
||||
.pagination{display:flex;gap:.5rem;margin-top:1rem;align-items:center;justify-content:center}
|
||||
.pagination a,.pagination span{padding:.5rem .75rem;border:1px solid var(--color-border);border-radius:.25rem;text-decoration:none;color:var(--color-text);font-size:.875rem}
|
||||
.pagination a:hover{background:var(--color-bg)}
|
||||
.pagination .disabled{color:var(--color-text-muted)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro nav(active, graph_label) %}
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<svg viewBox="0 0 243 215" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M123.5 77L149.048 121.25H97.9523L123.5 77Z" fill="#F2994A"/>
|
||||
<path d="M224.772 125.035L155.772 125.035L109.52 45.3147L86.7722 45.3147L40.2722 124.463L16.7725 124.463" stroke="#333" stroke-width="20"/>
|
||||
<path d="M86.6196 5.18886L121.12 64.9444L75.2062 144.86L86.58 164.56L178.375 165.256L190.125 185.608" stroke="#333" stroke-width="20"/>
|
||||
<path d="M51.966 184.847L86.4659 125.092L178.632 124.896L190.006 105.196L144.711 25.3514L156.461 5.00002" stroke="#333" stroke-width="20"/>
|
||||
</svg>
|
||||
<span>DataBuild</span>
|
||||
</a>
|
||||
<div class="links">
|
||||
<a href="/wants"{% if active == "wants" %} class="active"{% endif %}>Wants</a>
|
||||
<a href="/partitions"{% if active == "partitions" %} class="active"{% endif %}>Partitions</a>
|
||||
<a href="/job_runs"{% if active == "job_runs" %} class="active"{% endif %}>Job Runs</a>
|
||||
</div>
|
||||
<span class="graph-label">{{ graph_label }}</span>
|
||||
</nav>
|
||||
<main>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro footer() %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{% endmacro %}
|
||||
|
||||
{% call head("Job Runs - DataBuild") %}
|
||||
{% call nav("job_runs", base.graph_label) %}
|
||||
|
||||
<h1>Job Runs</h1>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Job Run ID</th>
|
||||
<th>Status</th>
|
||||
<th>Building Partitions</th>
|
||||
<th>Last Heartbeat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for jr in job_runs %}
|
||||
<tr style="view-transition-name: job-run-{{ loop.index }}">
|
||||
<td><a href="/job_runs/{{ jr.id }}">{{ jr.id }}</a></td>
|
||||
<td>
|
||||
{% match jr.status %}
|
||||
{% when Some with (s) %}<span class="status status-{{ s.name_lowercase }}">{{ s.name }}</span>
|
||||
{% when None %}<span class="status">Unknown</span>
|
||||
{% endmatch %}
|
||||
</td>
|
||||
<td>{{ jr.building_partitions.len() }}</td>
|
||||
<td>
|
||||
{% match jr.last_heartbeat_at %}
|
||||
{% when Some with (ts) %}{{ ts }}
|
||||
{% when None %}-
|
||||
{% endmatch %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if job_runs.is_empty() %}
|
||||
<tr>
|
||||
<td colspan="4" style="text-align:center;color:var(--color-text-muted)">No job runs found</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pagination">
|
||||
{% if self.has_prev() %}
|
||||
<a href="?page={{ self.prev_page() }}">Previous</a>
|
||||
{% else %}
|
||||
<span class="disabled">Previous</span>
|
||||
{% endif %}
|
||||
<span>Page {{ page + 1 }} of {{ (total_count + page_size - 1) / page_size }}</span>
|
||||
{% if self.has_next() %}
|
||||
<a href="?page={{ self.next_page() }}">Next</a>
|
||||
{% else %}
|
||||
<span class="disabled">Next</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% call footer() %}
|
||||
141
databuild/web/templates/partitions/detail.html
Normal file
141
databuild/web/templates/partitions/detail.html
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
{% macro head(title) %}<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@600&display=swap" rel="stylesheet">
|
||||
<title>{{ title }}</title>
|
||||
<style>
|
||||
@view-transition{navigation:auto}
|
||||
:root{--color-brand:#F2994A;--color-brand-light:#fef3e2;--color-text:#333;--color-text-muted:#6b7280;--color-bg:#f9fafb;--color-surface:#fff;--color-border:#e5e7eb}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--color-bg);color:var(--color-text);line-height:1.5}
|
||||
nav{background:var(--color-surface);border-bottom:1px solid var(--color-border);padding:.75rem 1.5rem;display:flex;align-items:center;gap:2rem;view-transition-name:nav}
|
||||
.logo{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--color-text)}
|
||||
.logo svg{width:28px;height:25px}
|
||||
.logo span{font-family:'Roboto Slab',serif;font-weight:600;font-size:1.125rem}
|
||||
nav .links{display:flex;gap:1.5rem}
|
||||
nav .links a{color:var(--color-text-muted);text-decoration:none;font-size:.875rem;padding:.25rem 0;border-bottom:2px solid transparent}
|
||||
nav .links a:hover{color:var(--color-text)}
|
||||
nav .links a.active{color:var(--color-brand);border-bottom-color:var(--color-brand)}
|
||||
nav .graph-label{margin-left:auto;color:var(--color-text-muted);font-size:.875rem}
|
||||
main{max-width:1200px;margin:0 auto;padding:1.5rem}
|
||||
h1{font-size:1.5rem;font-weight:600;margin-bottom:1rem}
|
||||
.status{display:inline-block;padding:.25rem .5rem;border-radius:.25rem;font-size:.75rem;font-weight:500}
|
||||
.status-live,.status-partitionlive{background:#dcfce7;color:#166534}
|
||||
.status-building,.status-partitionbuilding{background:#ede9fe;color:#5b21b6}
|
||||
.status-failed,.status-partitionfailed{background:#fee2e2;color:#991b1b}
|
||||
.status-idle,.status-partitionidle{background:#e0f2fe;color:#075985}
|
||||
.status-tainted,.status-partitiontainted{background:#fef3c7;color:#92400e}
|
||||
.status-upstreambuilding,.status-partitionupstreambuilding{background:#fae8ff;color:#86198f}
|
||||
.status-upstreamfailed,.status-partitionupstreamfailed{background:#ffe4e6;color:#be123c}
|
||||
.detail-header{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem}
|
||||
.detail-header h1{margin-bottom:0}
|
||||
.detail-section{background:var(--color-surface);border:1px solid var(--color-border);border-radius:.5rem;padding:1rem;margin-bottom:1rem}
|
||||
.detail-section h2{font-size:.875rem;font-weight:500;color:var(--color-text-muted);margin-bottom:.75rem}
|
||||
.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1rem}
|
||||
.detail-item label{display:block;font-size:.75rem;color:var(--color-text-muted);margin-bottom:.25rem}
|
||||
.partition-list{list-style:none;font-family:monospace;font-size:.8125rem}
|
||||
.partition-list li{padding:.25rem 0}
|
||||
.partition-list a{color:var(--color-brand);text-decoration:none}
|
||||
.partition-list a:hover{text-decoration:underline}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro nav(active, graph_label) %}
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<svg viewBox="0 0 243 215" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M123.5 77L149.048 121.25H97.9523L123.5 77Z" fill="#F2994A"/>
|
||||
<path d="M224.772 125.035L155.772 125.035L109.52 45.3147L86.7722 45.3147L40.2722 124.463L16.7725 124.463" stroke="#333" stroke-width="20"/>
|
||||
<path d="M86.6196 5.18886L121.12 64.9444L75.2062 144.86L86.58 164.56L178.375 165.256L190.125 185.608" stroke="#333" stroke-width="20"/>
|
||||
<path d="M51.966 184.847L86.4659 125.092L178.632 124.896L190.006 105.196L144.711 25.3514L156.461 5.00002" stroke="#333" stroke-width="20"/>
|
||||
</svg>
|
||||
<span>DataBuild</span>
|
||||
</a>
|
||||
<div class="links">
|
||||
<a href="/wants"{% if active == "wants" %} class="active"{% endif %}>Wants</a>
|
||||
<a href="/partitions"{% if active == "partitions" %} class="active"{% endif %}>Partitions</a>
|
||||
<a href="/job_runs"{% if active == "job_runs" %} class="active"{% endif %}>Job Runs</a>
|
||||
</div>
|
||||
<span class="graph-label">{{ graph_label }}</span>
|
||||
</nav>
|
||||
<main>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro footer() %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{% endmacro %}
|
||||
|
||||
{% call head("Partition - DataBuild") %}
|
||||
{% call nav("partitions", base.graph_label) %}
|
||||
|
||||
<div class="detail-header" style="view-transition-name:partition-header">
|
||||
<h1 style="font-family:monospace;font-size:1.25rem">
|
||||
{% if partition.has_partition_ref %}{{ partition.partition_ref }}{% else %}Unknown{% endif %}
|
||||
</h1>
|
||||
{% match partition.status %}
|
||||
{% when Some with (s) %}<span class="status status-{{ s.name_lowercase }}">{{ s.name }}</span>
|
||||
{% when None %}
|
||||
{% endmatch %}
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<h2>Details</h2>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label>UUID</label>
|
||||
<span style="font-family:monospace;font-size:.8125rem">{{ partition.uuid }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Last Updated</label>
|
||||
<span>
|
||||
{% match partition.last_updated_timestamp %}
|
||||
{% when Some with (ts) %}{{ ts }}
|
||||
{% when None %}-
|
||||
{% endmatch %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if !partition.job_run_ids.is_empty() %}
|
||||
<div class="detail-section">
|
||||
<h2>Job Runs ({{ partition.job_run_ids.len() }})</h2>
|
||||
<ul class="partition-list">
|
||||
{% for id in partition.job_run_ids %}
|
||||
<li><a href="/job_runs/{{ id }}">{{ id }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if !partition.want_ids.is_empty() %}
|
||||
<div class="detail-section">
|
||||
<h2>Referenced by Wants ({{ partition.want_ids.len() }})</h2>
|
||||
<ul class="partition-list">
|
||||
{% for id in partition.want_ids %}
|
||||
<li><a href="/wants/{{ id }}">{{ id }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if !partition.taint_ids.is_empty() %}
|
||||
<div class="detail-section">
|
||||
<h2>Taints ({{ partition.taint_ids.len() }})</h2>
|
||||
<ul class="partition-list">
|
||||
{% for id in partition.taint_ids %}
|
||||
<li>{{ id }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% call footer() %}
|
||||
136
databuild/web/templates/partitions/list.html
Normal file
136
databuild/web/templates/partitions/list.html
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
{% macro head(title) %}<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@600&display=swap" rel="stylesheet">
|
||||
<title>{{ title }}</title>
|
||||
<style>
|
||||
@view-transition{navigation:auto}
|
||||
:root{--color-brand:#F2994A;--color-brand-light:#fef3e2;--color-text:#333;--color-text-muted:#6b7280;--color-bg:#f9fafb;--color-surface:#fff;--color-border:#e5e7eb}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--color-bg);color:var(--color-text);line-height:1.5}
|
||||
nav{background:var(--color-surface);border-bottom:1px solid var(--color-border);padding:.75rem 1.5rem;display:flex;align-items:center;gap:2rem;view-transition-name:nav}
|
||||
.logo{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--color-text)}
|
||||
.logo svg{width:28px;height:25px}
|
||||
.logo span{font-family:'Roboto Slab',serif;font-weight:600;font-size:1.125rem}
|
||||
nav .links{display:flex;gap:1.5rem}
|
||||
nav .links a{color:var(--color-text-muted);text-decoration:none;font-size:.875rem;padding:.25rem 0;border-bottom:2px solid transparent}
|
||||
nav .links a:hover{color:var(--color-text)}
|
||||
nav .links a.active{color:var(--color-brand);border-bottom-color:var(--color-brand)}
|
||||
nav .graph-label{margin-left:auto;color:var(--color-text-muted);font-size:.875rem}
|
||||
main{max-width:1200px;margin:0 auto;padding:1.5rem}
|
||||
h1{font-size:1.5rem;font-weight:600;margin-bottom:1rem}
|
||||
table{width:100%;background:var(--color-surface);border:1px solid var(--color-border);border-radius:.5rem;border-collapse:collapse;font-size:.875rem}
|
||||
th,td{padding:.75rem 1rem;text-align:left;border-bottom:1px solid var(--color-border)}
|
||||
th{background:var(--color-bg);font-weight:500;color:var(--color-text-muted)}
|
||||
tr:last-child td{border-bottom:none}
|
||||
tr:hover{background:var(--color-bg)}
|
||||
td a{color:var(--color-brand);text-decoration:none}
|
||||
td a:hover{text-decoration:underline}
|
||||
.status{display:inline-block;padding:.25rem .5rem;border-radius:.25rem;font-size:.75rem;font-weight:500}
|
||||
.status-live,.status-partitionlive{background:#dcfce7;color:#166534}
|
||||
.status-building,.status-partitionbuilding{background:#ede9fe;color:#5b21b6}
|
||||
.status-failed,.status-partitionfailed{background:#fee2e2;color:#991b1b}
|
||||
.status-idle,.status-partitionidle{background:#e0f2fe;color:#075985}
|
||||
.status-tainted,.status-partitiontainted{background:#fef3c7;color:#92400e}
|
||||
.status-upstreambuilding,.status-partitionupstreambuilding{background:#fae8ff;color:#86198f}
|
||||
.status-upstreamfailed,.status-partitionupstreamfailed{background:#ffe4e6;color:#be123c}
|
||||
.pagination{display:flex;gap:.5rem;margin-top:1rem;align-items:center;justify-content:center}
|
||||
.pagination a,.pagination span{padding:.5rem .75rem;border:1px solid var(--color-border);border-radius:.25rem;text-decoration:none;color:var(--color-text);font-size:.875rem}
|
||||
.pagination a:hover{background:var(--color-bg)}
|
||||
.pagination .disabled{color:var(--color-text-muted)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro nav(active, graph_label) %}
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<svg viewBox="0 0 243 215" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M123.5 77L149.048 121.25H97.9523L123.5 77Z" fill="#F2994A"/>
|
||||
<path d="M224.772 125.035L155.772 125.035L109.52 45.3147L86.7722 45.3147L40.2722 124.463L16.7725 124.463" stroke="#333" stroke-width="20"/>
|
||||
<path d="M86.6196 5.18886L121.12 64.9444L75.2062 144.86L86.58 164.56L178.375 165.256L190.125 185.608" stroke="#333" stroke-width="20"/>
|
||||
<path d="M51.966 184.847L86.4659 125.092L178.632 124.896L190.006 105.196L144.711 25.3514L156.461 5.00002" stroke="#333" stroke-width="20"/>
|
||||
</svg>
|
||||
<span>DataBuild</span>
|
||||
</a>
|
||||
<div class="links">
|
||||
<a href="/wants"{% if active == "wants" %} class="active"{% endif %}>Wants</a>
|
||||
<a href="/partitions"{% if active == "partitions" %} class="active"{% endif %}>Partitions</a>
|
||||
<a href="/job_runs"{% if active == "job_runs" %} class="active"{% endif %}>Job Runs</a>
|
||||
</div>
|
||||
<span class="graph-label">{{ graph_label }}</span>
|
||||
</nav>
|
||||
<main>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro footer() %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{% endmacro %}
|
||||
|
||||
{% call head("Partitions - DataBuild") %}
|
||||
{% call nav("partitions", base.graph_label) %}
|
||||
|
||||
<h1>Partitions</h1>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Partition Ref</th>
|
||||
<th>Status</th>
|
||||
<th>Last Updated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for p in partitions %}
|
||||
<tr style="view-transition-name: partition-{{ loop.index }}">
|
||||
<td>
|
||||
{% if p.has_partition_ref %}
|
||||
<a href="/partitions/{{ p.partition_ref_encoded }}">{{ p.partition_ref }}</a>
|
||||
{% else %}
|
||||
<span style="color:var(--color-text-muted)">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% match p.status %}
|
||||
{% when Some with (s) %}<span class="status status-{{ s.name_lowercase }}">{{ s.name }}</span>
|
||||
{% when None %}<span class="status">Unknown</span>
|
||||
{% endmatch %}
|
||||
</td>
|
||||
<td>
|
||||
{% match p.last_updated_timestamp %}
|
||||
{% when Some with (ts) %}{{ ts }}
|
||||
{% when None %}-
|
||||
{% endmatch %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if partitions.is_empty() %}
|
||||
<tr>
|
||||
<td colspan="3" style="text-align:center;color:var(--color-text-muted)">No partitions found</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pagination">
|
||||
{% if self.has_prev() %}
|
||||
<a href="?page={{ self.prev_page() }}">Previous</a>
|
||||
{% else %}
|
||||
<span class="disabled">Previous</span>
|
||||
{% endif %}
|
||||
<span>Page {{ page + 1 }} of {{ (total_count + page_size - 1) / page_size }}</span>
|
||||
{% if self.has_next() %}
|
||||
<a href="?page={{ self.next_page() }}">Next</a>
|
||||
{% else %}
|
||||
<span class="disabled">Next</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% call footer() %}
|
||||
141
databuild/web/templates/wants/detail.html
Normal file
141
databuild/web/templates/wants/detail.html
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
{% macro head(title) %}<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@600&display=swap" rel="stylesheet">
|
||||
<title>{{ title }}</title>
|
||||
<style>
|
||||
@view-transition{navigation:auto}
|
||||
:root{--color-brand:#F2994A;--color-brand-light:#fef3e2;--color-text:#333;--color-text-muted:#6b7280;--color-bg:#f9fafb;--color-surface:#fff;--color-border:#e5e7eb}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--color-bg);color:var(--color-text);line-height:1.5}
|
||||
nav{background:var(--color-surface);border-bottom:1px solid var(--color-border);padding:.75rem 1.5rem;display:flex;align-items:center;gap:2rem;view-transition-name:nav}
|
||||
.logo{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--color-text)}
|
||||
.logo svg{width:28px;height:25px}
|
||||
.logo span{font-family:'Roboto Slab',serif;font-weight:600;font-size:1.125rem}
|
||||
nav .links{display:flex;gap:1.5rem}
|
||||
nav .links a{color:var(--color-text-muted);text-decoration:none;font-size:.875rem;padding:.25rem 0;border-bottom:2px solid transparent}
|
||||
nav .links a:hover{color:var(--color-text)}
|
||||
nav .links a.active{color:var(--color-brand);border-bottom-color:var(--color-brand)}
|
||||
nav .graph-label{margin-left:auto;color:var(--color-text-muted);font-size:.875rem}
|
||||
main{max-width:1200px;margin:0 auto;padding:1.5rem}
|
||||
h1{font-size:1.5rem;font-weight:600;margin-bottom:1rem}
|
||||
.status{display:inline-block;padding:.25rem .5rem;border-radius:.25rem;font-size:.75rem;font-weight:500}
|
||||
.status-successful,.status-wantsuccessful{background:#dcfce7;color:#166534}
|
||||
.status-building,.status-wantbuilding{background:#ede9fe;color:#5b21b6}
|
||||
.status-failed,.status-wantfailed{background:#fee2e2;color:#991b1b}
|
||||
.status-canceled,.status-wantcanceled{background:#f1f5f9;color:#475569}
|
||||
.status-new,.status-wantnew,.status-queued,.status-wantqueued{background:var(--color-brand-light);color:#92400e}
|
||||
.status-upstreambuilding,.status-wantupstreambuilding{background:#fae8ff;color:#86198f}
|
||||
.status-upstreamfailed,.status-wantupstreamfailed{background:#ffe4e6;color:#be123c}
|
||||
.detail-header{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem}
|
||||
.detail-header h1{margin-bottom:0}
|
||||
.detail-section{background:var(--color-surface);border:1px solid var(--color-border);border-radius:.5rem;padding:1rem;margin-bottom:1rem}
|
||||
.detail-section h2{font-size:.875rem;font-weight:500;color:var(--color-text-muted);margin-bottom:.75rem}
|
||||
.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1rem}
|
||||
.detail-item label{display:block;font-size:.75rem;color:var(--color-text-muted);margin-bottom:.25rem}
|
||||
.partition-list{list-style:none;font-family:monospace;font-size:.8125rem}
|
||||
.partition-list li{padding:.25rem 0}
|
||||
.partition-list a{color:var(--color-brand);text-decoration:none}
|
||||
.partition-list a:hover{text-decoration:underline}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro nav(active, graph_label) %}
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<svg viewBox="0 0 243 215" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M123.5 77L149.048 121.25H97.9523L123.5 77Z" fill="#F2994A"/>
|
||||
<path d="M224.772 125.035L155.772 125.035L109.52 45.3147L86.7722 45.3147L40.2722 124.463L16.7725 124.463" stroke="#333" stroke-width="20"/>
|
||||
<path d="M86.6196 5.18886L121.12 64.9444L75.2062 144.86L86.58 164.56L178.375 165.256L190.125 185.608" stroke="#333" stroke-width="20"/>
|
||||
<path d="M51.966 184.847L86.4659 125.092L178.632 124.896L190.006 105.196L144.711 25.3514L156.461 5.00002" stroke="#333" stroke-width="20"/>
|
||||
</svg>
|
||||
<span>DataBuild</span>
|
||||
</a>
|
||||
<div class="links">
|
||||
<a href="/wants"{% if active == "wants" %} class="active"{% endif %}>Wants</a>
|
||||
<a href="/partitions"{% if active == "partitions" %} class="active"{% endif %}>Partitions</a>
|
||||
<a href="/job_runs"{% if active == "job_runs" %} class="active"{% endif %}>Job Runs</a>
|
||||
</div>
|
||||
<span class="graph-label">{{ graph_label }}</span>
|
||||
</nav>
|
||||
<main>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro footer() %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{% endmacro %}
|
||||
|
||||
{% call head("Want - DataBuild") %}
|
||||
{% call nav("wants", base.graph_label) %}
|
||||
|
||||
<div class="detail-header" style="view-transition-name:want-header">
|
||||
<h1>Want: {{ want.want_id }}</h1>
|
||||
{% match want.status %}
|
||||
{% when Some with (s) %}<span class="status status-{{ s.name_lowercase }}">{{ s.name }}</span>
|
||||
{% when None %}
|
||||
{% endmatch %}
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<h2>Details</h2>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label>Data Timestamp</label>
|
||||
<span>{{ want.data_timestamp }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>TTL (seconds)</label>
|
||||
<span>{{ want.ttl_seconds }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>SLA (seconds)</label>
|
||||
<span>{{ want.sla_seconds }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Last Updated</label>
|
||||
<span>{{ want.last_updated_timestamp }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% match want.comment %}
|
||||
{% when Some with (c) %}
|
||||
<div class="detail-section">
|
||||
<h2>Comment</h2>
|
||||
<p>{{ c }}</p>
|
||||
</div>
|
||||
{% when None %}
|
||||
{% endmatch %}
|
||||
|
||||
<div class="detail-section">
|
||||
<h2>Requested Partitions ({{ want.partitions.len() }})</h2>
|
||||
<ul class="partition-list">
|
||||
{% for p in want.partitions %}
|
||||
<li><a href="/partitions/{{ p.partition_ref_encoded }}">{{ p.partition_ref }}</a></li>
|
||||
{% endfor %}
|
||||
{% if want.partitions.is_empty() %}
|
||||
<li style="color:var(--color-text-muted)">No partitions</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if !want.upstreams.is_empty() %}
|
||||
<div class="detail-section">
|
||||
<h2>Upstream Dependencies ({{ want.upstreams.len() }})</h2>
|
||||
<ul class="partition-list">
|
||||
{% for p in want.upstreams %}
|
||||
<li><a href="/partitions/{{ p.partition_ref_encoded }}">{{ p.partition_ref }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% call footer() %}
|
||||
127
databuild/web/templates/wants/list.html
Normal file
127
databuild/web/templates/wants/list.html
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
{% macro head(title) %}<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@600&display=swap" rel="stylesheet">
|
||||
<title>{{ title }}</title>
|
||||
<style>
|
||||
@view-transition{navigation:auto}
|
||||
:root{--color-brand:#F2994A;--color-brand-light:#fef3e2;--color-text:#333;--color-text-muted:#6b7280;--color-bg:#f9fafb;--color-surface:#fff;--color-border:#e5e7eb}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--color-bg);color:var(--color-text);line-height:1.5}
|
||||
nav{background:var(--color-surface);border-bottom:1px solid var(--color-border);padding:.75rem 1.5rem;display:flex;align-items:center;gap:2rem;view-transition-name:nav}
|
||||
.logo{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--color-text)}
|
||||
.logo svg{width:28px;height:25px}
|
||||
.logo span{font-family:'Roboto Slab',serif;font-weight:600;font-size:1.125rem}
|
||||
nav .links{display:flex;gap:1.5rem}
|
||||
nav .links a{color:var(--color-text-muted);text-decoration:none;font-size:.875rem;padding:.25rem 0;border-bottom:2px solid transparent}
|
||||
nav .links a:hover{color:var(--color-text)}
|
||||
nav .links a.active{color:var(--color-brand);border-bottom-color:var(--color-brand)}
|
||||
nav .graph-label{margin-left:auto;color:var(--color-text-muted);font-size:.875rem}
|
||||
main{max-width:1200px;margin:0 auto;padding:1.5rem}
|
||||
h1{font-size:1.5rem;font-weight:600;margin-bottom:1rem}
|
||||
table{width:100%;background:var(--color-surface);border:1px solid var(--color-border);border-radius:.5rem;border-collapse:collapse;font-size:.875rem}
|
||||
th,td{padding:.75rem 1rem;text-align:left;border-bottom:1px solid var(--color-border)}
|
||||
th{background:var(--color-bg);font-weight:500;color:var(--color-text-muted)}
|
||||
tr:last-child td{border-bottom:none}
|
||||
tr:hover{background:var(--color-bg)}
|
||||
td a{color:var(--color-brand);text-decoration:none}
|
||||
td a:hover{text-decoration:underline}
|
||||
.status{display:inline-block;padding:.25rem .5rem;border-radius:.25rem;font-size:.75rem;font-weight:500}
|
||||
.status-successful,.status-wantsuccessful{background:#dcfce7;color:#166534}
|
||||
.status-building,.status-wantbuilding{background:#ede9fe;color:#5b21b6}
|
||||
.status-failed,.status-wantfailed{background:#fee2e2;color:#991b1b}
|
||||
.status-canceled,.status-wantcanceled{background:#f1f5f9;color:#475569}
|
||||
.status-new,.status-wantnew,.status-queued,.status-wantqueued{background:var(--color-brand-light);color:#92400e}
|
||||
.status-upstreambuilding,.status-wantupstreambuilding{background:#fae8ff;color:#86198f}
|
||||
.status-upstreamfailed,.status-wantupstreamfailed{background:#ffe4e6;color:#be123c}
|
||||
.pagination{display:flex;gap:.5rem;margin-top:1rem;align-items:center;justify-content:center}
|
||||
.pagination a,.pagination span{padding:.5rem .75rem;border:1px solid var(--color-border);border-radius:.25rem;text-decoration:none;color:var(--color-text);font-size:.875rem}
|
||||
.pagination a:hover{background:var(--color-bg)}
|
||||
.pagination .disabled{color:var(--color-text-muted)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro nav(active, graph_label) %}
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<svg viewBox="0 0 243 215" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M123.5 77L149.048 121.25H97.9523L123.5 77Z" fill="#F2994A"/>
|
||||
<path d="M224.772 125.035L155.772 125.035L109.52 45.3147L86.7722 45.3147L40.2722 124.463L16.7725 124.463" stroke="#333" stroke-width="20"/>
|
||||
<path d="M86.6196 5.18886L121.12 64.9444L75.2062 144.86L86.58 164.56L178.375 165.256L190.125 185.608" stroke="#333" stroke-width="20"/>
|
||||
<path d="M51.966 184.847L86.4659 125.092L178.632 124.896L190.006 105.196L144.711 25.3514L156.461 5.00002" stroke="#333" stroke-width="20"/>
|
||||
</svg>
|
||||
<span>DataBuild</span>
|
||||
</a>
|
||||
<div class="links">
|
||||
<a href="/wants"{% if active == "wants" %} class="active"{% endif %}>Wants</a>
|
||||
<a href="/partitions"{% if active == "partitions" %} class="active"{% endif %}>Partitions</a>
|
||||
<a href="/job_runs"{% if active == "job_runs" %} class="active"{% endif %}>Job Runs</a>
|
||||
</div>
|
||||
<span class="graph-label">{{ graph_label }}</span>
|
||||
</nav>
|
||||
<main>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro footer() %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{% endmacro %}
|
||||
|
||||
{% call head("Wants - DataBuild") %}
|
||||
{% call nav("wants", base.graph_label) %}
|
||||
|
||||
<h1>Wants</h1>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Want ID</th>
|
||||
<th>Status</th>
|
||||
<th>Partitions</th>
|
||||
<th>Comment</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for want in wants %}
|
||||
<tr style="view-transition-name: want-{{ loop.index }}">
|
||||
<td><a href="/wants/{{ want.want_id }}">{{ want.want_id }}</a></td>
|
||||
<td>
|
||||
{% match want.status %}
|
||||
{% when Some with (s) %}<span class="status status-{{ s.name_lowercase }}">{{ s.name }}</span>
|
||||
{% when None %}<span class="status">Unknown</span>
|
||||
{% endmatch %}
|
||||
</td>
|
||||
<td>{{ want.partitions.len() }}</td>
|
||||
<td>{{ want.comment_display }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if wants.is_empty() %}
|
||||
<tr>
|
||||
<td colspan="4" style="text-align:center;color:var(--color-text-muted)">No wants found</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pagination">
|
||||
{% if self.has_prev() %}
|
||||
<a href="?page={{ self.prev_page() }}">Previous</a>
|
||||
{% else %}
|
||||
<span class="disabled">Previous</span>
|
||||
{% endif %}
|
||||
<span>Page {{ page + 1 }} of {{ (total_count + page_size - 1) / page_size }}</span>
|
||||
{% if self.has_next() %}
|
||||
<a href="?page={{ self.next_page() }}">Next</a>
|
||||
{% else %}
|
||||
<span class="disabled">Next</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% call footer() %}
|
||||
Loading…
Reference in a new issue