Files
ismysiteup.online/FEATURE_PAGES_IMPLEMENTATION.md
2026-01-24 12:16:10 +02:00

18 KiB

Feature Pages Implementation - Complete Views & Charts

Individual Feature Detail Page Template

Create resources/views/features/show.blade.php:

@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:

@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:

@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:

@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:

<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:

<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:

<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:

<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:

<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:
./vendor/bin/sail artisan migrate
  1. Create the SiteFeature model
  2. Create FeaturesController
  3. Add routes
  4. Create view files
  5. Test feature toggles

Chart.js CDN

Already included in the show.blade.php template:

<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:

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.