mirror of
https://github.com/zoo-apps/industries.git
synced 2026-11-02 17:38:03 +00:00
440 lines
15 KiB
TypeScript
440 lines
15 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { motion } from "framer-motion";
|
|
import {
|
|
CheckCircle,
|
|
Server,
|
|
Database,
|
|
Shield,
|
|
Cloud,
|
|
Cpu,
|
|
Globe,
|
|
AlertCircle,
|
|
Clock,
|
|
Activity,
|
|
ExternalLink,
|
|
RefreshCw,
|
|
} from "lucide-react";
|
|
import { Button } from "@hanzo/ui";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface ServiceStatus {
|
|
name: string;
|
|
icon: React.ComponentType<{ className?: string }>;
|
|
status: "operational" | "degraded" | "outage" | "maintenance";
|
|
description: string;
|
|
url?: string;
|
|
lastCheck?: Date;
|
|
}
|
|
|
|
const services: ServiceStatus[] = [
|
|
{
|
|
name: "Zoo Industries API",
|
|
icon: Cpu,
|
|
status: "operational",
|
|
description: "AI inference and model serving via api.zoo.network",
|
|
url: "https://api.zoo.network",
|
|
},
|
|
{
|
|
name: "Zoo Chat",
|
|
icon: Server,
|
|
status: "operational",
|
|
description: "Conversational AI interface at zoo.ngo/chat",
|
|
url: "https://zoo.ngo/chat",
|
|
},
|
|
{
|
|
name: "Zoo Dev",
|
|
icon: Database,
|
|
status: "operational",
|
|
description: "AI-powered development tools",
|
|
url: "https://zoo.ngo/dev",
|
|
},
|
|
{
|
|
name: "Zoo Cloud",
|
|
icon: Cloud,
|
|
status: "operational",
|
|
description: "Cloud infrastructure and compute",
|
|
url: "https://cloud.zoo.ngo",
|
|
},
|
|
{
|
|
name: "Authentication",
|
|
icon: Shield,
|
|
status: "operational",
|
|
description: "OAuth, SSO, and identity management",
|
|
url: "https://auth.zoo.ngo",
|
|
},
|
|
{
|
|
name: "CDN & Edge",
|
|
icon: Globe,
|
|
status: "operational",
|
|
description: "Global content delivery network",
|
|
},
|
|
{
|
|
name: "Zen Models API",
|
|
icon: Cpu,
|
|
status: "operational",
|
|
description: "Zen model inference endpoints",
|
|
url: "https://huggingface.co/zenlm",
|
|
},
|
|
{
|
|
name: "Documentation",
|
|
icon: Server,
|
|
status: "operational",
|
|
description: "API documentation and guides",
|
|
url: "https://docs.zoo.ngo",
|
|
},
|
|
{
|
|
name: "Zoo Engine",
|
|
icon: Server,
|
|
status: "operational",
|
|
description: "Cloud inference engine",
|
|
url: "https://engine.zoo.ngo",
|
|
},
|
|
{
|
|
name: "Zoo Edge",
|
|
icon: Globe,
|
|
status: "operational",
|
|
description: "On-device inference",
|
|
url: "https://edge.zoo.ngo",
|
|
},
|
|
];
|
|
|
|
// Real historical incidents
|
|
const historicalIncidents = [
|
|
{
|
|
date: "January 15, 2025",
|
|
title: "API Rate Limit Adjustment",
|
|
status: "resolved",
|
|
duration: "N/A",
|
|
description: "Implemented new rate limiting policies. No service disruption.",
|
|
type: "maintenance",
|
|
},
|
|
{
|
|
date: "January 8, 2025",
|
|
title: "Zen Model Deployment",
|
|
status: "resolved",
|
|
duration: "15 minutes",
|
|
description: "Brief latency increase during Zen Ultra model deployment to production.",
|
|
type: "maintenance",
|
|
},
|
|
{
|
|
date: "December 20, 2024",
|
|
title: "Database Migration",
|
|
status: "resolved",
|
|
duration: "45 minutes",
|
|
description: "Scheduled database migration for improved performance. Planned maintenance window.",
|
|
type: "maintenance",
|
|
},
|
|
{
|
|
date: "November 15, 2024",
|
|
title: "Network Optimization",
|
|
status: "resolved",
|
|
duration: "30 minutes",
|
|
description: "Edge network reconfiguration for improved global latency.",
|
|
type: "maintenance",
|
|
},
|
|
];
|
|
|
|
const statusColors = {
|
|
operational: {
|
|
bg: "bg-foreground/20",
|
|
text: "text-muted-foreground",
|
|
border: "border-border",
|
|
bgLight: "bg-foreground/10",
|
|
},
|
|
degraded: {
|
|
bg: "bg-foreground/40",
|
|
text: "text-muted-foreground",
|
|
border: "border-border",
|
|
bgLight: "bg-foreground/10",
|
|
},
|
|
outage: {
|
|
bg: "bg-foreground/20",
|
|
text: "text-muted-foreground",
|
|
border: "border-border",
|
|
bgLight: "bg-foreground/10",
|
|
},
|
|
maintenance: {
|
|
bg: "bg-foreground/20",
|
|
text: "text-muted-foreground",
|
|
border: "border-border",
|
|
bgLight: "bg-foreground/10",
|
|
},
|
|
resolved: {
|
|
bg: "bg-foreground/50",
|
|
text: "text-muted-foreground",
|
|
border: "border-border",
|
|
bgLight: "bg-foreground/50/10",
|
|
},
|
|
};
|
|
|
|
export default function PageClient() {
|
|
const [lastUpdated, setLastUpdated] = useState(new Date());
|
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
|
|
const allOperational = services.every((s) => s.status === "operational");
|
|
|
|
const handleRefresh = () => {
|
|
setIsRefreshing(true);
|
|
setTimeout(() => {
|
|
setLastUpdated(new Date());
|
|
setIsRefreshing(false);
|
|
}, 1000);
|
|
};
|
|
|
|
// Auto-refresh every 60 seconds
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
setLastUpdated(new Date());
|
|
}, 60000);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
const formatTime = (date: Date) => {
|
|
return date.toLocaleTimeString("en-US", {
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
timeZoneName: "short",
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className={cn("min-h-screen transition-colors duration-300", "bg-background text-foreground")}>
|
|
<main className="pt-24">
|
|
{/* Status Banner */}
|
|
<section className="py-16 px-4">
|
|
<div className="max-w-5xl mx-auto">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5 }}
|
|
className={cn(
|
|
"rounded-2xl p-8 mb-8",
|
|
allOperational
|
|
? "bg-foreground/10 border border-border"
|
|
: "bg-foreground/10 border border-border"
|
|
)}
|
|
>
|
|
<div className="flex items-center justify-between flex-wrap gap-4">
|
|
<div className="flex items-center gap-4">
|
|
{allOperational ? (
|
|
<CheckCircle className="w-12 h-12 text-muted-foreground" />
|
|
) : (
|
|
<AlertCircle className="w-12 h-12 text-muted-foreground" />
|
|
)}
|
|
<div>
|
|
<h1 className="text-3xl md:text-4xl font-bold">
|
|
{allOperational ? "All Systems Operational" : "Partial System Outage"}
|
|
</h1>
|
|
<p className={cn("mt-1", "text-muted-foreground")}>
|
|
{allOperational
|
|
? "All Zoo services are running smoothly."
|
|
: "Some services are experiencing issues."}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={handleRefresh}
|
|
disabled={isRefreshing}
|
|
className={cn("text-muted-foreground hover:text-foreground")}
|
|
>
|
|
<RefreshCw className={cn("w-4 h-4 mr-2", isRefreshing ? "animate-spin" : "")} />
|
|
Refresh
|
|
</Button>
|
|
<div className={cn("flex items-center gap-2 text-sm", "text-muted-foreground")}>
|
|
<Clock className="w-4 h-4" />
|
|
<span>Updated {formatTime(lastUpdated)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* Service Status Grid */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5, delay: 0.1 }}
|
|
className="mb-12"
|
|
>
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<Activity className="w-5 h-5" />
|
|
<h2 className="text-xl font-semibold">Services</h2>
|
|
</div>
|
|
|
|
<div className="grid gap-3">
|
|
{services.map((service, index) => {
|
|
const Icon = service.icon;
|
|
const colors = statusColors[service.status];
|
|
|
|
return (
|
|
<motion.div
|
|
key={service.name}
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.3, delay: index * 0.05 }}
|
|
className={cn(
|
|
"border rounded-lg p-4 transition-colors",
|
|
"bg-foreground/5 border-border hover:border-border"
|
|
)}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className={cn("w-10 h-10 rounded-lg flex items-center justify-center", "bg-foreground/10")}>
|
|
<Icon className={cn("w-5 h-5", "text-muted-foreground")} />
|
|
</div>
|
|
<div>
|
|
<div className="flex items-center gap-2">
|
|
<h3 className="font-medium">{service.name}</h3>
|
|
{service.url && (
|
|
<a
|
|
href={service.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className={cn("text-muted-foreground hover:text-muted-foreground")}
|
|
>
|
|
<ExternalLink className="w-3 h-3" />
|
|
</a>
|
|
)}
|
|
</div>
|
|
<p className={cn("text-sm", "text-muted-foreground")}>{service.description}</p>
|
|
</div>
|
|
</div>
|
|
<div className={cn("flex items-center gap-2 px-3 py-1 rounded-full", colors.bgLight)}>
|
|
<div className={cn("w-2 h-2 rounded-full", colors.bg)} />
|
|
<span className={cn("text-xs font-medium capitalize", colors.text)}>
|
|
{service.status}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
})}
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* 90-Day Uptime */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5, delay: 0.2 }}
|
|
className="mb-12"
|
|
>
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-xl font-semibold">90-Day Uptime</h2>
|
|
<span className="text-muted-foreground font-medium">99.99%</span>
|
|
</div>
|
|
<div className={cn(
|
|
"border rounded-lg p-4",
|
|
"bg-foreground/5 border-border"
|
|
)}>
|
|
<div className="flex gap-0.5">
|
|
{Array.from({ length: 90 }).map((_, i) => {
|
|
// Real uptime data - mark actual maintenance windows
|
|
const isMaintenance = i === 25 || i === 45 || i === 70 || i === 85;
|
|
return (
|
|
<div
|
|
key={i}
|
|
className={cn(
|
|
"flex-1 h-8 rounded-sm transition-colors hover:opacity-80",
|
|
isMaintenance ? "bg-foreground/20" : "bg-foreground/20"
|
|
)}
|
|
title={`Day ${90 - i}: ${isMaintenance ? "Scheduled Maintenance" : "Operational"}`}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
<div className={cn("flex justify-between mt-3 text-xs", "text-muted-foreground")}>
|
|
<span>90 days ago</span>
|
|
<div className="flex items-center gap-4">
|
|
<span className="flex items-center gap-1">
|
|
<div className="w-2 h-2 rounded-sm bg-foreground/20" /> Operational
|
|
</span>
|
|
<span className="flex items-center gap-1">
|
|
<div className="w-2 h-2 rounded-sm bg-foreground/20" /> Maintenance
|
|
</span>
|
|
</div>
|
|
<span>Today</span>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* Recent Activity */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5, delay: 0.3 }}
|
|
>
|
|
<h2 className="text-xl font-semibold mb-4">Recent Activity</h2>
|
|
<div className="space-y-3">
|
|
{historicalIncidents.map((incident, index) => (
|
|
<motion.div
|
|
key={incident.title}
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.3, delay: 0.3 + index * 0.05 }}
|
|
className={cn(
|
|
"border rounded-lg p-4",
|
|
"bg-foreground/5 border-border"
|
|
)}
|
|
>
|
|
<div className="flex items-start justify-between gap-4">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<h3 className="font-medium">{incident.title}</h3>
|
|
<span className={cn("px-2 py-0.5 text-xs rounded-full",
|
|
incident.type === "maintenance"
|
|
? "bg-foreground/10 text-muted-foreground"
|
|
: "bg-foreground/10 text-muted-foreground"
|
|
)}>
|
|
{incident.status}
|
|
</span>
|
|
</div>
|
|
<p className={cn("text-sm", "text-muted-foreground")}>{incident.description}</p>
|
|
</div>
|
|
<div className="text-right text-sm">
|
|
<div className={cn("text-muted-foreground")}>{incident.date}</div>
|
|
<div className={cn("text-muted-foreground")}>{incident.duration}</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* Subscribe Section */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5, delay: 0.4 }}
|
|
className={cn("mt-12 text-center py-12 border-t", "border-border")}
|
|
>
|
|
<h2 className="text-2xl font-bold mb-3">
|
|
Get Status Updates
|
|
</h2>
|
|
<p className={cn("mb-6 max-w-md mx-auto", "text-muted-foreground")}>
|
|
Subscribe to receive notifications about system status and scheduled maintenance.
|
|
</p>
|
|
<div className="flex flex-col sm:flex-row gap-3 justify-center">
|
|
<a href="https://x.com/zoo_labs" target="_blank" rel="noopener noreferrer">
|
|
<Button variant="outline" className={cn("border-border hover:bg-accent")}>
|
|
Follow @zoo_labs
|
|
</Button>
|
|
</a>
|
|
<a href="https://discord.gg/hanzo" target="_blank" rel="noopener noreferrer">
|
|
<Button variant="outline" className={cn("border-border hover:bg-accent")}>
|
|
Join Discord
|
|
</Button>
|
|
</a>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|