"use client"
import { useState, useMemo } from "react"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import { useToast } from "@/components/ui/use-toast"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea"
import {
Plus,
ExternalLink,
Globe,
Lock,
AlertTriangle,
CheckCircle2,
Clock,
XCircle,
LayoutTemplate,
Activity,
TrendingUp,
Filter,
Search,
Wrench,
Trash2,
} from "lucide-react"
import {
getStatusPages,
deleteStatusPage,
getStatusPageUrl,
type StatusPage,
} from "@/lib/statuspages"
import {
getIncidents,
createIncident,
acknowledgeIncident,
resolveIncident,
closeIncident,
getIncidentStats,
type Incident,
type CreateIncidentRequest,
getSeverityColor,
getStatusColor,
formatDuration,
} from "@/lib/incidents"
import { StatusPageDialog } from "./status-page-dialog"
import { cn } from "@/lib/utils"
// Quick Stats Card Component
function QuickStatCard({
title,
value,
subtitle,
icon: Icon,
trend,
color = "blue",
}: {
title: string
value: string | number
subtitle?: string
icon: React.ElementType
trend?: { value: number; positive: boolean }
color?: "blue" | "green" | "yellow" | "red" | "purple"
}) {
const colorClasses = {
blue: "bg-blue-500/10 text-blue-600 border-blue-500/20",
green: "bg-green-500/10 text-green-600 border-green-500/20",
yellow: "bg-yellow-500/10 text-yellow-600 border-yellow-500/20",
red: "bg-red-500/10 text-red-600 border-red-500/20",
purple: "bg-purple-500/10 text-purple-600 border-purple-500/20",
}
return (
{title}
{value}
{subtitle && (
{subtitle}
)}
{trend && (
{trend.value}%
)}
)
}
// Incident Quick Actions Menu
function IncidentQuickActions({
incident,
onAcknowledge,
onResolve,
onClose,
}: {
incident: Incident
onAcknowledge: (id: string) => void
onResolve: (id: string) => void
onClose: (id: string) => void
}) {
const [showResolveDialog, setShowResolveDialog] = useState(false)
const [resolution, setResolution] = useState("")
return (
{incident.status === "open" && (
)}
{(incident.status === "open" || incident.status === "acknowledged") && (
<>
>
)}
{incident.status === "resolved" && (
)}
)
}
// Create Incident Dialog
function CreateIncidentDialog({
open,
onOpenChange,
onCreate,
}: {
open: boolean
onOpenChange: (open: boolean) => void
onCreate: (data: CreateIncidentRequest) => void
}) {
const [title, setTitle] = useState("")
const [description, setDescription] = useState("")
const [severity, setSeverity] = useState<"critical" | "high" | "medium" | "low">("high")
const [type, setType] = useState("monitor_down")
const handleSubmit = () => {
onCreate({
title,
description,
severity,
type,
})
onOpenChange(false)
setTitle("")
setDescription("")
setSeverity("high")
setType("monitor_down")
}
return (
)
}
// Main Status Page Manager Component
export function StatusPageManager() {
const { toast } = useToast()
const queryClient = useQueryClient()
const [activeTab, setActiveTab] = useState("overview")
const [statusPageDialogOpen, setStatusPageDialogOpen] = useState(false)
const [editingPage, setEditingPage] = useState(null)
const [createIncidentOpen, setCreateIncidentOpen] = useState(false)
const [incidentFilter, setIncidentFilter] = useState("all")
const [searchQuery, setSearchQuery] = useState("")
// Fetch data
const { data: pages, isLoading: pagesLoading } = useQuery({
queryKey: ["status-pages"],
queryFn: getStatusPages,
})
const { data: incidents, isLoading: incidentsLoading } = useQuery({
queryKey: ["incidents", incidentFilter],
queryFn: () => getIncidents(incidentFilter === "all" ? {} : { status: incidentFilter }),
})
const { data: stats } = useQuery({
queryKey: ["incident-stats"],
queryFn: getIncidentStats,
})
// Mutations
const deletePageMutation = useMutation({
mutationFn: deleteStatusPage,
onSuccess: () => {
toast({ title: "Status page deleted" })
queryClient.invalidateQueries({ queryKey: ["status-pages"] })
},
onError: (error: Error) => {
toast({ title: "Failed to delete", description: error.message, variant: "destructive" })
},
})
const createIncidentMutation = useMutation({
mutationFn: createIncident,
onSuccess: () => {
toast({ title: "Incident created" })
queryClient.invalidateQueries({ queryKey: ["incidents"] })
queryClient.invalidateQueries({ queryKey: ["incident-stats"] })
},
onError: (error: Error) => {
toast({ title: "Failed to create incident", description: error.message, variant: "destructive" })
},
})
const acknowledgeMutation = useMutation({
mutationFn: acknowledgeIncident,
onSuccess: () => {
toast({ title: "Incident acknowledged" })
queryClient.invalidateQueries({ queryKey: ["incidents"] })
},
})
const resolveMutation = useMutation({
mutationFn: (id: string) => resolveIncident(id),
onSuccess: () => {
toast({ title: "Incident resolved" })
queryClient.invalidateQueries({ queryKey: ["incidents"] })
queryClient.invalidateQueries({ queryKey: ["incident-stats"] })
},
})
const closeMutation = useMutation({
mutationFn: closeIncident,
onSuccess: () => {
toast({ title: "Incident closed" })
queryClient.invalidateQueries({ queryKey: ["incidents"] })
queryClient.invalidateQueries({ queryKey: ["incident-stats"] })
},
})
// Filtered incidents
const filteredIncidents = useMemo(() => {
if (!incidents) return []
if (!searchQuery) return incidents
return incidents.filter(
(i) =>
i.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
i.description?.toLowerCase().includes(searchQuery.toLowerCase())
)
}, [incidents, searchQuery])
// Active incidents count
const activeIncidents = useMemo(
() => incidents?.filter((i) => i.status === "open" || i.status === "acknowledged").length || 0,
[incidents]
)
const handleEdit = (page: StatusPage) => {
setEditingPage(page)
setStatusPageDialogOpen(true)
}
const handleAdd = () => {
setEditingPage(null)
setStatusPageDialogOpen(true)
}
const handleDelete = (page: StatusPage) => {
if (confirm(`Delete "${page.name}"? This will unlink all ${page.monitor_count} monitor(s).`)) {
deletePageMutation.mutate(page.id)
}
}
return (
{/* Header */}
Status Page Manager
Manage status pages, incidents, and public communications
{/* Quick Stats */}
p.public).length || 0} public`}
icon={LayoutTemplate}
color="blue"
/>
i.severity === "critical").length || 0} critical`}
icon={AlertTriangle}
color={activeIncidents > 0 ? "red" : "green"}
/>
acc + p.monitor_count, 0) || 0}
subtitle="Across all pages"
icon={Activity}
color="purple"
/>
{/* Main Tabs */}
Overview
Status Pages
{pages && pages.length > 0 && (
{pages.length}
)}
Incidents
{activeIncidents > 0 && (
{activeIncidents}
)}
{/* Overview Tab */}
{/* Recent Status Pages */}
Recent Status Pages
Your public and private status pages
{pagesLoading ? (
{[1, 2, 3].map((i) => (
))}
) : pages?.length === 0 ? (
No status pages yet
) : (
{pages?.slice(0, 5).map((page) => (
{page.public ? (
) : (
)}
{page.name}
{page.monitor_count} monitors
{page.public && (
)}
))}
)}
{/* Recent Incidents */}
Active Incidents
Incidents requiring attention
{incidentsLoading ? (
{[1, 2, 3].map((i) => (
))}
) : filteredIncidents.filter((i) => i.status !== "closed").length === 0 ? (
All clear! No active incidents.
) : (
{filteredIncidents
.filter((i) => i.status !== "closed")
.slice(0, 5)
.map((incident) => (
{incident.severity}
{incident.title}
{formatDuration(incident.started_at)}
))}
)}
{/* Status Pages Tab */}
All Status Pages
Manage your public and private status pages
{pagesLoading ? (
{[1, 2, 3].map((i) => (
))}
) : (
Name
Slug
Monitors
Visibility
Updated
Actions
{pages?.map((page) => (
{page.name}
{page.slug}
{page.monitor_count}
{page.public ? (
Public
) : (
Private
)}
{new Date(page.updated).toLocaleDateString()}
{page.public && (
)}
))}
)}
{/* Incidents Tab */}
All Incidents
Manage and track all incidents
{incidentsLoading ? (
{[1, 2, 3].map((i) => (
))}
) : filteredIncidents.length === 0 ? (
No incidents found
{searchQuery
? "Try adjusting your search or filters"
: "All systems are running smoothly"}
) : (
Title
Severity
Status
Duration
Started
Actions
{filteredIncidents.map((incident) => (
{incident.title}
{incident.severity}
{incident.status}
{formatDuration(incident.started_at)}
{new Date(incident.started_at).toLocaleDateString()}
))}
)}
{/* Dialogs */}
)
}