Fix: Implement public status page functionality
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { RefreshCw, AlertCircle } from 'lucide-react';
|
||||
import { usePublicStatusPageData } from './hooks/usePublicStatusPageData';
|
||||
import { StatusPageHeader } from './StatusPageHeader';
|
||||
import { CurrentStatusSection } from './CurrentStatusSection';
|
||||
@@ -11,19 +12,36 @@ import { PublicStatusPageFooter } from './PublicStatusPageFooter';
|
||||
|
||||
export const PublicStatusPage = () => {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
console.log('PublicStatusPage - slug from params:', slug);
|
||||
|
||||
const { page, components, services, uptimeData, loading, error } = usePublicStatusPageData(slug);
|
||||
const [lastUpdated, setLastUpdated] = useState(new Date());
|
||||
|
||||
// Auto-refresh every 30 seconds
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setLastUpdated(new Date());
|
||||
// The usePublicStatusPageData hook handles data refetching
|
||||
}, 30000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// Apply theme to document
|
||||
useEffect(() => {
|
||||
if (page) {
|
||||
const root = document.documentElement;
|
||||
|
||||
// Remove any existing theme classes
|
||||
root.classList.remove('dark', 'light');
|
||||
|
||||
// Apply the selected theme
|
||||
if (page.theme === 'dark') {
|
||||
root.classList.add('dark');
|
||||
root.classList.remove('light');
|
||||
} else {
|
||||
} else if (page.theme === 'light') {
|
||||
root.classList.add('light');
|
||||
root.classList.remove('dark');
|
||||
}
|
||||
// For 'default' theme, don't add any class (uses system preference)
|
||||
}
|
||||
|
||||
// Cleanup on unmount
|
||||
@@ -33,12 +51,18 @@ export const PublicStatusPage = () => {
|
||||
};
|
||||
}, [page?.theme]);
|
||||
|
||||
console.log('PublicStatusPage state:', { loading, error, page: !!page, components: components.length, services: services.length });
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
|
||||
<p className="text-muted-foreground">Loading status page...</p>
|
||||
<div className="text-center space-y-4">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto"></div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-lg font-medium text-foreground">Loading Status Page</p>
|
||||
<p className="text-sm text-muted-foreground">Fetching real-time system status...</p>
|
||||
<p className="text-xs text-muted-foreground">Slug: {slug || 'No slug provided'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -47,10 +71,26 @@ export const PublicStatusPage = () => {
|
||||
if (error || !page) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl font-bold text-foreground mb-2">Page Not Found</h1>
|
||||
<p className="text-muted-foreground mb-4">{error || 'The requested status page could not be found.'}</p>
|
||||
<Button onClick={() => window.history.back()}>Go Back</Button>
|
||||
<div className="text-center space-y-6 max-w-md">
|
||||
<div className="mx-auto h-16 w-16 bg-red-100 dark:bg-red-900/20 rounded-full flex items-center justify-center">
|
||||
<AlertCircle className="h-8 w-8 text-red-600 dark:text-red-400" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h1 className="text-2xl font-bold text-foreground">Status Page Not Found</h1>
|
||||
<p className="text-muted-foreground">
|
||||
{error || 'The requested status page could not be found or is not publicly accessible.'}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">Slug: {slug || 'No slug provided'}</p>
|
||||
</div>
|
||||
<div className="flex gap-3 justify-center">
|
||||
<Button onClick={() => window.history.back()} variant="outline">
|
||||
Go Back
|
||||
</Button>
|
||||
<Button onClick={() => window.location.reload()} className="gap-2">
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -62,7 +102,7 @@ export const PublicStatusPage = () => {
|
||||
<StatusPageHeader page={page} />
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
<main className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Current Status */}
|
||||
<CurrentStatusSection page={page} components={components} services={services} />
|
||||
|
||||
@@ -78,7 +118,7 @@ export const PublicStatusPage = () => {
|
||||
|
||||
{/* Footer */}
|
||||
<PublicStatusPageFooter page={page} />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Custom CSS */}
|
||||
{page.custom_css && (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
import { StatusBadge } from '@/components/operational-page/StatusBadge';
|
||||
import { OperationalPageRecord } from '@/types/operational.types';
|
||||
import { Shield, Globe, ExternalLink } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
interface StatusPageHeaderProps {
|
||||
page: OperationalPageRecord;
|
||||
@@ -8,23 +9,63 @@ interface StatusPageHeaderProps {
|
||||
|
||||
export const StatusPageHeader = ({ page }: StatusPageHeaderProps) => {
|
||||
return (
|
||||
<div className="bg-card shadow-sm border-b border-border">
|
||||
<div className="max-w-4xl mx-auto px-4 py-6">
|
||||
<header className="bg-background border-b border-border">
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
{page.logo_url && (
|
||||
<img src={page.logo_url} alt="Logo" className="h-8 w-8 rounded" />
|
||||
{page.logo_url ? (
|
||||
<img
|
||||
src={page.logo_url}
|
||||
alt={`${page.title} logo`}
|
||||
className="h-12 w-12 rounded-lg object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="h-12 w-12 bg-primary/10 rounded-lg flex items-center justify-center">
|
||||
<Shield className="h-6 w-6 text-primary" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-card-foreground">{page.title}</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{page.description}
|
||||
</p>
|
||||
<h1 className="text-3xl font-bold text-foreground">{page.title}</h1>
|
||||
<p className="text-muted-foreground mt-1">{page.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<StatusBadge status={page.status} />
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
{page.custom_domain && (
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<a
|
||||
href={`https://${page.custom_domain}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Globe className="h-4 w-4" />
|
||||
Visit Site
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
</a>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<div className="text-right text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="h-2 w-2 bg-green-500 rounded-full animate-pulse"></div>
|
||||
<span className="font-medium">Live Status</span>
|
||||
</div>
|
||||
<div className="text-xs">
|
||||
Auto-updated every 30s
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Breadcrumb */}
|
||||
<div className="mt-6 flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Shield className="h-4 w-4" />
|
||||
<span>Status Page</span>
|
||||
<span>•</span>
|
||||
<span className="text-foreground font-medium">{page.title}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user