fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
@@ -2,16 +2,14 @@
|
||||
|
||||
<div class="admin-page">
|
||||
<div class="page-header">
|
||||
<h1>{{ title }}</h1>
|
||||
<if condition="{{ subtitle }}">
|
||||
<p class="subtitle">{{ subtitle }}</p>
|
||||
</if>
|
||||
<h1>{{$title}}</h1>
|
||||
<p class="subtitle" if="{{$subtitle}}">{{$subtitle}}</p>
|
||||
</div>
|
||||
|
||||
<div class="form-container">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{ form }}
|
||||
{{$form}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,34 +2,26 @@
|
||||
|
||||
<div class="admin-page">
|
||||
<div class="page-header">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1>{{$title}}</h1>
|
||||
|
||||
<div class="page-actions">
|
||||
<if condition="{{ actions }}">
|
||||
<for items="{{ actions }}" value="action">
|
||||
<a href="{{ action.url }}" class="btn btn-primary">
|
||||
<if condition="{{ action.icon }}">
|
||||
<i class="icon-{{ action.icon }}"></i>
|
||||
</if>
|
||||
{{ action.label }}
|
||||
</a>
|
||||
</for>
|
||||
</if>
|
||||
<a href="{{$action['url']}}" class="btn btn-primary" foreach="$actions as $action">
|
||||
<i class="icon-{{$action['icon']}}" if="{{$action['icon']}}"></i>
|
||||
{{$action['label']}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<if condition="{{ searchable }}">
|
||||
<div class="table-controls">
|
||||
<input type="text"
|
||||
class="form-control search-input"
|
||||
data-table-search="{{ resource }}"
|
||||
placeholder="Search...">
|
||||
</div>
|
||||
</if>
|
||||
<div class="table-controls" if="{{$searchable}}">
|
||||
<input type="text"
|
||||
class="form-control search-input"
|
||||
data-table-search="{{$resource}}"
|
||||
placeholder="Search...">
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
{{ table }}
|
||||
{{$table}}
|
||||
</div>
|
||||
|
||||
<div class="pagination-container" data-table-pagination="{{ resource }}"></div>
|
||||
<div class="pagination-container" data-table-pagination="{{$resource}}"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="de" data-theme="{theme ?? 'auto'}">
|
||||
<html lang="de" data-theme="{{ $theme ?? 'auto' }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>{title} | Admin Panel</title>
|
||||
<meta name="description" content="{description}">
|
||||
<title>{{ $title }} | Admin Panel</title>
|
||||
<meta name="description" content="{{ $description }}">
|
||||
<meta property="og:type" content="website">
|
||||
|
||||
<!-- Theme Meta -->
|
||||
@@ -27,14 +27,14 @@
|
||||
<!-- Admin Layout Grid -->
|
||||
<div class="admin-layout">
|
||||
<!-- Sidebar Navigation -->
|
||||
<x-admin-sidebar currentPath="{current_path}" />
|
||||
<x-admin-sidebar currentPath="{{ $current_path }}" navigation-menu='{{ json_encode($navigation_menu) }}' />
|
||||
|
||||
<!-- Header with Search & User Menu -->
|
||||
<x-admin-header pageTitle="{page_title}" />
|
||||
<x-admin-header pageTitle="{{ $page_title }}" />
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="admin-content" id="main-content" role="main">
|
||||
{content}
|
||||
{{ $content }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,31 +5,30 @@
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Page Views</h3>
|
||||
<p><strong>Today:</strong> {{ today_page_views }}</p>
|
||||
<p><strong>This Week:</strong> {{ week_page_views }}</p>
|
||||
<p><strong>This Month:</strong> {{ month_page_views }}</p>
|
||||
<p><strong>Today:</strong> {{$today_page_views}}</p>
|
||||
<p><strong>This Week:</strong> {{$week_page_views}}</p>
|
||||
<p><strong>This Month:</strong> {{$month_page_views}}</p>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>Unique Visitors</h3>
|
||||
<p><strong>Today:</strong> {{ today_visitors }}</p>
|
||||
<p><strong>This Week:</strong> {{ week_visitors }}</p>
|
||||
<p><strong>This Month:</strong> {{ month_visitors }}</p>
|
||||
<p><strong>Today:</strong> {{$today_visitors}}</p>
|
||||
<p><strong>This Week:</strong> {{$week_visitors}}</p>
|
||||
<p><strong>This Month:</strong> {{$month_visitors}}</p>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>Performance</h3>
|
||||
<p><strong>Avg. Load Time:</strong> {{ avg_load_time }}</p>
|
||||
<p><strong>Bounce Rate:</strong> {{ bounce_rate }}</p>
|
||||
<p><strong>Session Duration:</strong> {{ avg_session_duration }}</p>
|
||||
<p><strong>Avg. Load Time:</strong> {{$avg_load_time}}</p>
|
||||
<p><strong>Bounce Rate:</strong> {{$bounce_rate}}</p>
|
||||
<p><strong>Session Duration:</strong> {{$avg_session_duration}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card full-width">
|
||||
<h3>Top Pages</h3>
|
||||
<if condition="{{ top_pages }}">
|
||||
<table>
|
||||
<table if="{{$top_pages}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
@@ -39,44 +38,37 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<for items="{{ top_pages }}" value="page">
|
||||
<tr>
|
||||
<td>{{ page.path }}</td>
|
||||
<td>{{ page.views }}</td>
|
||||
<td>{{ page.unique_visitors }}</td>
|
||||
<td>{{ page.avg_time }}</td>
|
||||
<tr foreach="$top_pages as $page">
|
||||
<td>{{$page['path']}}</td>
|
||||
<td>{{$page['views']}}</td>
|
||||
<td>{{$page['unique_visitors']}}</td>
|
||||
<td>{{$page['avg_time']}}</td>
|
||||
</tr>
|
||||
</for>
|
||||
</tbody>
|
||||
</table>
|
||||
<else>
|
||||
<p>No analytics data available yet.</p>
|
||||
<p>The analytics system will start collecting data once visitors start using your website.</p>
|
||||
</if>
|
||||
<div if="{{!$top_pages}}">
|
||||
<p>No analytics data available yet.</p>
|
||||
<p>The analytics system will start collecting data once visitors start using your website.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Traffic Sources</h3>
|
||||
<if condition="{{ traffic_sources }}">
|
||||
<ul style="list-style: none; padding: 0;">
|
||||
<for items="{{ traffic_sources }}" key="source" value="percentage">
|
||||
<li style="margin: 8px 0;">
|
||||
<strong>{{ source }}:</strong> {{ percentage }}%
|
||||
<ul style="list-style: none; padding: 0;" if="{{$traffic_sources}}">
|
||||
<li style="margin: 8px 0;" foreach="$traffic_sources as $source => $percentage">
|
||||
<strong>{{$source}}:</strong> {{$percentage}}%
|
||||
</li>
|
||||
</for>
|
||||
</ul>
|
||||
<else>
|
||||
<p>No traffic source data available.</p>
|
||||
</if>
|
||||
<p if="{{!$traffic_sources}}">No traffic source data available.</p>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>System Status</h3>
|
||||
<p><strong>Analytics Status:</strong> <span style="color: var(--success);">Active</span></p>
|
||||
<p><strong>Data Collection:</strong> <span style="color: var(--success);">Running</span></p>
|
||||
<p><strong>Last Update:</strong> {{ last_update }}</p>
|
||||
<p><strong>Last Update:</strong> {{$last_update}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -7,31 +7,31 @@
|
||||
<!-- Cache Overview Cards -->
|
||||
<div class="admin-grid admin-grid--4col">
|
||||
<div class="admin-card metric-card">
|
||||
<div class="metric-card__value">{{ hit_rate }}%</div>
|
||||
<div class="metric-card__value">{{$hit_rate}}%</div>
|
||||
<div class="metric-card__label">Hit Rate</div>
|
||||
<div class="metric-card__change metric-card__change--{{ hit_rate >= 80 ? 'positive' : 'negative' }}">
|
||||
{{ hit_rate >= 80 ? 'Excellent' : (hit_rate >= 60 ? 'Good' : 'Needs Improvement') }}
|
||||
<div class="metric-card__change metric-card__change--{{$hit_rate >= 80 ? 'positive' : 'negative'}}">
|
||||
{{$hit_rate >= 80 ? 'Excellent' : ($hit_rate >= 60 ? 'Good' : 'Needs Improvement')}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card metric-card">
|
||||
<div class="metric-card__value">{{ total_operations }}</div>
|
||||
<div class="metric-card__value">{{$total_operations}}</div>
|
||||
<div class="metric-card__label">Total Operations</div>
|
||||
<div class="metric-card__change">Since startup</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card metric-card">
|
||||
<div class="metric-card__value">{{ avg_latency_ms }}ms</div>
|
||||
<div class="metric-card__value">{{$avg_latency_ms}}ms</div>
|
||||
<div class="metric-card__label">Average Latency</div>
|
||||
<div class="metric-card__change metric-card__change--{{ avg_latency_ms <= 5 ? 'positive' : 'negative' }}">
|
||||
{{ avg_latency_ms <= 5 ? 'Fast' : 'Slow' }}
|
||||
<div class="metric-card__change metric-card__change--{{$avg_latency_ms <= 5 ? 'positive' : 'negative'}}">
|
||||
{{$avg_latency_ms <= 5 ? 'Fast' : 'Slow'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card metric-card">
|
||||
<div class="metric-card__value">{{ total_size_mb }}MB</div>
|
||||
<div class="metric-card__value">{{$total_size_mb}}MB</div>
|
||||
<div class="metric-card__label">Total Cache Size</div>
|
||||
<div class="metric-card__change">{{ active_drivers }} active drivers</div>
|
||||
<div class="metric-card__change">{{$active_drivers}} active drivers</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -39,53 +39,47 @@
|
||||
<div class="admin-card">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">Cache Health Status</h3>
|
||||
<span class="admin-table__status admin-table__status--{{ health_status === 'healthy' ? 'success' : (health_status === 'warning' ? 'warning' : 'error') }}">
|
||||
<span class="status-indicator status-indicator--{{ health_status === 'healthy' ? 'success' : (health_status === 'warning' ? 'warning' : 'error') }}"></span>
|
||||
{{ health_status|upper }}
|
||||
<span class="admin-table__status admin-table__status--{{$health_status === 'healthy' ? 'success' : ($health_status === 'warning' ? 'warning' : 'error')}}">
|
||||
<span class="status-indicator status-indicator--{{$health_status === 'healthy' ? 'success' : ($health_status === 'warning' ? 'warning' : 'error')}}"></span>
|
||||
{{strtoupper($health_status)}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<p>Cache system efficiency rating: <strong>{{ efficiency_rating }}</strong></p>
|
||||
<if condition="{{ recommendations_count > 0 }}">
|
||||
<p class="text-warning">{{ recommendations_count }} recommendations available for optimization.</p>
|
||||
</if>
|
||||
<p>Cache system efficiency rating: <strong>{{$efficiency_rating}}</strong></p>
|
||||
<p class="text-warning" if="{{$recommendations_count > 0}}">{{$recommendations_count}} recommendations available for optimization.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Driver Statistics -->
|
||||
<if condition="{{ active_drivers > 0 }}">
|
||||
<div class="admin-card">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">Driver Statistics</h3>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div class="admin-table-wrapper">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Driver</th>
|
||||
<th>Hit Rate</th>
|
||||
<th>Operations</th>
|
||||
<th>Avg Latency</th>
|
||||
<th>Size (MB)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<for items="{{ driver_stats }}" key="driver" value="stats">
|
||||
<tr>
|
||||
<td class="font-mono">{{ driver }}</td>
|
||||
<td>{{ stats.hit_rate }}%</td>
|
||||
<td>{{ stats.operations }}</td>
|
||||
<td>{{ stats.avg_latency }}ms</td>
|
||||
<td>{{ stats.size }}MB</td>
|
||||
</tr>
|
||||
</for>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="admin-card" if="{{$active_drivers > 0}}">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">Driver Statistics</h3>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div class="admin-table-wrapper">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Driver</th>
|
||||
<th>Hit Rate</th>
|
||||
<th>Operations</th>
|
||||
<th>Avg Latency</th>
|
||||
<th>Size (MB)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr foreach="$driver_stats as $driver => $stats">
|
||||
<td class="font-mono">{{$driver}}</td>
|
||||
<td>{{$stats['hit_rate']}}%</td>
|
||||
<td>{{$stats['operations']}}</td>
|
||||
<td>{{$stats['avg_latency']}}ms</td>
|
||||
<td>{{$stats['size']}}MB</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</if>
|
||||
</div>
|
||||
|
||||
<!-- Real-time Metrics (JavaScript will update these) -->
|
||||
<div class="admin-card">
|
||||
|
||||
@@ -13,30 +13,21 @@
|
||||
</li>
|
||||
|
||||
<!-- LinkCollection Items -->
|
||||
<if condition="breadcrumbs && breadcrumbs.count() > 0">
|
||||
<for items="breadcrumbs" as="link" index="index">
|
||||
<!-- Separator -->
|
||||
<li class="admin-breadcrumbs__separator" aria-hidden="true">
|
||||
<svg class="admin-breadcrumbs__separator-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="admin-breadcrumbs__separator" aria-hidden="true" foreach="$breadcrumbs as $link">
|
||||
<svg class="admin-breadcrumbs__separator-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</li>
|
||||
|
||||
<!-- Breadcrumb Item -->
|
||||
<li class="admin-breadcrumbs__item">
|
||||
<!-- Check if current page (AccessibleLink with aria-current) -->
|
||||
<if condition="link.hasAttribute('aria-current')">
|
||||
<span class="admin-breadcrumbs__current" aria-current="page">
|
||||
{link.text}
|
||||
</span>
|
||||
<else>
|
||||
<a href="{link.href}" class="admin-breadcrumbs__link">
|
||||
{link.text}
|
||||
</a>
|
||||
</else>
|
||||
</if>
|
||||
</li>
|
||||
</for>
|
||||
</if>
|
||||
<!-- Breadcrumb Item -->
|
||||
<li class="admin-breadcrumbs__item" foreach="$breadcrumbs as $link">
|
||||
<!-- Check if current page (AccessibleLink with aria-current) -->
|
||||
<span class="admin-breadcrumbs__current" aria-current="page" if="{{$link->hasAttribute('aria-current')}}">
|
||||
{{$link->text}}
|
||||
</span>
|
||||
<a href="{{$link->href}}" class="admin-breadcrumbs__link" if="{{!$link->hasAttribute('aria-current')}}">
|
||||
{{$link->text}}
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="admin-nav__section">
|
||||
<ul class="admin-nav__list" role="list">
|
||||
<li class="admin-nav__item">
|
||||
<a href="/admin" class="admin-nav__link" aria-current="{current_path === '/admin' ? 'page' : null}">
|
||||
<a href="/admin" class="admin-nav__link" aria-current="{{$current_path === '/admin' ? 'page' : null}}">
|
||||
<svg class="admin-nav__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
|
||||
</svg>
|
||||
@@ -30,7 +30,7 @@
|
||||
<h2 class="admin-nav__section-title">Content</h2>
|
||||
<ul class="admin-nav__list" role="list">
|
||||
<li class="admin-nav__item">
|
||||
<a href="/admin/pages" class="admin-nav__link" aria-current="{current_path === '/admin/pages' ? 'page' : null}">
|
||||
<a href="/admin/pages" class="admin-nav__link" aria-current="{{$current_path === '/admin/pages' ? 'page' : null}}">
|
||||
<svg class="admin-nav__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
@@ -38,7 +38,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="admin-nav__item">
|
||||
<a href="/admin/media" class="admin-nav__link" aria-current="{current_path === '/admin/media' ? 'page' : null}">
|
||||
<a href="/admin/media" class="admin-nav__link" aria-current="{{$current_path === '/admin/media' ? 'page' : null}}">
|
||||
<svg class="admin-nav__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
@@ -53,7 +53,7 @@
|
||||
<h2 class="admin-nav__section-title">System</h2>
|
||||
<ul class="admin-nav__list" role="list">
|
||||
<li class="admin-nav__item">
|
||||
<a href="/admin/users" class="admin-nav__link" aria-current="{current_path === '/admin/users' ? 'page' : null}">
|
||||
<a href="/admin/users" class="admin-nav__link" aria-current="{{$current_path === '/admin/users' ? 'page' : null}}">
|
||||
<svg class="admin-nav__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
|
||||
</svg>
|
||||
@@ -61,7 +61,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="admin-nav__item">
|
||||
<a href="/admin/settings" class="admin-nav__link" aria-current="{current_path === '/admin/settings' ? 'page' : null}">
|
||||
<a href="/admin/settings" class="admin-nav__link" aria-current="{{$current_path === '/admin/settings' ? 'page' : null}}">
|
||||
<svg class="admin-nav__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
@@ -77,15 +77,15 @@
|
||||
<div class="admin-sidebar__footer">
|
||||
<a href="/admin/profile" class="admin-sidebar__user">
|
||||
<img
|
||||
src="{user.avatar ?? '/assets/default-avatar.png'}"
|
||||
alt="{user.name}"
|
||||
src="{{$user['avatar'] ?? '/assets/default-avatar.png'}}"
|
||||
alt="{{$user['name']}}"
|
||||
class="admin-sidebar__avatar"
|
||||
width="32"
|
||||
height="32"
|
||||
/>
|
||||
<div class="admin-sidebar__user-info">
|
||||
<span class="admin-sidebar__user-name">{user.name}</span>
|
||||
<span class="admin-sidebar__user-role">{user.role}</span>
|
||||
<span class="admin-sidebar__user-name">{{$user['name']}}</span>
|
||||
<span class="admin-sidebar__user-role">{{$user['role']}}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Uptime</span>
|
||||
<span class="admin-stat-list__value">{{ uptime_formatted }}</span>
|
||||
<span class="admin-stat-list__value">{{$uptime_formatted}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Version</span>
|
||||
<span class="admin-stat-list__value">{{ $framework_version }}</span>
|
||||
<span class="admin-stat-list__value">{{$framework_version}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,15 +43,15 @@
|
||||
<div class="admin-stat-list">
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Memory Usage</span>
|
||||
<span class="admin-stat-list__value">{{ memory_usage_formatted }}</span>
|
||||
<span class="admin-stat-list__value">{{$memory_usage_formatted}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Peak Memory</span>
|
||||
<span class="admin-stat-list__value">{{ peak_memory_formatted }}</span>
|
||||
<span class="admin-stat-list__value">{{$peak_memory_formatted}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Load Average</span>
|
||||
<span class="admin-stat-list__value">{{ load_average }}</span>
|
||||
<span class="admin-stat-list__value">{{$load_average}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,11 +69,11 @@
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Pool Size</span>
|
||||
<span class="admin-stat-list__value">{{ db_pool_size }}</span>
|
||||
<span class="admin-stat-list__value">{{$db_pool_size}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Active Connections</span>
|
||||
<span class="admin-stat-list__value">{{ db_active_connections }}</span>
|
||||
<span class="admin-stat-list__value">{{$db_active_connections}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,11 +90,11 @@
|
||||
<div class="admin-stat-list">
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Hit Rate</span>
|
||||
<span class="admin-stat-list__value">{{ cache_hit_rate }}</span>
|
||||
<span class="admin-stat-list__value">{{$cache_hit_rate}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Total Operations</span>
|
||||
<span class="admin-stat-list__value">{{ cache_total_operations }}</span>
|
||||
<span class="admin-stat-list__value">{{$cache_total_operations}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Status</span>
|
||||
@@ -112,15 +112,15 @@
|
||||
<div class="admin-stat-list">
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Requests Today</span>
|
||||
<span class="admin-stat-list__value">{{ requests_today }}</span>
|
||||
<span class="admin-stat-list__value">{{$requests_today}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Errors</span>
|
||||
<span class="admin-stat-list__value">{{ errors_today }}</span>
|
||||
<span class="admin-stat-list__value">{{$errors_today}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Last Deployment</span>
|
||||
<span class="admin-stat-list__value">{{ last_deployment }}</span>
|
||||
<span class="admin-stat-list__value">{{$last_deployment}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,9 +132,9 @@
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div class="admin-cluster">
|
||||
<x-a href="{{ clear_cache_url }}" class="admin-btn admin-btn--primary">Clear Cache</x-a>
|
||||
<x-a href="{{ logs_url }}" class="admin-btn admin-btn--secondary">View Logs</x-a>
|
||||
<x-a href="{{ migrations_url }}" class="admin-btn admin-btn--accent">Migrations</x-a>
|
||||
<x-a href="{{$clear_cache_url}}" class="admin-btn admin-btn--primary">Clear Cache</x-a>
|
||||
<x-a href="{{$logs_url}}" class="admin-btn admin-btn--secondary">View Logs</x-a>
|
||||
<x-a href="{{$migrations_url}}" class="admin-btn admin-btn--accent">Migrations</x-a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
33
src/Application/Admin/templates/database/browser.view.php
Normal file
33
src/Application/Admin/templates/database/browser.view.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="admin-page">
|
||||
<div class="page-header">
|
||||
<h1>{{ $title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Database Information</h3>
|
||||
<p><strong>Name:</strong> {{$database['name']}}</p>
|
||||
<p if="{{$database['charset']}}"><strong>Charset:</strong> {{$database['charset']}}</p>
|
||||
<p if="{{$database['collation']}}"><strong>Collation:</strong> {{$database['collation']}}</p>
|
||||
<p><strong>Total Tables:</strong> {{$table_count}}</p>
|
||||
<p if="{{$database['size_mb']}}"><strong>Total Size:</strong> {{number_format($database['size_mb'], 2)}} MB</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card">
|
||||
<h2>Tables</h2>
|
||||
<div class="table-container">
|
||||
{{$table}}
|
||||
</div>
|
||||
|
||||
<div class="pagination-info" if="{{$pagination}}">
|
||||
<p>
|
||||
Showing page {{$pagination['current_page']}} of {{$pagination['total_pages']}}
|
||||
({{$pagination['total_items']}} total tables)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="admin-page">
|
||||
<div class="page-header">
|
||||
<h1>{{ $title }}</h1>
|
||||
<div class="page-actions">
|
||||
<a href="/admin/database" class="btn btn-secondary">Back to Database</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-error" if="{{$error}}">
|
||||
{{$error}}
|
||||
</div>
|
||||
|
||||
<div if="{{$table}}">
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Table Information</h3>
|
||||
<p><strong>Name:</strong> {{$table['name']}}</p>
|
||||
<p if="{{$table['row_count']}}"><strong>Rows:</strong> {{number_format($table['row_count'])}}</p>
|
||||
<p if="{{$table['size_mb']}}"><strong>Size:</strong> {{number_format($table['size_mb'], 2)}} MB</p>
|
||||
<p if="{{$table['engine']}}"><strong>Engine:</strong> {{$table['engine']}}</p>
|
||||
<p if="{{$table['collation']}}"><strong>Collation:</strong> {{$table['collation']}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card">
|
||||
<h2>Columns</h2>
|
||||
<div class="table-container">
|
||||
{{$columns_table}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card" if="{{$has_indexes}}">
|
||||
<h2>Indexes</h2>
|
||||
<div class="table-container">
|
||||
{{$indexes_table}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card" if="{{$has_foreign_keys}}">
|
||||
<h2>Foreign Keys</h2>
|
||||
<div class="table-container">
|
||||
{{$foreign_keys_table}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div class="admin-stat-big">
|
||||
<span class="admin-stat-big__value">{{ $totalDeployments }}</span>
|
||||
<span class="admin-stat-big__value">{{$totalDeployments}}</span>
|
||||
<span class="admin-stat-big__label">All Time</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div class="admin-stat-big">
|
||||
<span class="admin-stat-big__value">{{ $successRate }}%</span>
|
||||
<span class="admin-stat-big__value">{{$successRate}}%</span>
|
||||
<span class="admin-stat-big__label">Success</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div class="admin-stat-big">
|
||||
<span class="admin-stat-big__value">{{ $failedDeployments }}</span>
|
||||
<span class="admin-stat-big__value">{{$failedDeployments}}</span>
|
||||
<span class="admin-stat-big__label">Failures</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -55,7 +55,7 @@
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div class="admin-stat-big">
|
||||
<span class="admin-stat-big__value">{{ $averageDurationFormatted }}</span>
|
||||
<span class="admin-stat-big__value">{{$averageDurationFormatted}}</span>
|
||||
<span class="admin-stat-big__label">Avg Time</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,19 +72,19 @@
|
||||
<div class="admin-stat-list">
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Total Deployments</span>
|
||||
<span class="admin-stat-list__value">{{ $productionStats['total_deployments'] }}</span>
|
||||
<span class="admin-stat-list__value">{{$productionStats['total_deployments']}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Success Rate</span>
|
||||
<span class="admin-stat-list__value">{{ $productionStats['success_rate'] }}%</span>
|
||||
<span class="admin-stat-list__value">{{$productionStats['success_rate']}}%</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Failed</span>
|
||||
<span class="admin-stat-list__value">{{ $productionStats['failed'] }}</span>
|
||||
<span class="admin-stat-list__value">{{$productionStats['failed']}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Rolled Back</span>
|
||||
<span class="admin-stat-list__value">{{ $productionStats['rolled_back'] }}</span>
|
||||
<span class="admin-stat-list__value">{{$productionStats['rolled_back']}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,19 +98,19 @@
|
||||
<div class="admin-stat-list">
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Total Deployments</span>
|
||||
<span class="admin-stat-list__value">{{ $stagingStats['total_deployments'] }}</span>
|
||||
<span class="admin-stat-list__value">{{$stagingStats['total_deployments']}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Success Rate</span>
|
||||
<span class="admin-stat-list__value">{{ $stagingStats['success_rate'] }}%</span>
|
||||
<span class="admin-stat-list__value">{{$stagingStats['success_rate']}}%</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Failed</span>
|
||||
<span class="admin-stat-list__value">{{ $stagingStats['failed'] }}</span>
|
||||
<span class="admin-stat-list__value">{{$stagingStats['failed']}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-list__item">
|
||||
<span class="admin-stat-list__label">Rolled Back</span>
|
||||
<span class="admin-stat-list__value">{{ $stagingStats['rolled_back'] }}</span>
|
||||
<span class="admin-stat-list__value">{{$stagingStats['rolled_back']}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -123,7 +123,7 @@
|
||||
<h3 class="admin-card__title">Recent Deployments</h3>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div if="count($recentDeployments) > 0">
|
||||
<div if="{{count($recentDeployments) > 0}}">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -138,31 +138,29 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<for items="recentDeployments" as="deployment">
|
||||
<tr>
|
||||
<td><code>{{ $deployment['pipelineId'] }}</code></td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--neutral">
|
||||
{{ $deployment['environment'] }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="admin-badge {{ $deployment['statusBadgeClass'] }}">
|
||||
{{ $deployment['status'] }}
|
||||
</span>
|
||||
<span if="$deployment['wasRolledBack']" class="admin-badge admin-badge--warning">Rolled Back</span>
|
||||
</td>
|
||||
<td>{{ $deployment['durationFormatted'] }}</td>
|
||||
<td>{{ $deployment['stageCount'] }}</td>
|
||||
<td>{{ $deployment['successRate'] }}%</td>
|
||||
<td>{{ $deployment['startedAt'] }}</td>
|
||||
<td>{{ $deployment['completedAt'] }}</td>
|
||||
</tr>
|
||||
</for>
|
||||
<tr foreach="$recentDeployments as $deployment">
|
||||
<td><code>{{$deployment['pipelineId']}}</code></td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--neutral">
|
||||
{{$deployment['environment']}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="admin-badge {{$deployment['statusBadgeClass']}}">
|
||||
{{$deployment['status']}}
|
||||
</span>
|
||||
<span if="{{$deployment['wasRolledBack']}}" class="admin-badge admin-badge--warning">Rolled Back</span>
|
||||
</td>
|
||||
<td>{{$deployment['durationFormatted']}}</td>
|
||||
<td>{{$deployment['stageCount']}}</td>
|
||||
<td>{{$deployment['successRate']}}%</td>
|
||||
<td>{{$deployment['startedAt']}}</td>
|
||||
<td>{{$deployment['completedAt']}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div if="count($recentDeployments) === 0">
|
||||
<div if="{{count($recentDeployments) === 0}}">
|
||||
<div class="admin-empty-state">
|
||||
<p class="admin-empty-state__text">No deployments found</p>
|
||||
</div>
|
||||
@@ -171,7 +169,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Failed Deployments Section -->
|
||||
<div if="count($failedDeployments) > 0" class="admin-card" style="margin-top: var(--admin-spacing-xl);">
|
||||
<div class="admin-card" style="margin-top: var(--admin-spacing-xl);" if="{{count($failedDeployments) > 0}}">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">Recent Failed Deployments</h3>
|
||||
</div>
|
||||
@@ -188,30 +186,28 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<for items="failedDeployments" as="deployment">
|
||||
<tr>
|
||||
<td><code>{{ $deployment['pipelineId'] }}</code></td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--neutral">
|
||||
{{ $deployment['environment'] }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span if="$deployment['failedStage'] !== null" class="admin-badge admin-badge--danger">
|
||||
{{ $deployment['failedStage'] }}
|
||||
</span>
|
||||
<span if="$deployment['failedStage'] === null" class="admin-badge admin-badge--neutral">N/A</span>
|
||||
</td>
|
||||
<td>
|
||||
<span if="$deployment['error'] !== null" class="admin-text-truncate" title="{{ $deployment['error'] }}">
|
||||
{{ $deployment['error'] }}
|
||||
</span>
|
||||
<span if="$deployment['error'] === null" class="admin-text-muted">No error message</span>
|
||||
</td>
|
||||
<td>{{ $deployment['durationFormatted'] }}</td>
|
||||
<td>{{ $deployment['startedAt'] }}</td>
|
||||
</tr>
|
||||
</for>
|
||||
<tr foreach="$failedDeployments as $deployment">
|
||||
<td><code>{{$deployment['pipelineId']}}</code></td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--neutral">
|
||||
{{$deployment['environment']}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span if="{{$deployment['failedStage'] !== null}}" class="admin-badge admin-badge--danger">
|
||||
{{$deployment['failedStage']}}
|
||||
</span>
|
||||
<span if="{{$deployment['failedStage'] === null}}" class="admin-badge admin-badge--neutral">N/A</span>
|
||||
</td>
|
||||
<td>
|
||||
<span if="{{$deployment['error'] !== null}}" class="admin-text-truncate" title="{{$deployment['error']}}">
|
||||
{{$deployment['error']}}
|
||||
</span>
|
||||
<span if="{{$deployment['error'] === null}}" class="admin-text-muted">No error message</span>
|
||||
</td>
|
||||
<td>{{$deployment['durationFormatted']}}</td>
|
||||
<td>{{$deployment['startedAt']}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
11
src/Application/Admin/templates/docker-dashboard.view.php
Normal file
11
src/Application/Admin/templates/docker-dashboard.view.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="admin-page">
|
||||
<div class="page-header">
|
||||
<h1>{{$title}}</h1>
|
||||
</div>
|
||||
|
||||
<!-- LiveComponent for real-time Docker container monitoring -->
|
||||
<x-docker-containers id="docker-dashboard" />
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="section">
|
||||
<h2>Umgebungsvariablen</h2>
|
||||
<h2>{{$title}}</h2>
|
||||
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="envFilter" placeholder="Variablen filtern..." style="padding: 8px 12px; border: 1px solid var(--gray-300); border-radius: 6px; width: 300px;">
|
||||
|
||||
@@ -1,84 +1,12 @@
|
||||
<layout name="admin" />
|
||||
<!-- Cache invalidation: 2025-01-20 16:11 -->
|
||||
|
||||
<div class="section">
|
||||
<h2>{{ title }}</h2>
|
||||
|
||||
<div class="admin-card">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">System Overview</h3>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-md); margin-bottom: var(--space-lg);">
|
||||
<div class="metric-card">
|
||||
<div class="metric-card__value" style="color: var(--success);">
|
||||
{{ overall_status }}
|
||||
</div>
|
||||
<div class="metric-card__label">Overall Status</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-card__value">{{ total_checks }}</div>
|
||||
<div class="metric-card__label">Total Checks</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-card__value" style="color: var(--success);">{{ healthy_checks }}</div>
|
||||
<div class="metric-card__label">Healthy</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-card__value" style="color: var(--warning);">{{ warning_checks }}</div>
|
||||
<div class="metric-card__label">Warnings</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-card__value" style="color: var(--error);">{{ failed_checks }}</div>
|
||||
<div class="metric-card__label">Failed</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card" style="margin-top: var(--space-lg);">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">Health Check Details</h3>
|
||||
<button class="admin-button admin-button--small" onclick="refreshHealthChecks()" style="margin-left: auto;">
|
||||
🔄 Refresh
|
||||
</button>
|
||||
</div>
|
||||
<table-data source="health_check_table" container-class="admin-card" />
|
||||
</div>
|
||||
<h2>{{$title}}</h2>
|
||||
|
||||
<!-- LiveComponent for real-time health status -->
|
||||
<x-health-status id="health-dashboard" />
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleDetails(id) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.style.display = element.style.display === 'none' ? 'table-row' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function refreshHealthChecks() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// Auto-refresh health status every 60 seconds
|
||||
setInterval(function() {
|
||||
window.location.reload();
|
||||
}, 60000);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.metric-card {
|
||||
text-align: center;
|
||||
padding: var(--space-md);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--bg-alt);
|
||||
}
|
||||
|
||||
.metric-card__value {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: var(--space-sm);
|
||||
}
|
||||
|
||||
.metric-card__label {
|
||||
color: var(--muted);
|
||||
font-size: 0.875rem;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<div class="section">
|
||||
<div class="page-header-actions">
|
||||
<div>
|
||||
<h2>{{ title }}</h2>
|
||||
<p>{{ subtitle }}</p>
|
||||
<h2>{{$title}}</h2>
|
||||
<p>{{$subtitle}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -68,24 +68,22 @@
|
||||
</p>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px;">
|
||||
<for var="slot" in="slots">
|
||||
<div class="stat-card slot-item" data-slot-id="{{ slot.id }}">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div>
|
||||
<h4>{{ slot.slotName }}</h4>
|
||||
<p style="color: var(--gray-600); margin: 0;">ID: {{ slot.id }}</p>
|
||||
</div>
|
||||
<div class="slot-image-container" style="width: 100px; height: 100px;">
|
||||
<div style="border: 2px dashed var(--gray-300); display: flex; align-items: center; justify-content: center; height: 100%; border-radius: 4px; cursor: pointer;"
|
||||
ondrop="handleDrop(event, '{{ slot.id }}')"
|
||||
ondragover="handleDragOver(event)"
|
||||
ondragleave="handleDragLeave(event)">
|
||||
<span style="color: var(--gray-500); font-size: 12px; text-align: center;">Drop image here</span>
|
||||
</div>
|
||||
<div class="stat-card slot-item" data-slot-id="{{$slot->id}}" foreach="$slots as $slot">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div>
|
||||
<h4>{{$slot->slotName}}</h4>
|
||||
<p style="color: var(--gray-600); margin: 0;">ID: {{$slot->id}}</p>
|
||||
</div>
|
||||
<div class="slot-image-container" style="width: 100px; height: 100px;">
|
||||
<div style="border: 2px dashed var(--gray-300); display: flex; align-items: center; justify-content: center; height: 100%; border-radius: 4px; cursor: pointer;"
|
||||
ondrop="handleDrop(event, '{{$slot->id}}')"
|
||||
ondragover="handleDragOver(event)"
|
||||
ondragleave="handleDragLeave(event)">
|
||||
<span style="color: var(--gray-500); font-size: 12px; text-align: center;">Drop image here</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</for>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<layout src="admin-main"/>
|
||||
|
||||
<form action='/admin/imageslots/edit/{{ id }}' method='post'>
|
||||
<form action='/admin/imageslots/edit/{{$id}}' method='post'>
|
||||
<input type='hidden' name='_method' value='PUT'/>
|
||||
|
||||
<label>Slot Name:
|
||||
<input type='text' name='slotName' value='{{ slotName }}'/>
|
||||
<input type='text' name='slotName' value='{{$slotName}}'/>
|
||||
</label>
|
||||
<input type='submit' value='Update'/>
|
||||
|
||||
|
||||
@@ -1,47 +1,39 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="section">
|
||||
<h2>{{ title }}</h2>
|
||||
<h2>{{$title}}</h2>
|
||||
|
||||
<div class="admin-card">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">Existing Image Slots</h3>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<if condition="slots">
|
||||
<div class="admin-table-wrapper">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Slot Name</th>
|
||||
<th>Current Image</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<for var="slot" in="slots">
|
||||
<tr>
|
||||
<td>{{ slot.slotName }}</td>
|
||||
<td>
|
||||
<if condition="slot.image">
|
||||
{{ slot.image.filename }}
|
||||
<else/>
|
||||
<span style="color: var(--muted);">No image assigned</span>
|
||||
</if>
|
||||
</td>
|
||||
<td class="admin-table__actions">
|
||||
<form action="/admin/content/image-slots/{{ slot.slotName }}" method="post" style="display: inline;">
|
||||
<button type="submit" class="admin-table__action">Edit</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</for>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<else/>
|
||||
<p style="color: var(--muted); text-align: center; padding: var(--space-lg);">No image slots created yet.</p>
|
||||
</if>
|
||||
<div class="admin-table-wrapper" if="{{$slots}}">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Slot Name</th>
|
||||
<th>Current Image</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr foreach="$slots as $slot">
|
||||
<td>{{$slot->slotName}}</td>
|
||||
<td>
|
||||
<span if="{{$slot->image}}">{{$slot->image->filename}}</span>
|
||||
<span style="color: var(--muted);" if="{{!$slot->image}}">No image assigned</span>
|
||||
</td>
|
||||
<td class="admin-table__actions">
|
||||
<form action="/admin/content/image-slots/{{$slot->slotName}}" method="post" style="display: inline;">
|
||||
<button type="submit" class="admin-table__action">Edit</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p style="color: var(--muted); text-align: center; padding: var(--space-lg);" if="{{!$slots}}">No image slots created yet.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="section">
|
||||
<h2>📄 {{ title }}</h2>
|
||||
<h2>📄 {{$title}}</h2>
|
||||
|
||||
<div class="admin-card">
|
||||
<div class="admin-card__header">
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="section">
|
||||
<h2>{{ title }}</h2>
|
||||
<h2>{{$title}}</h2>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Migration Status</h3>
|
||||
<p><strong>Total Migrations:</strong> {{ total_migrations }}</p>
|
||||
<p><strong>Applied:</strong> <span style="color: var(--success);">{{ applied_count }}</span></p>
|
||||
<p><strong>Pending:</strong> <span style="color: var(--warning);">{{ pending_count }}</span></p>
|
||||
<p><strong>Total Migrations:</strong> {{$total_migrations}}</p>
|
||||
<p><strong>Applied:</strong> <span style="color: var(--success);">{{$applied_count}}</span></p>
|
||||
<p><strong>Pending:</strong> <span style="color: var(--warning);">{{$pending_count}}</span></p>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>Database State</h3>
|
||||
<p><strong>Pending Count:</strong> {{ pending_count }}</p>
|
||||
<p><strong>Pending Count:</strong> {{$pending_count}}</p>
|
||||
<p><strong>Status:</strong>
|
||||
<if condition="{{ has_pending }}">
|
||||
<span style="color: var(--warning);">⚠️ {{ pending_count }} Migration(s) pending</span>
|
||||
<else/>
|
||||
<span style="color: var(--success);">✅ Database is up to date</span>
|
||||
</if>
|
||||
<span style="color: var(--warning);" if="{{$has_pending}}">⚠️ {{$pending_count}} Migration(s) pending</span>
|
||||
<span style="color: var(--success);" if="{{!$has_pending}}">✅ Database is up to date</span>
|
||||
</p>
|
||||
<if condition="{{ has_pending }}">
|
||||
<p><strong>Action Required:</strong> <span style="color: var(--error);">Run migrations to update database</span></p>
|
||||
</if>
|
||||
<p if="{{$has_pending}}"><strong>Action Required:</strong> <span style="color: var(--error);">Run migrations to update database</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -33,20 +33,20 @@
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Overall Status</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<span class="admin-badge admin-badge--{{ $overall_badge }}">{{ $overall_status }}</span>
|
||||
<span class="admin-badge admin-badge--{{$overall_badge}}">{{$overall_status}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Health Percentage</span>
|
||||
<span class="admin-stat-item__value">{{ $health_percentage }}%</span>
|
||||
<span class="admin-stat-item__value">{{$health_percentage}}%</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Average Accuracy</span>
|
||||
<span class="admin-stat-item__value">{{ $average_accuracy }}%</span>
|
||||
<span class="admin-stat-item__value">{{$average_accuracy}}%</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Time Window</span>
|
||||
<span class="admin-stat-item__value">{{ $time_window_hours }} hours</span>
|
||||
<span class="admin-stat-item__value">{{$time_window_hours}} hours</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -61,24 +61,24 @@
|
||||
<div class="admin-stat-list">
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Total Models</span>
|
||||
<span class="admin-stat-item__value">{{ $total_models }}</span>
|
||||
<span class="admin-stat-item__value">{{$total_models}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Healthy</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<span class="admin-badge admin-badge--success">{{ $healthy_models }}</span>
|
||||
<span class="admin-badge admin-badge--success">{{$healthy_models}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Degraded</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<span class="admin-badge admin-badge--warning">{{ $degraded_models }}</span>
|
||||
<span class="admin-badge admin-badge--warning">{{$degraded_models}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Critical</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<span class="admin-badge admin-badge--danger">{{ $critical_models }}</span>
|
||||
<span class="admin-badge admin-badge--danger">{{$critical_models}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,19 +94,19 @@
|
||||
<div class="admin-stat-list">
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Total Predictions</span>
|
||||
<span class="admin-stat-item__value">{{ $total_predictions }}</span>
|
||||
<span class="admin-stat-item__value">{{$total_predictions}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Supervised Models</span>
|
||||
<span class="admin-stat-item__value">{{ $supervised_count }}</span>
|
||||
<span class="admin-stat-item__value">{{$supervised_count}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Unsupervised Models</span>
|
||||
<span class="admin-stat-item__value">{{ $unsupervised_count }}</span>
|
||||
<span class="admin-stat-item__value">{{$unsupervised_count}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Reinforcement Models</span>
|
||||
<span class="admin-stat-item__value">{{ $reinforcement_count }}</span>
|
||||
<span class="admin-stat-item__value">{{$reinforcement_count}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,11 +114,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Degradation Alerts Section -->
|
||||
<div class="admin-card" if="{{ $has_alerts }}">
|
||||
<div class="admin-card" if="{{$has_alerts}}">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">
|
||||
Degradation Alerts
|
||||
<span class="admin-badge admin-badge--danger">{{ $alert_count }}</span>
|
||||
<span class="admin-badge admin-badge--danger">{{$alert_count}}</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
@@ -137,23 +137,23 @@
|
||||
<tbody>
|
||||
<tr foreach="$alerts as $alert">
|
||||
<td>
|
||||
<strong>{{ $alert['model_name'] }}</strong>
|
||||
<strong>{{$alert['model_name']}}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ $alert['version'] }}</code>
|
||||
<code>{{$alert['version']}}</code>
|
||||
</td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--{{ $alert['severity_badge'] }}">
|
||||
{{ $alert['current_accuracy'] }}%
|
||||
<span class="admin-badge admin-badge--{{$alert['severity_badge']}}">
|
||||
{{$alert['current_accuracy']}}%
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ $alert['threshold'] }}%</td>
|
||||
<td>{{$alert['threshold']}}%</td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--{{ $alert['severity_badge'] }}">
|
||||
{{ $alert['severity'] }}
|
||||
<span class="admin-badge admin-badge--{{$alert['severity_badge']}}">
|
||||
{{$alert['severity']}}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ $alert['recommendation'] }}</td>
|
||||
<td>{{$alert['recommendation']}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -187,38 +187,38 @@
|
||||
<tbody>
|
||||
<tr foreach="$models as $model">
|
||||
<td>
|
||||
<strong>{{ $model['model_name'] }}</strong>
|
||||
<strong>{{$model['model_name']}}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ $model['version'] }}</code>
|
||||
<code>{{$model['version']}}</code>
|
||||
</td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--info">
|
||||
{{ $model['type'] }}
|
||||
{{$model['type']}}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ $model['accuracy'] }}%</td>
|
||||
<td>{{$model['accuracy']}}%</td>
|
||||
<td>
|
||||
<span if="!{{ $model['precision'] }}">-</span>
|
||||
<span if="{{ $model['precision'] }}">{{ $model['precision'] }}%</span>
|
||||
<span if="{{!$model['precision']}}">-</span>
|
||||
<span if="{{$model['precision']}}">{{$model['precision']}}%</span>
|
||||
</td>
|
||||
<td>
|
||||
<span if="!{{ $model['recall'] }}">-</span>
|
||||
<span if="{{ $model['recall'] }}">{{ $model['recall'] }}%</span>
|
||||
<span if="{{!$model['recall']}}">-</span>
|
||||
<span if="{{$model['recall']}}">{{$model['recall']}}%</span>
|
||||
</td>
|
||||
<td>
|
||||
<span if="!{{ $model['f1_score'] }}">-</span>
|
||||
<span if="{{ $model['f1_score'] }}">{{ $model['f1_score'] }}%</span>
|
||||
<span if="{{!$model['f1_score']}}">-</span>
|
||||
<span if="{{$model['f1_score']}}">{{$model['f1_score']}}%</span>
|
||||
</td>
|
||||
<td>{{ $model['total_predictions'] }}</td>
|
||||
<td>{{$model['total_predictions']}}</td>
|
||||
<td>
|
||||
<span if="!{{ $model['average_confidence'] }}">-</span>
|
||||
<span if="{{ $model['average_confidence'] }}">{{ $model['average_confidence'] }}%</span>
|
||||
<span if="{{!$model['average_confidence']}}">-</span>
|
||||
<span if="{{$model['average_confidence']}}">{{$model['average_confidence']}}%</span>
|
||||
</td>
|
||||
<td>{{ $model['threshold'] }}</td>
|
||||
<td>{{$model['threshold']}}</td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--{{ $model['status_badge'] }}">
|
||||
{{ $model['status'] }}
|
||||
<span class="admin-badge admin-badge--{{$model['status_badge']}}">
|
||||
{{$model['status']}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -229,39 +229,39 @@
|
||||
</div>
|
||||
|
||||
<!-- Confusion Matrices Section -->
|
||||
<div class="admin-card" if="{{ $has_confusion_matrices }}">
|
||||
<div class="admin-card" if="{{$has_confusion_matrices}}">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">Classification Performance (Confusion Matrices)</h3>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<div class="admin-grid admin-grid--2-col">
|
||||
<div foreach="$confusion_matrices as $matrix" class="confusion-matrix-card">
|
||||
<h4 class="confusion-matrix-card__title">{{ $matrix['model_name'] }} v{{ $matrix['version'] }}</h4>
|
||||
<h4 class="confusion-matrix-card__title">{{$matrix['model_name']}} v{{$matrix['version']}}</h4>
|
||||
|
||||
<div class="confusion-matrix">
|
||||
<div class="confusion-matrix__grid">
|
||||
<!-- True Positive -->
|
||||
<div class="confusion-matrix__cell confusion-matrix__cell--tp">
|
||||
<div class="confusion-matrix__cell-label">True Positive</div>
|
||||
<div class="confusion-matrix__cell-value">{{ $matrix['true_positives'] }}</div>
|
||||
<div class="confusion-matrix__cell-value">{{$matrix['true_positives']}}</div>
|
||||
</div>
|
||||
|
||||
<!-- False Positive -->
|
||||
<div class="confusion-matrix__cell confusion-matrix__cell--fp">
|
||||
<div class="confusion-matrix__cell-label">False Positive</div>
|
||||
<div class="confusion-matrix__cell-value">{{ $matrix['false_positives'] }}</div>
|
||||
<div class="confusion-matrix__cell-value">{{$matrix['false_positives']}}</div>
|
||||
</div>
|
||||
|
||||
<!-- False Negative -->
|
||||
<div class="confusion-matrix__cell confusion-matrix__cell--fn">
|
||||
<div class="confusion-matrix__cell-label">False Negative</div>
|
||||
<div class="confusion-matrix__cell-value">{{ $matrix['false_negatives'] }}</div>
|
||||
<div class="confusion-matrix__cell-value">{{$matrix['false_negatives']}}</div>
|
||||
</div>
|
||||
|
||||
<!-- True Negative -->
|
||||
<div class="confusion-matrix__cell confusion-matrix__cell--tn">
|
||||
<div class="confusion-matrix__cell-label">True Negative</div>
|
||||
<div class="confusion-matrix__cell-value">{{ $matrix['true_negatives'] }}</div>
|
||||
<div class="confusion-matrix__cell-value">{{$matrix['true_negatives']}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -269,16 +269,16 @@
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">False Positive Rate</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<span class="admin-badge admin-badge--{{ $matrix['fp_rate_badge'] }}">
|
||||
{{ $matrix['fp_rate_percent'] }}%
|
||||
<span class="admin-badge admin-badge--{{$matrix['fp_rate_badge']}}">
|
||||
{{$matrix['fp_rate_percent']}}%
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">False Negative Rate</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<span class="admin-badge admin-badge--{{ $matrix['fn_rate_badge'] }}">
|
||||
{{ $matrix['fn_rate_percent'] }}%
|
||||
<span class="admin-badge admin-badge--{{$matrix['fn_rate_badge']}}">
|
||||
{{$matrix['fn_rate_percent']}}%
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -290,7 +290,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Model Registry Summary -->
|
||||
<div class="admin-card" if="{{ $has_registry_summary }}">
|
||||
<div class="admin-card" if="{{$has_registry_summary}}">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">Model Registry Summary</h3>
|
||||
</div>
|
||||
@@ -298,18 +298,18 @@
|
||||
<div class="admin-grid admin-grid--3-col">
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Total Versions</span>
|
||||
<span class="admin-stat-item__value">{{ $registry_total_versions }}</span>
|
||||
<span class="admin-stat-item__value">{{$registry_total_versions}}</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Production Models</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<span class="admin-badge admin-badge--success">{{ $registry_production_count }}</span>
|
||||
<span class="admin-badge admin-badge--success">{{$registry_production_count}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Development Models</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<span class="admin-badge admin-badge--info">{{ $registry_development_count }}</span>
|
||||
<span class="admin-badge admin-badge--info">{{$registry_development_count}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -327,15 +327,15 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr foreach="$registry_models as $regModel">
|
||||
<td><strong>{{ $regModel['model_name'] }}</strong></td>
|
||||
<td>{{ $regModel['version_count'] }}</td>
|
||||
<td><strong>{{$regModel['model_name']}}</strong></td>
|
||||
<td>{{$regModel['version_count']}}</td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--info">{{ $regModel['type'] }}</span>
|
||||
<span class="admin-badge admin-badge--info">{{$regModel['type']}}</span>
|
||||
</td>
|
||||
<td><code>{{ $regModel['latest_version'] }}</code></td>
|
||||
<td><code>{{$regModel['latest_version']}}</code></td>
|
||||
<td>
|
||||
<span class="admin-badge admin-badge--{{ $regModel['environment'] === 'production' ? 'success' : 'info' }}">
|
||||
{{ $regModel['environment'] }}
|
||||
<span class="admin-badge admin-badge--{{$regModel['environment'] === 'production' ? 'success' : 'info'}}">
|
||||
{{$regModel['environment']}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -355,13 +355,13 @@
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Dashboard Data</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<code>GET {{ $api_dashboard_url }}</code>
|
||||
<code>GET {{$api_dashboard_url}}</code>
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
<span class="admin-stat-item__label">Health Check</span>
|
||||
<span class="admin-stat-item__value">
|
||||
<code>GET {{ $api_health_url }}</code>
|
||||
<code>GET {{$api_health_url}}</code>
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-stat-item">
|
||||
|
||||
@@ -2,197 +2,7 @@
|
||||
|
||||
<div class="section">
|
||||
<h2>Performance Übersicht</h2>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>💾 Speicher <span id="realtime-indicator" class="indicator"></span></h3>
|
||||
<p><strong>Aktuell:</strong> <span id="current-memory">{{ performance.currentMemoryUsage }}</span></p>
|
||||
<p><strong>Peak:</strong> <span id="peak-memory">{{ performance.peakMemoryUsage }}</span></p>
|
||||
<p><strong>Limit:</strong> {{ performance.memoryLimit }}</p>
|
||||
<p><strong>Auslastung:</strong> <span id="memory-percentage">{{ performance.memoryUsagePercentage }}</span>%</p>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="memory-progress" style="width: {{ performance.memoryUsagePercentage }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>System</h3>
|
||||
<p><strong>Ausführungszeit:</strong> {{ performance.executionTime }}</p>
|
||||
<p><strong>Geladene Dateien:</strong> {{ performance.includedFiles }}</p>
|
||||
<p><strong>OPCache:</strong> {{ performance.opcacheEnabled }}</p>
|
||||
</div>
|
||||
|
||||
<if condition="{{ performance.opcacheMemoryUsage }}">
|
||||
<div class="stat-card">
|
||||
<h3>OPCache</h3>
|
||||
<p><strong>Speicher:</strong> {{ performance.opcacheMemoryUsage }}</p>
|
||||
<p><strong>Cache Hits:</strong> {{ performance.opcacheCacheHits }}</p>
|
||||
<p><strong>Miss Rate:</strong> {{ performance.opcacheMissRate }}</p>
|
||||
</div>
|
||||
</if>
|
||||
</div>
|
||||
|
||||
<!-- Real-time Controls -->
|
||||
<div class="admin-card" style="margin-top: var(--space-lg);">
|
||||
<div class="admin-card__header">
|
||||
<h3 class="admin-card__title">⚙️ Real-time Monitoring</h3>
|
||||
</div>
|
||||
<div class="admin-card__content">
|
||||
<label style="display: flex; align-items: center; gap: 10px;">
|
||||
<input type="checkbox" id="realtime-toggle">
|
||||
<span>Enable Real-time Updates</span>
|
||||
<span id="realtime-status" class="status-indicator"></span>
|
||||
</label>
|
||||
<p style="margin-top: 10px; font-size: 0.9em; color: var(--text-muted);">
|
||||
<span id="last-update">Last Updated: {{ timestamp }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.progress-bar {
|
||||
height: 8px;
|
||||
background: var(--bg-muted);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--success), var(--success-dark));
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--text-muted);
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.indicator.active {
|
||||
background: var(--success);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: var(--text-muted);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.status-indicator.active {
|
||||
background: var(--success);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: var(--space-lg);
|
||||
margin-bottom: var(--space-lg);
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-lg);
|
||||
}
|
||||
|
||||
.stat-card h3 {
|
||||
margin: 0 0 var(--space-md) 0;
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-card p {
|
||||
margin: var(--space-sm) 0;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const toggle = document.getElementById('realtime-toggle');
|
||||
const statusIndicator = document.getElementById('realtime-status');
|
||||
const realtimeIndicator = document.getElementById('realtime-indicator');
|
||||
const lastUpdate = document.getElementById('last-update');
|
||||
let updateInterval = null;
|
||||
|
||||
toggle?.addEventListener('change', function() {
|
||||
if (this.checked) {
|
||||
startRealtimeUpdates();
|
||||
} else {
|
||||
stopRealtimeUpdates();
|
||||
}
|
||||
});
|
||||
|
||||
function startRealtimeUpdates() {
|
||||
statusIndicator?.classList.add('active');
|
||||
realtimeIndicator?.classList.add('active');
|
||||
|
||||
updateInterval = setInterval(async () => {
|
||||
try {
|
||||
const response = await fetch('/admin/system/performance/api/realtime');
|
||||
const data = await response.json();
|
||||
updateMetrics(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch realtime metrics:', error);
|
||||
stopRealtimeUpdates();
|
||||
toggle.checked = false;
|
||||
}
|
||||
}, 3000); // Update every 3 seconds
|
||||
}
|
||||
|
||||
function stopRealtimeUpdates() {
|
||||
statusIndicator?.classList.remove('active');
|
||||
realtimeIndicator?.classList.remove('active');
|
||||
|
||||
if (updateInterval) {
|
||||
clearInterval(updateInterval);
|
||||
updateInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateMetrics(data) {
|
||||
// Update memory metrics
|
||||
const currentMemory = document.getElementById('current-memory');
|
||||
if (currentMemory && data.memory?.current) {
|
||||
currentMemory.textContent = data.memory.current;
|
||||
}
|
||||
|
||||
const peakMemory = document.getElementById('peak-memory');
|
||||
if (peakMemory && data.memory?.peak) {
|
||||
peakMemory.textContent = data.memory.peak;
|
||||
}
|
||||
|
||||
const memoryPercentage = document.getElementById('memory-percentage');
|
||||
const memoryProgress = document.getElementById('memory-progress');
|
||||
if (data.memory?.usage_percentage !== undefined) {
|
||||
if (memoryPercentage) {
|
||||
memoryPercentage.textContent = data.memory.usage_percentage.toFixed(1);
|
||||
}
|
||||
if (memoryProgress) {
|
||||
memoryProgress.style.width = data.memory.usage_percentage + '%';
|
||||
}
|
||||
}
|
||||
|
||||
// Update timestamp
|
||||
if (lastUpdate && data.timestamp) {
|
||||
lastUpdate.textContent = 'Last Updated: ' + data.timestamp;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- LiveComponent for real-time performance metrics -->
|
||||
<x-performance-metrics id="performance-dashboard" />
|
||||
</div>
|
||||
@@ -1,7 +1,7 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="section">
|
||||
<h2>{{ title }}</h2>
|
||||
<h2>{{$title}}</h2>
|
||||
|
||||
<div class="admin-card">
|
||||
<div class="admin-card__header">
|
||||
@@ -24,17 +24,15 @@
|
||||
<div class="admin-card__content">
|
||||
<div style="margin-bottom: var(--space-md);">
|
||||
<div class="metric-card" style="display: inline-block; margin-right: var(--space-md);">
|
||||
<div class="metric-card__value">{{ extensions_count }}</div>
|
||||
<div class="metric-card__value">{{$extensions_count}}</div>
|
||||
<div class="metric-card__label">Total Extensions</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--space-sm);">
|
||||
<for var="extension" in="extensions_list">
|
||||
<div style="padding: var(--space-sm); background: var(--bg-alt); border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: 0.875rem;">
|
||||
{{ extension }}
|
||||
</div>
|
||||
</for>
|
||||
<div style="padding: var(--space-sm); background: var(--bg-alt); border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: 0.875rem;" foreach="$extensions_list as $extension">
|
||||
{{$extension}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Campaign - {campaign.title}</title>
|
||||
<title>Edit Campaign - {{$campaign->title}}</title>
|
||||
<link rel="stylesheet" href="/css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
@@ -11,10 +11,10 @@
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1>Edit Campaign</h1>
|
||||
<a href="/admin/presave/campaigns/{campaign.id}" class="btn btn-secondary">Back to Details</a>
|
||||
<a href="/admin/presave/campaigns/{{$campaign->id}}" class="btn btn-secondary">Back to Details</a>
|
||||
</div>
|
||||
|
||||
<form action="/admin/presave/campaigns/{campaign.id}" method="POST" class="campaign-form">
|
||||
<form action="/admin/presave/campaigns/{{$campaign->id}}" method="POST" class="campaign-form">
|
||||
<csrf-token />
|
||||
<input type="hidden" name="_method" value="PUT">
|
||||
|
||||
@@ -23,23 +23,23 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="title">Campaign Title *</label>
|
||||
<input type="text" id="title" name="title" value="{campaign.title}" required class="form-control">
|
||||
<input type="text" id="title" name="title" value="{{$campaign->title}}" required class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="artist_name">Artist Name *</label>
|
||||
<input type="text" id="artist_name" name="artist_name" value="{campaign.artistName}" required class="form-control">
|
||||
<input type="text" id="artist_name" name="artist_name" value="{{$campaign->artistName}}" required class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cover_image_url">Cover Image URL *</label>
|
||||
<input type="url" id="cover_image_url" name="cover_image_url" value="{campaign.coverImageUrl}" required class="form-control">
|
||||
<input type="url" id="cover_image_url" name="cover_image_url" value="{{$campaign->coverImageUrl}}" required class="form-control">
|
||||
<small>Direct URL to album/single cover image</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" rows="4" class="form-control">{campaign.description}</textarea>
|
||||
<textarea id="description" name="description" rows="4" class="form-control">{{$campaign->description}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="release_date">Release Date *</label>
|
||||
<input type="datetime-local" id="release_date" name="release_date" value="{campaign.releaseDate|datetime_input}" required class="form-control">
|
||||
<input type="datetime-local" id="release_date" name="release_date" value="{{$campaign->releaseDate}}" required class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -58,23 +58,23 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="spotify_url">Spotify URL</label>
|
||||
<input type="url" id="spotify_url" name="spotify_url" value="{campaign.trackUrls.spotify}" class="form-control" placeholder="https://open.spotify.com/album/...">
|
||||
<input type="url" id="spotify_url" name="spotify_url" value="{{$campaign->trackUrls['spotify'] ?? ''}}" class="form-control" placeholder="https://open.spotify.com/album/...">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="apple_music_url">Apple Music URL</label>
|
||||
<input type="url" id="apple_music_url" name="apple_music_url" value="{campaign.trackUrls.apple_music}" class="form-control" placeholder="https://music.apple.com/album/...">
|
||||
<input type="url" id="apple_music_url" name="apple_music_url" value="{{$campaign->trackUrls['apple_music'] ?? ''}}" class="form-control" placeholder="https://music.apple.com/album/...">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tidal_url">Tidal URL</label>
|
||||
<input type="url" id="tidal_url" name="tidal_url" value="{campaign.trackUrls.tidal}" class="form-control" placeholder="https://tidal.com/browse/album/...">
|
||||
<input type="url" id="tidal_url" name="tidal_url" value="{{$campaign->trackUrls['tidal'] ?? ''}}" class="form-control" placeholder="https://tidal.com/browse/album/...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Update Campaign</button>
|
||||
<a href="/admin/presave/campaigns/{campaign.id}" class="btn btn-secondary">Cancel</a>
|
||||
<a href="/admin/presave/campaigns/{{$campaign->id}}" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
<script>
|
||||
// Convert timestamp to datetime-local format
|
||||
const releaseDate = new Date({campaign.releaseDate} * 1000);
|
||||
const releaseDate = new Date({{$campaign->releaseDate}} * 1000);
|
||||
document.getElementById('release_date').value = releaseDate.toISOString().slice(0, 16);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -14,73 +14,61 @@
|
||||
<a href="/admin/presave/campaigns/create" class="btn btn-primary">New Campaign</a>
|
||||
</div>
|
||||
|
||||
<if condition="{stats}">
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Total Campaigns</h3>
|
||||
<p class="stat-value">{stats.total}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>Active</h3>
|
||||
<p class="stat-value">{stats.active}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>Total Registrations</h3>
|
||||
<p class="stat-value">{stats.total_registrations}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>Completed</h3>
|
||||
<p class="stat-value">{stats.completed}</p>
|
||||
</div>
|
||||
<div class="stats-grid" if="{{$stats}}">
|
||||
<div class="stat-card">
|
||||
<h3>Total Campaigns</h3>
|
||||
<p class="stat-value">{{$stats['total']}}</p>
|
||||
</div>
|
||||
</if>
|
||||
<div class="stat-card">
|
||||
<h3>Active</h3>
|
||||
<p class="stat-value">{{$stats['active']}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>Total Registrations</h3>
|
||||
<p class="stat-value">{{$stats['total_registrations']}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>Completed</h3>
|
||||
<p class="stat-value">{{$stats['completed']}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<if condition="{campaigns}">
|
||||
<div class="campaigns-list">
|
||||
<for items="{campaigns}" as="campaign">
|
||||
<div class="campaign-card">
|
||||
<div class="campaign-header">
|
||||
<img src="{campaign.coverImageUrl}" alt="{campaign.title}" class="campaign-cover">
|
||||
<div class="campaign-info">
|
||||
<h3>{campaign.title}</h3>
|
||||
<p class="artist">{campaign.artistName}</p>
|
||||
<p class="release-date">Release: {campaign.releaseDate|date}</p>
|
||||
</div>
|
||||
<div class="campaign-status">
|
||||
<span class="badge badge-{campaign.status.value}">{campaign.status.value}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="campaign-actions">
|
||||
<a href="/admin/presave/campaigns/{campaign.id}" class="btn btn-sm">View</a>
|
||||
<a href="/admin/presave/campaigns/{campaign.id}/edit" class="btn btn-sm">Edit</a>
|
||||
|
||||
<if condition="{campaign.status.value === 'draft'}">
|
||||
<form action="/admin/presave/campaigns/{campaign.id}/activate" method="POST" style="display: inline;">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-sm btn-success">Activate</button>
|
||||
</form>
|
||||
</if>
|
||||
|
||||
<if condition="{campaign.status.value === 'active'}">
|
||||
<form action="/admin/presave/campaigns/{campaign.id}/pause" method="POST" style="display: inline;">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-sm btn-warning">Pause</button>
|
||||
</form>
|
||||
</if>
|
||||
|
||||
<button class="btn btn-sm btn-danger" onclick="deleteCampaign({campaign.id})">Delete</button>
|
||||
</div>
|
||||
<div class="campaigns-list" if="{{$campaigns}}">
|
||||
<div class="campaign-card" foreach="$campaigns as $campaign">
|
||||
<div class="campaign-header">
|
||||
<img src="{{$campaign->coverImageUrl}}" alt="{{$campaign->title}}" class="campaign-cover">
|
||||
<div class="campaign-info">
|
||||
<h3>{{$campaign->title}}</h3>
|
||||
<p class="artist">{{$campaign->artistName}}</p>
|
||||
<p class="release-date">Release: {{$campaign->releaseDate}}</p>
|
||||
</div>
|
||||
</for>
|
||||
</div>
|
||||
</if>
|
||||
<div class="campaign-status">
|
||||
<span class="badge badge-{{$campaign->status->value}}">{{$campaign->status->value}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="campaign-actions">
|
||||
<a href="/admin/presave/campaigns/{{$campaign->id}}" class="btn btn-sm">View</a>
|
||||
<a href="/admin/presave/campaigns/{{$campaign->id}}/edit" class="btn btn-sm">Edit</a>
|
||||
|
||||
<if condition="{!campaigns || campaigns.length === 0}">
|
||||
<div class="empty-state">
|
||||
<p>No campaigns yet.</p>
|
||||
<a href="/admin/presave/campaigns/create" class="btn btn-primary">Create your first campaign</a>
|
||||
<form action="/admin/presave/campaigns/{{$campaign->id}}/activate" method="POST" style="display: inline;" if="{{$campaign->status->value === 'draft'}}">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-sm btn-success">Activate</button>
|
||||
</form>
|
||||
|
||||
<form action="/admin/presave/campaigns/{{$campaign->id}}/pause" method="POST" style="display: inline;" if="{{$campaign->status->value === 'active'}}">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-sm btn-warning">Pause</button>
|
||||
</form>
|
||||
|
||||
<button class="btn btn-sm btn-danger" onclick="deleteCampaign({{$campaign->id}})">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</if>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" if="{{!$campaigns || count($campaigns) === 0}}">
|
||||
<p>No campaigns yet.</p>
|
||||
<a href="/admin/presave/campaigns/create" class="btn btn-primary">Create your first campaign</a>
|
||||
</div>
|
||||
</div>
|
||||
</layout>
|
||||
|
||||
|
||||
@@ -3,48 +3,44 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{campaign.title} - Campaign Details</title>
|
||||
<title>{{$campaign->title}} - Campaign Details</title>
|
||||
<link rel="stylesheet" href="/css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<layout name="admin">
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1>{campaign.title}</h1>
|
||||
<h1>{{$campaign->title}}</h1>
|
||||
<div class="header-actions">
|
||||
<a href="/admin/presave/campaigns/{campaign.id}/edit" class="btn btn-primary">Edit</a>
|
||||
<a href="/admin/presave/campaigns/{{$campaign->id}}/edit" class="btn btn-primary">Edit</a>
|
||||
<a href="/admin/presave/campaigns" class="btn btn-secondary">Back to List</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="campaign-details">
|
||||
<div class="campaign-cover-large">
|
||||
<img src="{campaign.coverImageUrl}" alt="{campaign.title}">
|
||||
<img src="{{$campaign->coverImageUrl}}" alt="{{$campaign->title}}">
|
||||
</div>
|
||||
|
||||
<div class="campaign-info-section">
|
||||
<h2>Campaign Information</h2>
|
||||
<dl class="info-list">
|
||||
<dt>Artist</dt>
|
||||
<dd>{campaign.artistName}</dd>
|
||||
<dd>{{$campaign->artistName}}</dd>
|
||||
|
||||
<dt>Status</dt>
|
||||
<dd><span class="badge badge-{campaign.status.value}">{campaign.status.value}</span></dd>
|
||||
<dd><span class="badge badge-{{$campaign->status->value}}">{{$campaign->status->value}}</span></dd>
|
||||
|
||||
<dt>Release Date</dt>
|
||||
<dd>{campaign.releaseDate|date}</dd>
|
||||
<dd>{{$campaign->releaseDate}}</dd>
|
||||
|
||||
<if condition="{campaign.description}">
|
||||
<dt>Description</dt>
|
||||
<dd>{campaign.description}</dd>
|
||||
</if>
|
||||
<dt if="{{$campaign->description}}">Description</dt>
|
||||
<dd if="{{$campaign->description}}">{{$campaign->description}}</dd>
|
||||
|
||||
<dt>Available Platforms</dt>
|
||||
<dd>
|
||||
<div class="platform-links">
|
||||
<for items="{campaign.trackUrls}" as="platform=>url">
|
||||
<a href="{url}" target="_blank" class="platform-badge">{platform}</a>
|
||||
</for>
|
||||
<a href="{{$url}}" target="_blank" class="platform-badge" foreach="$campaign->trackUrls as $platform => $url">{{$platform}}</a>
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
@@ -55,19 +51,19 @@
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Total Registrations</h3>
|
||||
<p class="stat-value">{stats.total_registrations}</p>
|
||||
<p class="stat-value">{{$stats['total_registrations']}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>Pending</h3>
|
||||
<p class="stat-value">{stats.pending}</p>
|
||||
<p class="stat-value">{{$stats['pending']}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>Completed</h3>
|
||||
<p class="stat-value">{stats.completed}</p>
|
||||
<p class="stat-value">{{$stats['completed']}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>Failed</h3>
|
||||
<p class="stat-value">{stats.failed}</p>
|
||||
<p class="stat-value">{{$stats['failed']}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,15 +71,15 @@
|
||||
<div class="platform-stats">
|
||||
<div class="platform-stat">
|
||||
<span class="platform-name">Spotify</span>
|
||||
<span class="platform-count">{stats.by_platform.spotify}</span>
|
||||
<span class="platform-count">{{$stats['by_platform']['spotify']}}</span>
|
||||
</div>
|
||||
<div class="platform-stat">
|
||||
<span class="platform-name">Apple Music</span>
|
||||
<span class="platform-count">{stats.by_platform.apple_music}</span>
|
||||
<span class="platform-count">{{$stats['by_platform']['apple_music']}}</span>
|
||||
</div>
|
||||
<div class="platform-stat">
|
||||
<span class="platform-name">Tidal</span>
|
||||
<span class="platform-count">{stats.by_platform.tidal}</span>
|
||||
<span class="platform-count">{{$stats['by_platform']['tidal']}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,83 +87,61 @@
|
||||
<div class="campaign-actions-section">
|
||||
<h2>Campaign Actions</h2>
|
||||
<div class="action-buttons">
|
||||
<if condition="{campaign.status.value === 'draft'}">
|
||||
<form action="/admin/presave/campaigns/{campaign.id}/activate" method="POST">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-success">Activate Campaign</button>
|
||||
</form>
|
||||
</if>
|
||||
<form action="/admin/presave/campaigns/{{$campaign->id}}/activate" method="POST" if="{{$campaign->status->value === 'draft'}}">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-success">Activate Campaign</button>
|
||||
</form>
|
||||
|
||||
<if condition="{campaign.status.value === 'active'}">
|
||||
<form action="/admin/presave/campaigns/{campaign.id}/pause" method="POST">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-warning">Pause Campaign</button>
|
||||
</form>
|
||||
</if>
|
||||
<form action="/admin/presave/campaigns/{{$campaign->id}}/pause" method="POST" if="{{$campaign->status->value === 'active'}}">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-warning">Pause Campaign</button>
|
||||
</form>
|
||||
|
||||
<if condition="{campaign.status.value === 'paused'}">
|
||||
<form action="/admin/presave/campaigns/{campaign.id}/activate" method="POST">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-success">Resume Campaign</button>
|
||||
</form>
|
||||
</if>
|
||||
<form action="/admin/presave/campaigns/{{$campaign->id}}/activate" method="POST" if="{{$campaign->status->value === 'paused'}}">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-success">Resume Campaign</button>
|
||||
</form>
|
||||
|
||||
<if condition="{campaign.status.value !== 'completed'}">
|
||||
<form action="/admin/presave/campaigns/{campaign.id}/complete" method="POST">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-primary">Mark as Completed</button>
|
||||
</form>
|
||||
</if>
|
||||
<form action="/admin/presave/campaigns/{{$campaign->id}}/complete" method="POST" if="{{$campaign->status->value !== 'completed'}}">
|
||||
<csrf-token />
|
||||
<button type="submit" class="btn btn-primary">Mark as Completed</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="registrations-section">
|
||||
<h2>Registrations</h2>
|
||||
<if condition="{registrations && registrations.length > 0}">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User ID</th>
|
||||
<th>Platform</th>
|
||||
<th>Status</th>
|
||||
<th>Registered</th>
|
||||
<th>Processed</th>
|
||||
<th>Error</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<for items="{registrations}" as="registration">
|
||||
<tr>
|
||||
<td>{registration.userId}</td>
|
||||
<td>{registration.platform.value}</td>
|
||||
<td><span class="badge badge-{registration.status.value}">{registration.status.value}</span></td>
|
||||
<td>{registration.registeredAt|datetime}</td>
|
||||
<td>
|
||||
<if condition="{registration.processedAt}">
|
||||
{registration.processedAt|datetime}
|
||||
</if>
|
||||
<if condition="{!registration.processedAt}">
|
||||
-
|
||||
</if>
|
||||
</td>
|
||||
<td>
|
||||
<if condition="{registration.errorMessage}">
|
||||
<span class="error-message" title="{registration.errorMessage}">
|
||||
{registration.errorMessage|truncate:50}
|
||||
</span>
|
||||
</if>
|
||||
<if condition="{!registration.errorMessage}">
|
||||
-
|
||||
</if>
|
||||
</td>
|
||||
</tr>
|
||||
</for>
|
||||
</tbody>
|
||||
</table>
|
||||
</if>
|
||||
<if condition="{!registrations || registrations.length === 0}">
|
||||
<p class="text-muted">No registrations yet.</p>
|
||||
</if>
|
||||
<table class="data-table" if="{{$registrations && count($registrations) > 0}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User ID</th>
|
||||
<th>Platform</th>
|
||||
<th>Status</th>
|
||||
<th>Registered</th>
|
||||
<th>Processed</th>
|
||||
<th>Error</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr foreach="$registrations as $registration">
|
||||
<td>{{$registration->userId}}</td>
|
||||
<td>{{$registration->platform->value}}</td>
|
||||
<td><span class="badge badge-{{$registration->status->value}}">{{$registration->status->value}}</span></td>
|
||||
<td>{{$registration->registeredAt}}</td>
|
||||
<td>
|
||||
<span if="{{$registration->processedAt}}">{{$registration->processedAt}}</span>
|
||||
<span if="{{!$registration->processedAt}}">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="error-message" title="{{$registration->errorMessage}}" if="{{$registration->errorMessage}}">
|
||||
{{substr($registration->errorMessage, 0, 50)}}
|
||||
</span>
|
||||
<span if="{{!$registration->errorMessage}}">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-muted" if="{{!$registrations || count($registrations) === 0}}">No registrations yet.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="section">
|
||||
<h2>{{ title }}</h2>
|
||||
<h2>{{$title}}</h2>
|
||||
|
||||
<!-- Connection Status -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Connection Status</h3>
|
||||
<p><strong>Status:</strong> {{ redis.status }}</p>
|
||||
<p><strong>Connected:</strong> {{ redis.is_connected }}</p>
|
||||
<p><strong>Status:</strong> {{$redis['status']}}</p>
|
||||
<p><strong>Connected:</strong> {{$redis['is_connected']}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Conditional sections will work once ForProcessor/IfProcessor are fixed -->
|
||||
<div class="stat-card">
|
||||
<h3>Debug Information</h3>
|
||||
<p><strong>Has Basic Info:</strong> {{ redis.has_basic_info }}</p>
|
||||
<p><strong>Has Error:</strong> {{ redis.has_error }}</p>
|
||||
<p><strong>Has Databases:</strong> {{ redis.has_databases }}</p>
|
||||
<p><strong>Has Cache Patterns:</strong> {{ redis.has_cache_patterns }}</p>
|
||||
<p><strong>Has Basic Info:</strong> {{$redis['has_basic_info']}}</p>
|
||||
<p><strong>Has Error:</strong> {{$redis['has_error']}}</p>
|
||||
<p><strong>Has Databases:</strong> {{$redis['has_databases']}}</p>
|
||||
<p><strong>Has Cache Patterns:</strong> {{$redis['has_cache_patterns']}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,23 +5,23 @@
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Overview</h3>
|
||||
<p><strong>Total Routes:</strong> {{ total_routes }}</p>
|
||||
<p><strong>Admin Routes:</strong> {{ admin_routes_count }}</p>
|
||||
<p><strong>API Routes:</strong> {{ api_routes_count }}</p>
|
||||
<p><strong>Total Routes:</strong> {{$total_routes}}</p>
|
||||
<p><strong>Admin Routes:</strong> {{$admin_routes_count}}</p>
|
||||
<p><strong>API Routes:</strong> {{$api_routes_count}}</p>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>HTTP Methods</h3>
|
||||
<p><strong>GET:</strong> {{ get_routes_count }}</p>
|
||||
<p><strong>POST:</strong> {{ post_routes_count }}</p>
|
||||
<p><strong>PUT/PATCH:</strong> {{ put_routes_count }}</p>
|
||||
<p><strong>DELETE:</strong> {{ delete_routes_count }}</p>
|
||||
<p><strong>GET:</strong> {{$get_routes_count}}</p>
|
||||
<p><strong>POST:</strong> {{$post_routes_count}}</p>
|
||||
<p><strong>PUT/PATCH:</strong> {{$put_routes_count}}</p>
|
||||
<p><strong>DELETE:</strong> {{$delete_routes_count}}</p>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>Authentication</h3>
|
||||
<p><strong>Protected Routes:</strong> {{ protected_routes_count }}</p>
|
||||
<p><strong>Public Routes:</strong> {{ public_routes_count }}</p>
|
||||
<p><strong>Protected Routes:</strong> {{$protected_routes_count}}</p>
|
||||
<p><strong>Public Routes:</strong> {{$public_routes_count}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,8 +32,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<if condition="{{ middlewares }}">
|
||||
<div class="stats-grid">
|
||||
<div class="stats-grid" if="{{$middlewares}}">
|
||||
<div class="stat-card full-width">
|
||||
<h3>Middleware Usage</h3>
|
||||
<table>
|
||||
@@ -45,16 +44,13 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<for items="{{ middlewares }}" key="middleware" value="data">
|
||||
<tr>
|
||||
<td>{{ middleware }}</td>
|
||||
<td>{{ data.count }}</td>
|
||||
<td style="font-size: 12px;">{{ data.routes }}</td>
|
||||
<tr foreach="$middlewares as $middleware => $data">
|
||||
<td>{{$middleware}}</td>
|
||||
<td>{{$data['count']}}</td>
|
||||
<td style="font-size: 12px;">{{$data['routes']}}</td>
|
||||
</tr>
|
||||
</for>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</if>
|
||||
</div>
|
||||
@@ -6,19 +6,15 @@
|
||||
<div style="margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<input type="text" id="serviceFilter" placeholder="Dienste filtern..."
|
||||
style="padding: 8px 12px; border: 1px solid var(--gray-300); border-radius: 6px; width: 300px;">
|
||||
<span class="services-count" style="color: var(--gray-600);">{{ servicesCount }} Dienste insgesamt</span>
|
||||
<span class="services-count" style="color: var(--gray-600);">{{$servicesCount}} Dienste insgesamt</span>
|
||||
</div>
|
||||
|
||||
<div class="stats-grid" id="serviceList">
|
||||
<for var="service" in="services">
|
||||
<div class="stat-card service-item">
|
||||
<h3>{{ service.name }}</h3>
|
||||
<p><strong>Kategorie:</strong> {{ service.category }}</p>
|
||||
<if condition="{{ service.subCategory }}">
|
||||
<p><strong>Unterkategorie:</strong> {{ service.subCategory }}</p>
|
||||
</if>
|
||||
</div>
|
||||
</for>
|
||||
<div class="stat-card service-item" foreach="$services as $service">
|
||||
<h3>{{$service['name']}}</h3>
|
||||
<p><strong>Kategorie:</strong> {{$service['category']}}</p>
|
||||
<p if="{{$service['subCategory']}}"><strong>Unterkategorie:</strong> {{$service['subCategory']}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,6 +32,6 @@ document.getElementById('serviceFilter').addEventListener('input', function() {
|
||||
});
|
||||
|
||||
document.querySelector('.services-count').textContent =
|
||||
visibleCount + ' von {{ servicesCount }} Diensten';
|
||||
visibleCount + ' von {{$servicesCount}} Diensten';
|
||||
});
|
||||
</script>
|
||||
@@ -1,25 +1,19 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="section">
|
||||
<h2>{{ title }}</h2>
|
||||
<h2>{{$title}}</h2>
|
||||
|
||||
<if condition="{{ success }}">
|
||||
<div class="alert alert-success">
|
||||
<strong>Erfolg!</strong> {{ description }}
|
||||
</div>
|
||||
<else>
|
||||
<if condition="{{ error }}">
|
||||
<div class="alert alert-danger">
|
||||
<strong>Fehler:</strong> {{ description }}
|
||||
</div>
|
||||
<else>
|
||||
<p>{{ description }}</p>
|
||||
</if>
|
||||
</if>
|
||||
<div class="alert alert-success" if="{{$success}}">
|
||||
<strong>Erfolg!</strong> {{$description}}
|
||||
</div>
|
||||
<div class="alert alert-danger" if="{{$error}}">
|
||||
<strong>Fehler:</strong> {{$description}}
|
||||
</div>
|
||||
<p if="{{!$success && !$error}}">{{$description}}</p>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>Bild hochladen</h3>
|
||||
{{formHtml}}
|
||||
{{$formHtml}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<layout name="admin" />
|
||||
|
||||
<div class="section">
|
||||
<h2>{{ title }}</h2>
|
||||
<p>{{ description }}</p>
|
||||
<h2>{{$title}}</h2>
|
||||
<p>{{$description}}</p>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>JavaScript Upload Test</h3>
|
||||
|
||||
Reference in New Issue
Block a user