6.0 KB
raw
{% extends "base.html" %}
{% block extra_css %}
<link rel="stylesheet" href="{{ vite_asset('static_src/properties/index.js', 'css') }}">
{% endblock %}
{% block breadcrumbs %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ title }}</li>
</ol>
</nav>
{% endblock %}
{% block main %}
<div class="container my-4">
<div class="row align-items-center g-3 mb-4">
<div class="col-md-5">
<div class="section-label mb-1">operator</div>
<h1 class="fw-bolder text-white mb-0" style="letter-spacing: -0.01em;">{{ title }}</h1>
<p class="text-muted mb-0 small mt-1">Every URL you've registered, current probe state, and audit signals.</p>
</div>
<div class="col-md-7">
<div class="dashboard-toolbar">
<form method="get" class="search-form flex-grow-1">
<input type="text" class="form-control search-input" name="q" id="id_search" placeholder="grep properties..."{% if q %} value="{{ q }}"{% endif %} />
</form>
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePropertyAdd" aria-expanded="false" aria-controls="collapsePropertyAdd">
+ new
</button>
</div>
</div>
</div>
<div class="collapse mb-4" id="collapsePropertyAdd">
<div class="bg-surface border-subtle rounded border p-3">
<div class="section-label mb-2">register property</div>
<p class="text-muted small mb-3">Any public URL. Probe begins within 3 minutes.</p>
<form method="POST" action="{{ url_for('properties') }}" class="dashboard-toolbar">
<input type="url" name="url" id="id_url" class="form-control flex-grow-1" placeholder="https://example.com" required>
<button type="submit" class="btn btn-primary">Add →</button>
</form>
</div>
</div>
<div class="section-label mb-2">registered properties</div>
{% for property in properties %}
<div class="property-row {% if property.current_status != 200 %}is-down{% endif %}">
<div class="pr-main">
<div class="pr-title">
<span class="status-dot {% if property.current_status == 200 %}is-up{% else %}is-down{% endif %}" aria-hidden="true"></span>
<a href="/{{ property.id }}" class="text-truncate">{{ property.name }}</a>
</div>
<div class="pr-url">{{ property.url }}</div>
<div class="uptime-strip" aria-label="Recent checks">
{% for tick in property.recent_tick_stream %}
<span class="uptime-tick is-{{ tick }}"></span>
{% else %}
{% for i in range(30) %}<span class="uptime-tick is-none"></span>{% endfor %}
{% endfor %}
</div>
</div>
<div class="pr-side">
<div class="pr-meta">
<div class="pr-meta-cell">
<span class="pr-meta-k">status</span>
{% if property.current_status == 200 %}
<span class="chip chip-ok">ok · 200</span>
{% else %}
<span class="chip chip-down">down · {{ property.current_status }}</span>
{% endif %}
</div>
<div class="pr-meta-cell">
<span class="pr-meta-k">uptime</span>
{% if property.recent_uptime_pct is not none %}
{% if property.recent_uptime_pct >= 99 %}
<span class="chip chip-ok">{{ property.recent_uptime_pct }}%</span>
{% elif property.recent_uptime_pct < 95 %}
<span class="chip chip-down">{{ property.recent_uptime_pct }}%</span>
{% else %}
<span class="chip chip-warn">{{ property.recent_uptime_pct }}%</span>
{% endif %}
{% else %}
<span class="chip chip-muted">—</span>
{% endif %}
</div>
<div class="pr-meta-cell">
<span class="pr-meta-k">sec</span>
{% if property.has_security_issue %}
<span class="chip chip-warn">issues</span>
{% else %}
<span class="chip chip-ok">ok</span>
{% endif %}
</div>
<div class="pr-meta-cell">
<span class="pr-meta-k">seo</span>
{% set insights = property.crawler_insights | length %}
{% if insights > 100 %}
<span class="chip chip-down">{{ insights }}</span>
{% elif insights > 25 %}
<span class="chip chip-warn">{{ insights }}</span>
{% else %}
<span class="chip chip-ok">{{ insights }}</span>
{% endif %}
</div>
<div class="pr-meta-cell">
<span class="pr-meta-k">lh</span>
{% if property.avg_lighthouse_score %}
{% if property.avg_lighthouse_score >= 90 %}
<span class="chip chip-ok">{{ property.avg_lighthouse_score }}%</span>
{% elif property.avg_lighthouse_score >= 80 %}
<span class="chip chip-warn">{{ property.avg_lighthouse_score }}%</span>
{% else %}
<span class="chip chip-down">{{ property.avg_lighthouse_score }}%</span>
{% endif %}
{% else %}
<span class="chip chip-muted">—</span>
{% endif %}
</div>
</div>
<div class="pr-actions">
{% if not property.is_protected %}
<form method="POST" action="/properties/{{ property.id }}/delete" class="d-inline" onsubmit="return confirm('Delete {{ property.url }} and all its checks?');">
<button type="submit" class="btn btn-sm btn-outline-danger">Delete</button>
</form>
{% endif %}
<a href="/{{ property.id }}" class="btn btn-sm btn-outline-light">View →</a>
</div>
</div>
</div>
{% else %}
<div class="text-center py-5 text-muted">
<div class="section-label mb-3" style="justify-content:center;">no properties yet</div>
<p class="mb-0 small">Click <strong>+ new</strong> above to register your first URL.</p>
</div>
{% endfor %}
</div>
{% endblock %}