UI a menu a FA
This commit is contained in:
+15
-10
@@ -253,7 +253,7 @@ def render_health_dot(status: str | None, checked_at: str | None) -> str:
|
||||
|
||||
@router.get("/")
|
||||
def portal_home(user=Depends(require_user)):
|
||||
return RedirectResponse(url="/portal/operations", status_code=303)
|
||||
return RedirectResponse(url="/portal/apps", status_code=303)
|
||||
|
||||
|
||||
@router.get("/apps", response_class=HTMLResponse)
|
||||
@@ -373,21 +373,26 @@ def apps_page(
|
||||
</div>
|
||||
</details>
|
||||
</td>
|
||||
<td class="actions-cell service-actions">
|
||||
<a class="btn" href="/portal/apps/{app_url_id}">Detail</a>
|
||||
<a class="btn btn-secondary" href="/portal/deployments?app_id={app_url_id}">Nasazení</a>
|
||||
<details class="action-menu">
|
||||
<summary aria-label="Další akce">...</summary>
|
||||
<div class="action-menu-panel">
|
||||
<td class="action-icons-cell">
|
||||
<div class="action-icons">
|
||||
<a class="icon-action" href="/portal/apps/{app_url_id}" title="Detail služby" aria-label="Detail služby">
|
||||
<i class="fa-solid fa-circle-info" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a class="icon-action" href="/portal/deployments?app_id={app_url_id}" title="Nasazení" aria-label="Nasazení">
|
||||
<i class="fa-solid fa-rocket" aria-hidden="true"></i>
|
||||
</a>
|
||||
<form method="post" action="/portal/apps/{app_url_id}/redeploy" onsubmit="return confirm('Spustit nové nasazení služby {app_id}?');">
|
||||
<button type="submit" class="menu-action">Nasadit znovu</button>
|
||||
<button type="submit" class="icon-action" title="Nasadit znovu" aria-label="Nasadit znovu">
|
||||
<i class="fa-solid fa-arrows-rotate" aria-hidden="true"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form method="post" action="/portal/delete-app" onsubmit="return confirm('Smazat {app_id}? Tím se odstraní kontejner, image, workspace, záznam v katalogu a Gitea repozitář.');">
|
||||
<input type="hidden" name="app_id" value="{app_id}">
|
||||
<button type="submit" class="menu-action menu-action-danger">Smazat</button>
|
||||
<button type="submit" class="icon-action icon-action-danger" title="Smazat" aria-label="Smazat">
|
||||
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
+4
-4
@@ -33,7 +33,7 @@ GOOGLE_USERINFO_URL = "https://openidconnect.googleapis.com/v1/userinfo"
|
||||
@router.get("/login", response_class=HTMLResponse)
|
||||
def login_form(request: Request):
|
||||
if current_user(request):
|
||||
return RedirectResponse(url="/portal/operations", status_code=303)
|
||||
return RedirectResponse(url="/portal/apps", status_code=303)
|
||||
|
||||
return _render_login()
|
||||
|
||||
@@ -58,13 +58,13 @@ def login(request: Request, username: str = Form(...), password: str = Form(...)
|
||||
metadata={"username": user.get("username")},
|
||||
)
|
||||
|
||||
return RedirectResponse(url="/portal/operations", status_code=303)
|
||||
return RedirectResponse(url="/portal/apps", status_code=303)
|
||||
|
||||
|
||||
@router.get("/auth/gitea/login")
|
||||
def gitea_login(request: Request):
|
||||
if current_user(request):
|
||||
return RedirectResponse(url="/portal/operations", status_code=303)
|
||||
return RedirectResponse(url="/portal/apps", status_code=303)
|
||||
return _render_login("Gitea login is disabled. Use Google sign in.")
|
||||
|
||||
gitea_url = get_gitea_public_url()
|
||||
@@ -148,7 +148,7 @@ def gitea_callback(request: Request, code: str = "", state: str = "", error: str
|
||||
@router.get("/auth/google/login")
|
||||
def google_login(request: Request):
|
||||
if current_user(request):
|
||||
return RedirectResponse(url="/portal/operations", status_code=303)
|
||||
return RedirectResponse(url="/portal/apps", status_code=303)
|
||||
if get_auth_mode() == "local":
|
||||
return _render_login("Google login is not enabled in local auth mode.")
|
||||
|
||||
|
||||
@@ -816,6 +816,62 @@ input[readonly] {
|
||||
color: var(--danger-dark);
|
||||
}
|
||||
|
||||
.action-icons-cell {
|
||||
width: 1%;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.action-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action-icons form {
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.icon-action {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
min-height: 38px;
|
||||
padding: 0;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
color: var(--secondary);
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.icon-action:hover,
|
||||
.icon-action:focus-visible {
|
||||
background: #eef6f9;
|
||||
color: var(--secondary-dark);
|
||||
border-color: var(--secondary);
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.icon-action-danger {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.icon-action-danger:hover,
|
||||
.icon-action-danger:focus-visible {
|
||||
background: var(--danger-bg);
|
||||
color: var(--danger-dark);
|
||||
border-color: var(--danger);
|
||||
}
|
||||
|
||||
.running-row td {
|
||||
background: var(--warning-bg);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2023 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}
|
||||
Binary file not shown.
Binary file not shown.
+21
-18
@@ -12,33 +12,34 @@ def page(title: str, body: str, user=None) -> str:
|
||||
if user:
|
||||
username = html.escape(user.get("username", ""))
|
||||
display_name = html.escape(user.get("display_name") or user.get("username", ""))
|
||||
admin_links = ""
|
||||
admin_menu = ""
|
||||
if (user.get("role") or "").lower() == "admin":
|
||||
admin_links = '<a href="/portal/admin/users">Users</a>'
|
||||
nav = f"""
|
||||
<nav>
|
||||
<a class="nav-link" href="/portal/operations">Přehled</a>
|
||||
<a class="nav-link" href="/portal/apps">Služby</a>
|
||||
admin_menu = """
|
||||
<div class="nav-menu">
|
||||
<button type="button" class="nav-menu-button">Provoz</button>
|
||||
<button type="button" class="nav-menu-button">Admin</button>
|
||||
<div class="nav-menu-panel">
|
||||
<a href="/portal/jobs">Úlohy</a>
|
||||
<a href="/portal/operations">Přehled</a>
|
||||
<a href="/portal/apps">Služby</a>
|
||||
<a href="/portal/jobs">Úlohy</a>
|
||||
<a href="/portal/scheduled-scripts">Plánované skripty</a>
|
||||
<a href="/portal/migration-readiness">Migration Readiness</a>
|
||||
<a href="/portal/alerting/rules">Alerting</a>
|
||||
<a href="/portal/deployments">Nasazení</a>
|
||||
<a href="/portal/migration-readiness">Migration Readiness</a>
|
||||
<a href="/portal/deployments">Nasazení</a>
|
||||
<a href="/portal/incidents">Incidenty</a>
|
||||
<a href="/portal/workers">Workery</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-menu">
|
||||
<button type="button" class="nav-menu-button">Správa</button>
|
||||
<div class="nav-menu-panel">
|
||||
{admin_links}
|
||||
<a href="/portal/admin/users">Users</a>
|
||||
<a href="/portal/audit">Audit</a>
|
||||
<a href="/portal/backups">Zálohy</a>
|
||||
<a href="/portal/backups">Zálohy</a>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
nav = f"""
|
||||
<nav>
|
||||
<a class="nav-link" href="/portal/apps">Služby</a>
|
||||
<a class="nav-link" href="/portal/jobs">Úlohy</a>
|
||||
<a class="nav-link" href="/portal/scheduled-scripts">Plánované skripty</a>
|
||||
<a class="nav-link" href="/portal/alerting/rules">Alerting</a>
|
||||
{admin_menu}
|
||||
</nav>
|
||||
"""
|
||||
user_panel = f"""
|
||||
@@ -55,12 +56,14 @@ def page(title: str, body: str, user=None) -> str:
|
||||
<html>
|
||||
<head>
|
||||
<title>{APP_NAME} - {html.escape(title)}</title>
|
||||
<link rel="stylesheet" href="{PORTAL_PREFIX}/static/vendor/fontawesome/css/fontawesome.min.css">
|
||||
<link rel="stylesheet" href="{PORTAL_PREFIX}/static/vendor/fontawesome/css/solid.min.css">
|
||||
<link rel="stylesheet" href="{PORTAL_PREFIX}/static/styles.css">
|
||||
<script src="{PORTAL_PREFIX}/static/portal.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a class="brand" href="/portal/operations" aria-label="{APP_NAME}">
|
||||
<a class="brand" href="/portal/apps" aria-label="{APP_NAME}">
|
||||
<img src="{PORTAL_PREFIX}/static/csbot-logo.svg" alt="CSBOT">
|
||||
<span>{APP_NAME}</span>
|
||||
</a>
|
||||
|
||||
Reference in New Issue
Block a user