chore: fix failed build

This commit is contained in:
Faisal Amir 2025-09-29 17:21:53 +07:00
parent e065e56f5c
commit e8666ee5ec

View File

@ -1,186 +1,193 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useCallback } from 'react'
import { AlertCircle, CheckCircle, Clock, RefreshCw } from 'lucide-react'; import { AlertCircle, CheckCircle, Clock, RefreshCw } from 'lucide-react'
interface StatusData { interface StatusData {
status: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'under_maintenance' | 'unknown'; status:
lastUpdated: string; | 'operational'
| 'degraded'
| 'partial_outage'
| 'major_outage'
| 'under_maintenance'
| 'unknown'
lastUpdated: string
incidents: Array<{ incidents: Array<{
name: string; name: string
status: string; status: string
impact: string; impact: string
}>; }>
} }
const StatusIcon = ({ status }: { status: string }) => { const StatusIcon = ({ status }: { status: string }) => {
switch (status) { switch (status) {
case 'operational': case 'operational':
return <CheckCircle className="w-5 h-5 text-green-500" />; return <CheckCircle className="w-5 h-5 text-green-500" />
case 'degraded': case 'degraded':
case 'partial_outage': case 'partial_outage':
return <AlertCircle className="w-5 h-5 text-yellow-500" />; return <AlertCircle className="w-5 h-5 text-yellow-500" />
case 'major_outage': case 'major_outage':
return <AlertCircle className="w-5 h-5 text-red-500" />; return <AlertCircle className="w-5 h-5 text-red-500" />
case 'under_maintenance': case 'under_maintenance':
return <Clock className="w-5 h-5 text-blue-500" />; return <Clock className="w-5 h-5 text-blue-500" />
default: default:
return <AlertCircle className="w-5 h-5 text-gray-500" />; return <AlertCircle className="w-5 h-5 text-gray-500" />
}
} }
};
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
switch (status) { switch (status) {
case 'operational': case 'operational':
return 'bg-green-100 text-green-800 border-green-200 dark:bg-green-900/20 dark:text-green-300 dark:border-green-800'; return 'bg-green-100 text-green-800 border-green-200 dark:bg-green-900/20 dark:text-green-300 dark:border-green-800'
case 'degraded': case 'degraded':
case 'partial_outage': case 'partial_outage':
return 'bg-yellow-100 text-yellow-800 border-yellow-200 dark:bg-yellow-900/20 dark:text-yellow-300 dark:border-yellow-800'; return 'bg-yellow-100 text-yellow-800 border-yellow-200 dark:bg-yellow-900/20 dark:text-yellow-300 dark:border-yellow-800'
case 'major_outage': case 'major_outage':
return 'bg-red-100 text-red-800 border-red-200 dark:bg-red-900/20 dark:text-red-300 dark:border-red-800'; return 'bg-red-100 text-red-800 border-red-200 dark:bg-red-900/20 dark:text-red-300 dark:border-red-800'
case 'under_maintenance': case 'under_maintenance':
return 'bg-blue-100 text-blue-800 border-blue-200 dark:bg-blue-900/20 dark:text-blue-300 dark:border-blue-800'; return 'bg-blue-100 text-blue-800 border-blue-200 dark:bg-blue-900/20 dark:text-blue-300 dark:border-blue-800'
default: default:
return 'bg-gray-100 text-gray-800 border-gray-200 dark:bg-gray-900/20 dark:text-gray-300 dark:border-gray-800'; return 'bg-gray-100 text-gray-800 border-gray-200 dark:bg-gray-900/20 dark:text-gray-300 dark:border-gray-800'
}
} }
};
const getStatusText = (status: string) => { const getStatusText = (status: string) => {
switch (status) { switch (status) {
case 'operational': case 'operational':
return 'All Systems Operational'; return 'All Systems Operational'
case 'degraded': case 'degraded':
return 'Degraded Performance'; return 'Degraded Performance'
case 'partial_outage': case 'partial_outage':
return 'Partial Service Outage'; return 'Partial Service Outage'
case 'major_outage': case 'major_outage':
return 'Major Service Outage'; return 'Major Service Outage'
case 'under_maintenance': case 'under_maintenance':
return 'Under Maintenance'; return 'Under Maintenance'
default: default:
return 'Status Unknown'; return 'Status Unknown'
}
} }
};
export const OpenAIStatusChecker: React.FC = () => { export const OpenAIStatusChecker: React.FC = () => {
const [statusData, setStatusData] = useState<StatusData | null>(null); const [statusData, setStatusData] = useState<StatusData | null>(null)
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null)
const [lastRefresh, setLastRefresh] = useState<Date>(new Date()); const [lastRefresh, setLastRefresh] = useState<Date>(new Date())
const fetchStatus = async () => { const fetchStatus = useCallback(async () => {
setLoading(true); setLoading(true)
setError(null); setError(null)
try { try {
console.log('Fetching real OpenAI status...'); console.log('Fetching real OpenAI status...')
// Use CORS proxy to fetch real OpenAI status // Use CORS proxy to fetch real OpenAI status
const proxyUrl = 'https://api.allorigins.win/get?url='; const proxyUrl = 'https://api.allorigins.win/get?url='
const targetUrl = 'https://status.openai.com/api/v2/status.json'; const targetUrl = 'https://status.openai.com/api/v2/status.json'
const response = await fetch(proxyUrl + encodeURIComponent(targetUrl)); const response = await fetch(proxyUrl + encodeURIComponent(targetUrl))
if (!response.ok) { if (!response.ok) {
throw new Error(`Proxy returned ${response.status}`); throw new Error(`Proxy returned ${response.status}`)
} }
const proxyData = await response.json(); const proxyData = await response.json()
const openaiData = JSON.parse(proxyData.contents); const openaiData = JSON.parse(proxyData.contents)
console.log('Real OpenAI data received:', openaiData); console.log('Real OpenAI data received:', openaiData)
// Transform real OpenAI data to our format // Transform real OpenAI data to our format
const transformedData = { const transformedData: StatusData = {
status: mapOpenAIStatusClient(openaiData.status?.indicator || 'operational'), status: mapOpenAIStatusClient(
openaiData.status?.indicator || 'operational'
),
lastUpdated: openaiData.page?.updated_at || new Date().toISOString(), lastUpdated: openaiData.page?.updated_at || new Date().toISOString(),
incidents: (openaiData.incidents || []).slice(0, 3), incidents: (openaiData.incidents || []).slice(0, 3),
components: (openaiData.components || []).filter(component => }
['ChatGPT', 'API', 'Playground'].some(name =>
(component.name || '').toLowerCase().includes(name.toLowerCase())
)
)
};
setStatusData(transformedData);
setLastRefresh(new Date());
console.log('✅ Real OpenAI status loaded successfully!');
setStatusData(transformedData)
setLastRefresh(new Date())
console.log('✅ Real OpenAI status loaded successfully!')
} catch (err) { } catch (err) {
console.error('Failed to fetch real status:', err); console.error('Failed to fetch real status:', err)
// Fallback: try alternative proxy // Fallback: try alternative proxy
try { try {
console.log('Trying alternative proxy...'); console.log('Trying alternative proxy...')
const altResponse = await fetch(`https://cors-anywhere.herokuapp.com/https://status.openai.com/api/v2/summary.json`); const altResponse = await fetch(
`https://cors-anywhere.herokuapp.com/https://status.openai.com/api/v2/summary.json`
)
if (altResponse.ok) { if (altResponse.ok) {
const altData = await altResponse.json(); const altData = await altResponse.json()
setStatusData({ setStatusData({
status: mapOpenAIStatusClient(altData.status?.indicator || 'operational'), status: mapOpenAIStatusClient(
altData.status?.indicator || 'operational'
),
lastUpdated: new Date().toISOString(), lastUpdated: new Date().toISOString(),
incidents: [] incidents: [],
}); })
setLastRefresh(new Date()); setLastRefresh(new Date())
console.log('✅ Alternative proxy worked!'); console.log('✅ Alternative proxy worked!')
return; return
} }
} catch (altErr) { } catch (altErr) {
console.log('Alternative proxy also failed'); console.log('Alternative proxy also failed')
} }
// Final fallback // Final fallback
setError('Unable to fetch real-time status'); setError('Unable to fetch real-time status')
setStatusData({ setStatusData({
status: 'operational', status: 'operational' as const,
lastUpdated: new Date().toISOString(), lastUpdated: new Date().toISOString(),
incidents: [] incidents: [],
}); })
setLastRefresh(new Date()); setLastRefresh(new Date())
console.log('Using fallback status'); console.log('Using fallback status')
} finally { } finally {
setLoading(false); setLoading(false)
} }
}; }, [])
// Client-side status mapping function // Client-side status mapping function
const mapOpenAIStatusClient = (indicator: string) => { const mapOpenAIStatusClient = (indicator: string): StatusData['status'] => {
switch (indicator.toLowerCase()) { switch (indicator.toLowerCase()) {
case 'none': case 'none':
case 'operational': case 'operational':
return 'operational'; return 'operational'
case 'minor': case 'minor':
return 'degraded'; return 'degraded'
case 'major': case 'major':
return 'partial_outage'; return 'partial_outage'
case 'critical': case 'critical':
return 'major_outage'; return 'major_outage'
case 'maintenance': case 'maintenance':
return 'under_maintenance'; return 'under_maintenance'
default: default:
return 'operational'; // Default to operational return 'operational' as const // Default to operational
}
} }
};
useEffect(() => { useEffect(() => {
fetchStatus(); fetchStatus()
// Refresh every 2 minutes for more real-time updates // Refresh every 2 minutes for more real-time updates
const interval = setInterval(fetchStatus, 2 * 60 * 1000); const interval = setInterval(fetchStatus, 2 * 60 * 1000)
return () => clearInterval(interval); return () => clearInterval(interval)
}, []); }, [fetchStatus])
const handleRefresh = () => { const handleRefresh = () => {
fetchStatus(); fetchStatus()
}; }
if (loading && !statusData) { if (loading && !statusData) {
return ( return (
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 border border-gray-200 dark:border-gray-700"> <div className="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 border border-gray-200 dark:border-gray-700">
<div className="flex items-center justify-center space-x-3"> <div className="flex items-center justify-center space-x-3">
<RefreshCw className="w-6 h-6 text-blue-500 animate-spin" /> <RefreshCw className="w-6 h-6 text-blue-500 animate-spin" />
<span className="text-lg font-medium text-gray-700 dark:text-gray-300">Checking OpenAI Status...</span> <span className="text-lg font-medium text-gray-700 dark:text-gray-300">
Checking OpenAI Status...
</span>
</div> </div>
</div> </div>
); )
} }
if (error) { if (error) {
@ -190,7 +197,9 @@ export const OpenAIStatusChecker: React.FC = () => {
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<AlertCircle className="w-6 h-6 text-red-500" /> <AlertCircle className="w-6 h-6 text-red-500" />
<div> <div>
<h3 className="text-lg font-semibold text-red-800 dark:text-red-300">Unable to Check Status</h3> <h3 className="text-lg font-semibold text-red-800 dark:text-red-300">
Unable to Check Status
</h3>
<p className="text-red-600 dark:text-red-400">{error}</p> <p className="text-red-600 dark:text-red-400">{error}</p>
</div> </div>
</div> </div>
@ -202,7 +211,7 @@ export const OpenAIStatusChecker: React.FC = () => {
</button> </button>
</div> </div>
</div> </div>
); )
} }
return ( return (
@ -211,7 +220,9 @@ export const OpenAIStatusChecker: React.FC = () => {
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<StatusIcon status={statusData?.status || 'unknown'} /> <StatusIcon status={statusData?.status || 'unknown'} />
<div> <div>
<h3 className="text-xl font-bold text-gray-900 dark:text-gray-100">OpenAI Services</h3> <h3 className="text-xl font-bold text-gray-900 dark:text-gray-100">
OpenAI Services
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400"> <p className="text-sm text-gray-600 dark:text-gray-400">
Last updated: {new Date(lastRefresh).toLocaleTimeString()} Last updated: {new Date(lastRefresh).toLocaleTimeString()}
</p> </p>
@ -222,16 +233,22 @@ export const OpenAIStatusChecker: React.FC = () => {
disabled={loading} disabled={loading}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors disabled:opacity-50" className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors disabled:opacity-50"
> >
<RefreshCw className={`w-5 h-5 text-gray-600 dark:text-gray-400 ${loading ? 'animate-spin' : ''}`} /> <RefreshCw
className={`w-5 h-5 text-gray-600 dark:text-gray-400 ${loading ? 'animate-spin' : ''}`}
/>
</button> </button>
</div> </div>
<div className={`inline-flex items-center px-4 py-2 rounded-full text-sm font-semibold border ${getStatusColor(statusData?.status || 'unknown')}`}> <div
className={`inline-flex items-center px-4 py-2 rounded-full text-sm font-semibold border ${getStatusColor(statusData?.status || 'unknown')}`}
>
{getStatusText(statusData?.status || 'unknown')} {getStatusText(statusData?.status || 'unknown')}
</div> </div>
<div className="mt-4 p-4 bg-gray-50 dark:bg-gray-700 rounded-lg"> <div className="mt-4 p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<h4 className="font-semibold text-gray-900 dark:text-gray-100 mb-2">Quick Status Check</h4> <h4 className="font-semibold text-gray-900 dark:text-gray-100 mb-2">
Quick Status Check
</h4>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 text-sm"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-3 text-sm">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-gray-600 dark:text-gray-400">ChatGPT</span> <span className="text-gray-600 dark:text-gray-400">ChatGPT</span>
@ -249,13 +266,18 @@ export const OpenAIStatusChecker: React.FC = () => {
</div> </div>
<div className="mt-4 text-xs text-gray-500 dark:text-gray-400 text-center"> <div className="mt-4 text-xs text-gray-500 dark:text-gray-400 text-center">
{error ? 'Using fallback status • ' : '🟢 Real-time data from OpenAI • '} {error
? 'Using fallback status • '
: '🟢 Real-time data from OpenAI • '}
Updated: {new Date(lastRefresh).toLocaleTimeString()} Updated: {new Date(lastRefresh).toLocaleTimeString()}
<br /> <br />
<a href="/post/is-chatgpt-down-use-jan#-is-chatgpt-down" className="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 underline"> <a
href="/post/is-chatgpt-down-use-jan#-is-chatgpt-down"
className="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 underline"
>
View detailed status guide View detailed status guide
</a> </a>
</div> </div>
</div> </div>
); )
}; }