Fix: Implement public status page functionality

This commit is contained in:
Tola Leng
2025-06-15 21:28:47 +08:00
parent 41fcaaaf05
commit 4486a79e58
2 changed files with 104 additions and 23 deletions
@@ -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>
);
};