strankovani u deploys a styly pro pocitadla

This commit is contained in:
JiriUhlir
2026-05-28 13:26:58 +02:00
parent e6ffeb8fef
commit 8624352253
3 changed files with 152 additions and 10 deletions
+52 -5
View File
@@ -1,12 +1,14 @@
import html
import math
from datetime import datetime
from urllib.parse import quote
from urllib.parse import quote, urlencode
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi.responses import HTMLResponse, PlainTextResponse
from app.auth import require_user
from app.db.apps import (
count_deployments,
get_deployment,
get_deployment_filter_options,
get_deployment_stats,
@@ -19,6 +21,7 @@ router = APIRouter()
SUCCESS_STATUSES = {"ok", "success", "succeeded", "done", "deployed", "completed"}
RUNNING_STATUSES = {"running", "pending", "queued", "in_progress", "starting"}
FAILED_STATUSES = {"failed", "failure", "error", "cancelled", "canceled"}
DEFAULT_PAGE_SIZE = 20
def normalize_status(status: str | None) -> str:
@@ -115,18 +118,43 @@ def render_options(values: list[str], selected: str | None, empty_label: str) ->
return "".join(options)
def pagination_url(page_number: int, status: str, app_id: str, source: str) -> str:
params = {"page": page_number}
if status:
params["status"] = status
if app_id:
params["app_id"] = app_id
if source:
params["source"] = source
return f"/portal/deployments?{urlencode(params)}"
@router.get("/deployments", response_class=HTMLResponse)
async def deployments_page(
request: Request,
status: str = Query(""),
app_id: str = Query(""),
source: str = Query(""),
page_number: int = Query(1, alias="page", ge=1),
user=Depends(require_user),
):
selected_status = status.strip()
selected_app = app_id.strip()
selected_source = source.strip()
offset = (page_number - 1) * DEFAULT_PAGE_SIZE
total_deployments = count_deployments(
status=selected_status or None,
app_id=selected_app or None,
source=selected_source or None,
)
total_pages = max(1, math.ceil(total_deployments / DEFAULT_PAGE_SIZE))
if page_number > total_pages:
page_number = total_pages
offset = (page_number - 1) * DEFAULT_PAGE_SIZE
deployments = get_deployments(
limit=DEFAULT_PAGE_SIZE,
offset=offset,
status=selected_status or None,
app_id=selected_app or None,
source=selected_source or None,
@@ -179,6 +207,22 @@ async def deployments_page(
status_options = render_options(status_values, selected_status, "Všechny statusy")
app_options = render_options(options["apps"], selected_app, "Všechny aplikace")
source_options = render_options(options["sources"], selected_source, "Všechny zdroje")
first_item = offset + 1 if total_deployments else 0
last_item = min(offset + len(deployments), total_deployments)
previous_disabled = " disabled" if page_number <= 1 else ""
next_disabled = " disabled" if page_number >= total_pages else ""
previous_href = pagination_url(max(1, page_number - 1), selected_status, selected_app, selected_source)
next_href = pagination_url(min(total_pages, page_number + 1), selected_status, selected_app, selected_source)
pagination = f"""
<div class="pagination">
<span>Zobrazeno {first_item}-{last_item} z {total_deployments}</span>
<div class="pagination-actions">
<a class="btn btn-secondary{previous_disabled}" href="{previous_href}" aria-disabled="{str(page_number <= 1).lower()}">Předchozí</a>
<span class="page-indicator">Strana {page_number} / {total_pages}</span>
<a class="btn btn-secondary{next_disabled}" href="{next_href}" aria-disabled="{str(page_number >= total_pages).lower()}">Další</a>
</div>
</div>
"""
return page(
"Nasazení",
@@ -191,10 +235,10 @@ async def deployments_page(
</div>
<div class="stats-grid">
<div class="stat-card"><span>Total deploys</span><strong>{stats.get("total") or 0}</strong></div>
<div class="stat-card"><span>Failed deploys</span><strong>{stats.get("failed") or 0}</strong></div>
<div class="stat-card"><span>Running deploys</span><strong>{stats.get("running") or 0}</strong></div>
<div class="stat-card"><span>Success rate</span><strong>{stats.get("success_rate") or 0}%</strong></div>
<div class="stat-card stat-total"><span>Total deploys</span><strong>{stats.get("total") or 0}</strong></div>
<div class="stat-card stat-danger"><span>Failed deploys</span><strong>{stats.get("failed") or 0}</strong></div>
<div class="stat-card stat-warning"><span>Running deploys</span><strong>{stats.get("running") or 0}</strong></div>
<div class="stat-card stat-success"><span>Success rate</span><strong>{stats.get("success_rate") or 0}%</strong></div>
</div>
<div class="card">
@@ -203,6 +247,7 @@ async def deployments_page(
<select name="status">{status_options}</select>
<select name="app_id">{app_options}</select>
<select name="source">{source_options}</select>
<input type="hidden" name="page" value="1">
<button type="submit">Filtrovat</button>
<a class="btn btn-secondary" href="/portal/deployments">Reset</a>
</form>
@@ -210,6 +255,7 @@ async def deployments_page(
<div class="card">
<h2>Nasazení</h2>
{pagination}
<table>
<tr>
<th>ID</th>
@@ -224,6 +270,7 @@ async def deployments_page(
</tr>
{rows}
</table>
{pagination}
</div>
""",
user=user,