Files

518 lines
18 KiB
Markdown
Raw Permalink Normal View History

2026-01-24 12:16:10 +02:00
# Feature Pages Implementation - Complete Views & Charts
## Individual Feature Detail Page Template
Create `resources/views/features/show.blade.php`:
```blade
@extends('layouts.app')
@section('title', $featureInfo['name'])
@section('content')
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1>
<i class="{{ $featureInfo['icon'] }} text-{{ $featureInfo['color'] }}"></i>
{{ $featureInfo['name'] }}
</h1>
<p class="lead text-muted">{{ $featureInfo['description'] }}</p>
</div>
<a href="{{ route('features.index') }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Back to Features
</a>
</div>
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="bi bi-check-circle"></i> {{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
<!-- Feature Statistics -->
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card bg-{{ $featureInfo['color'] }} bg-opacity-10">
<div class="card-body text-center">
<i class="{{ $featureInfo['icon'] }} fs-1 text-{{ $featureInfo['color'] }}"></i>
<h3 class="mt-2">{{ $sites->sum(fn($s) => $s->features->where('is_enabled', true)->count()) }}</h3>
<p class="text-muted mb-0">Active Sites</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-check-circle fs-1 text-success"></i>
<h3 class="mt-2" id="healthy-count">0</h3>
<p class="text-muted mb-0">Healthy</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-exclamation-triangle fs-1 text-warning"></i>
<h3 class="mt-2" id="warning-count">0</h3>
<p class="text-muted mb-0">Warnings</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-clock-history fs-1 text-info"></i>
<h3 class="mt-2">{{ now()->format('H:i') }}</h3>
<p class="text-muted mb-0">Last Updated</p>
</div>
</div>
</div>
</div>
<!-- Charts Section -->
@if(!empty($chartData))
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-graph-up"></i> Historical Data & Trends</h5>
</div>
<div class="card-body">
<div class="row">
@foreach($chartData as $siteId => $data)
@php
$site = $sites->find($siteId);
@endphp
<div class="col-lg-6 mb-4">
<h6>{{ $site->name }}</h6>
<canvas id="chart-{{ $siteId }}" height="200"></canvas>
</div>
@endforeach
</div>
</div>
</div>
@endif
<!-- Sites Management -->
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-list-ul"></i> Manage Sites</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Site</th>
<th>Status</th>
<th>Last Checked</th>
<th>Metrics</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach($sites as $site)
@php
$feature = $site->features->where('feature_type', $featureType)->first();
$isEnabled = $feature && $feature->is_enabled;
@endphp
<tr>
<td>
<strong>{{ $site->name }}</strong><br>
<small class="text-muted">{{ $site->url }}</small>
</td>
<td>
<form action="{{ route('features.toggle', [$site, $featureType]) }}" method="POST" class="d-inline">
@csrf
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
{{ $isEnabled ? 'checked' : '' }}
onchange="this.form.submit()">
<label class="form-check-label">
{{ $isEnabled ? 'Enabled' : 'Disabled' }}
</label>
</div>
</form>
</td>
<td>
@if($feature && $feature->last_checked_at)
{{ $feature->last_checked_at->diffForHumans() }}
@else
<span class="text-muted">Never</span>
@endif
</td>
<td>
@include('features.partials.metrics-' . $featureType, ['site' => $site])
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{{ route('dashboard.show', $site) }}" class="btn btn-outline-primary" title="Dashboard">
<i class="bi bi-bar-chart"></i>
</a>
<button type="button" class="btn btn-outline-secondary" title="Settings"
data-bs-toggle="modal" data-bs-target="#settings-{{ $site->id }}">
<i class="bi bi-gear"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<!-- Feature Information -->
<div class="card mt-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-info-circle"></i> About {{ $featureInfo['name'] }}</h5>
</div>
<div class="card-body">
@include('features.info.' . $featureType)
</div>
</div>
</div>
<!-- Settings Modals -->
@foreach($sites as $site)
<div class="modal fade" id="settings-{{ $site->id }}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ $site->name }} - Feature Settings</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form action="{{ route('features.settings', [$site, $featureType]) }}" method="POST">
@csrf
<div class="modal-body">
@include('features.settings.' . $featureType, ['site' => $site])
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Settings</button>
</div>
</form>
</div>
</div>
</div>
@endforeach
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const chartData = @json($chartData);
Object.keys(chartData).forEach(siteId => {
const ctx = document.getElementById('chart-' + siteId);
if (ctx) {
new Chart(ctx, {
type: 'line',
data: chartData[siteId],
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
},
tooltip: {
mode: 'index',
intersect: false,
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
});
});
</script>
@endpush
@endsection
```
## Feature-Specific Metrics Partials
Create `resources/views/features/partials/metrics-uptime.blade.php`:
```blade
@php
$latestCheck = $site->uptimeChecks()->latest()->first();
$uptimePercentage = $site->uptimeChecks()
->where('created_at', '>=', now()->subDays(30))
->avg('is_online') * 100;
@endphp
<div class="small">
@if($latestCheck)
<div class="mb-1">
<span class="badge bg-{{ $latestCheck->is_online ? 'success' : 'danger' }}">
{{ $latestCheck->is_online ? 'Online' : 'Offline' }}
</span>
</div>
<div class="text-muted">
Uptime: {{ number_format($uptimePercentage, 1) }}%
</div>
<div class="text-muted">
Response: {{ number_format($latestCheck->response_time, 0) }}ms
</div>
@else
<span class="text-muted">No data</span>
@endif
</div>
```
Create `resources/views/features/partials/metrics-ssl.blade.php`:
```blade
@php
$latestCert = $site->sslCertificates()->latest()->first();
@endphp
<div class="small">
@if($latestCert)
@php
$daysLeft = now()->diffInDays($latestCert->expires_at, false);
@endphp
<div class="mb-1">
<span class="badge bg-{{ $daysLeft > 30 ? 'success' : ($daysLeft > 7 ? 'warning' : 'danger') }}">
{{ $daysLeft }} days left
</span>
</div>
<div class="text-muted">
Expires: {{ $latestCert->expires_at->format('M d, Y') }}
</div>
@else
<span class="text-muted">No certificate data</span>
@endif
</div>
```
Create `resources/views/features/partials/metrics-broken_links.blade.php`:
```blade
@php
$brokenCount = $site->brokenLinks()->count();
$totalLinks = $site->brokenLinks()->distinct('url')->count();
@endphp
<div class="small">
<div class="mb-1">
<span class="badge bg-{{ $brokenCount > 0 ? 'danger' : 'success' }}">
{{ $brokenCount }} broken
</span>
</div>
@if($totalLinks > 0)
<div class="text-muted">
Total links scanned: {{ $totalLinks }}
</div>
@else
<span class="text-muted">No scan data</span>
@endif
</div>
```
## Feature Information Pages
Create `resources/views/features/info/uptime.blade.php`:
```blade
<h6>What is Uptime Monitoring?</h6>
<p>
Uptime monitoring checks if your website is accessible and responding correctly.
It performs regular HTTP/HTTPS requests and tracks:
</p>
<ul>
<li>Site availability (up/down status)</li>
<li>HTTP response codes</li>
<li>Response times</li>
<li>SSL certificate validity</li>
</ul>
<h6 class="mt-3">How It Works</h6>
<ol>
<li>Automated checks run every 5 minutes</li>
<li>Your site is accessed from our servers</li>
<li>Response time and status are recorded</li>
<li>Alerts are sent if your site is down</li>
</ol>
<h6 class="mt-3">Metrics Tracked</h6>
<ul>
<li><strong>Uptime Percentage:</strong> Overall availability over the last 30 days</li>
<li><strong>Response Time:</strong> How fast your site responds (in milliseconds)</li>
<li><strong>Status Codes:</strong> HTTP response codes (200, 404, 500, etc.)</li>
</ul>
<div class="alert alert-info mt-3">
<i class="bi bi-lightbulb"></i>
<strong>Tip:</strong> A healthy website should maintain 99%+ uptime and respond in under 1000ms.
</div>
```
Create `resources/views/features/info/ssl.blade.php`:
```blade
<h6>What is SSL Certificate Monitoring?</h6>
<p>
SSL certificate monitoring ensures your website's security certificate is valid and hasn't expired.
This is crucial for:
</p>
<ul>
<li>Maintaining visitor trust</li>
<li>SEO rankings (Google prefers HTTPS)</li>
<li>Data encryption and security</li>
<li>Browser compatibility</li>
</ul>
<h6 class="mt-3">What We Check</h6>
<ul>
<li>Certificate expiration date</li>
<li>Certificate validity</li>
<li>Issuer information</li>
<li>Certificate chain completeness</li>
</ul>
<h6 class="mt-3">Alert Thresholds</h6>
<ul>
<li><strong class="text-danger">7 days:</strong> Critical alert</li>
<li><strong class="text-warning">30 days:</strong> Warning alert</li>
<li><strong class="text-info">60 days:</strong> Info notification</li>
</ul>
<div class="alert alert-warning mt-3">
<i class="bi bi-exclamation-triangle"></i>
<strong>Important:</strong> Expired SSL certificates will cause browser warnings and prevent visitors from accessing your site.
</div>
```
## Feature Settings Forms
Create `resources/views/features/settings/uptime.blade.php`:
```blade
<div class="mb-3">
<label class="form-label">Check Interval</label>
<select name="settings[interval]" class="form-select">
<option value="5">Every 5 minutes</option>
<option value="10">Every 10 minutes</option>
<option value="15">Every 15 minutes</option>
<option value="30">Every 30 minutes</option>
<option value="60">Every hour</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Response Time Threshold (ms)</label>
<input type="number" name="settings[response_threshold]" class="form-control"
value="1000" min="100" max="10000">
<div class="form-text">Alert if response time exceeds this value</div>
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox" name="settings[follow_redirects]" class="form-check-input" id="follow-redirects" checked>
<label class="form-check-label" for="follow-redirects">
Follow HTTP redirects
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox" name="settings[verify_ssl]" class="form-check-input" id="verify-ssl" checked>
<label class="form-check-label" for="verify-ssl">
Verify SSL certificate
</label>
</div>
</div>
```
Create `resources/views/features/settings/ssl.blade.php`:
```blade
<div class="mb-3">
<label class="form-label">Alert When Days Remaining</label>
<div class="row g-2">
<div class="col-4">
<input type="number" name="settings[alert_days][]" class="form-control"
value="60" placeholder="60 days">
</div>
<div class="col-4">
<input type="number" name="settings[alert_days][]" class="form-control"
value="30" placeholder="30 days">
</div>
<div class="col-4">
<input type="number" name="settings[alert_days][]" class="form-control"
value="7" placeholder="7 days">
</div>
</div>
<div class="form-text">You'll receive alerts at these intervals before expiration</div>
</div>
<div class="mb-3">
<label class="form-label">Check Interval</label>
<select name="settings[check_interval]" class="form-select">
<option value="daily">Daily</option>
<option value="weekly">Weekly</option>
</select>
</div>
```
## Layout Integration
Add to your navigation in `resources/views/layouts/app.blade.php`:
```blade
<li class="nav-item">
<a class="nav-link {{ request()->is('features*') ? 'active' : '' }}" href="{{ route('features.index') }}">
<i class="bi bi-puzzle"></i> Features
</a>
</li>
```
## Running the Implementation
1. Run migration:
```bash
./vendor/bin/sail artisan migrate
```
2. Create the SiteFeature model
3. Create FeaturesController
4. Add routes
5. Create view files
6. Test feature toggles
## Chart.js CDN
Already included in the show.blade.php template:
```html
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
```
## Auto-enabling Features
You may want to automatically enable default features when a site is created. Add to Site model:
```php
protected static function booted()
{
static::created(function ($site) {
// Auto-enable core features
$site->enableFeature('uptime');
$site->enableFeature('ssl');
});
}
```
This provides a complete, production-ready feature management system with graphs and per-site controls.