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 { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { RefreshCw, AlertCircle } from 'lucide-react';
import { usePublicStatusPageData } from './hooks/usePublicStatusPageData'; import { usePublicStatusPageData } from './hooks/usePublicStatusPageData';
import { StatusPageHeader } from './StatusPageHeader'; import { StatusPageHeader } from './StatusPageHeader';
import { CurrentStatusSection } from './CurrentStatusSection'; import { CurrentStatusSection } from './CurrentStatusSection';
@@ -11,19 +12,36 @@ import { PublicStatusPageFooter } from './PublicStatusPageFooter';
export const PublicStatusPage = () => { export const PublicStatusPage = () => {
const { slug } = useParams<{ slug: string }>(); const { slug } = useParams<{ slug: string }>();
console.log('PublicStatusPage - slug from params:', slug);
const { page, components, services, uptimeData, loading, error } = usePublicStatusPageData(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 // Apply theme to document
useEffect(() => { useEffect(() => {
if (page) { if (page) {
const root = document.documentElement; const root = document.documentElement;
// Remove any existing theme classes
root.classList.remove('dark', 'light');
// Apply the selected theme
if (page.theme === 'dark') { if (page.theme === 'dark') {
root.classList.add('dark'); root.classList.add('dark');
root.classList.remove('light'); } else if (page.theme === 'light') {
} else {
root.classList.add('light'); root.classList.add('light');
root.classList.remove('dark');
} }
// For 'default' theme, don't add any class (uses system preference)
} }
// Cleanup on unmount // Cleanup on unmount
@@ -33,12 +51,18 @@ export const PublicStatusPage = () => {
}; };
}, [page?.theme]); }, [page?.theme]);
console.log('PublicStatusPage state:', { loading, error, page: !!page, components: components.length, services: services.length });
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-background flex items-center justify-center"> <div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center"> <div className="text-center space-y-4">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto"></div>
<p className="text-muted-foreground">Loading status page...</p> <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>
</div> </div>
); );
@@ -47,10 +71,26 @@ export const PublicStatusPage = () => {
if (error || !page) { if (error || !page) {
return ( return (
<div className="min-h-screen bg-background flex items-center justify-center"> <div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center"> <div className="text-center space-y-6 max-w-md">
<h1 className="text-2xl font-bold text-foreground mb-2">Page Not Found</h1> <div className="mx-auto h-16 w-16 bg-red-100 dark:bg-red-900/20 rounded-full flex items-center justify-center">
<p className="text-muted-foreground mb-4">{error || 'The requested status page could not be found.'}</p> <AlertCircle className="h-8 w-8 text-red-600 dark:text-red-400" />
<Button onClick={() => window.history.back()}>Go Back</Button> </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>
</div> </div>
); );
@@ -62,7 +102,7 @@ export const PublicStatusPage = () => {
<StatusPageHeader page={page} /> <StatusPageHeader page={page} />
{/* Main Content */} {/* 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 */} {/* Current Status */}
<CurrentStatusSection page={page} components={components} services={services} /> <CurrentStatusSection page={page} components={components} services={services} />
@@ -78,7 +118,7 @@ export const PublicStatusPage = () => {
{/* Footer */} {/* Footer */}
<PublicStatusPageFooter page={page} /> <PublicStatusPageFooter page={page} />
</div> </main>
{/* Custom CSS */} {/* Custom CSS */}
{page.custom_css && ( {page.custom_css && (
@@ -1,6 +1,7 @@
import { StatusBadge } from '@/components/operational-page/StatusBadge';
import { OperationalPageRecord } from '@/types/operational.types'; import { OperationalPageRecord } from '@/types/operational.types';
import { Shield, Globe, ExternalLink } from 'lucide-react';
import { Button } from '@/components/ui/button';
interface StatusPageHeaderProps { interface StatusPageHeaderProps {
page: OperationalPageRecord; page: OperationalPageRecord;
@@ -8,23 +9,63 @@ interface StatusPageHeaderProps {
export const StatusPageHeader = ({ page }: StatusPageHeaderProps) => { export const StatusPageHeader = ({ page }: StatusPageHeaderProps) => {
return ( return (
<div className="bg-card shadow-sm border-b border-border"> <header className="bg-background border-b border-border">
<div className="max-w-4xl mx-auto px-4 py-6"> <div className="max-w-4xl mx-auto px-4 py-8">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{page.logo_url && ( {page.logo_url ? (
<img src={page.logo_url} alt="Logo" className="h-8 w-8 rounded" /> <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> <div>
<h1 className="text-2xl font-bold text-card-foreground">{page.title}</h1> <h1 className="text-3xl font-bold text-foreground">{page.title}</h1>
<p className="text-sm text-muted-foreground"> <p className="text-muted-foreground mt-1">{page.description}</p>
{page.description}
</p>
</div> </div>
</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>
</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>
</header>
); );
}; };