Add team and stream counts to footer
- Created /api/counts endpoint for database statistics - Updated footer to display team and stream counts - Shows counts when OBS is connected (alongside OBS stats) - Shows database stats when OBS is disconnected (fallback display) - Polls counts every 60 seconds (less frequent than OBS status) - Maintains backward compatibility with API response formats Footer now shows: - Teams: X - Streams: Y 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
cbf8cd6516
commit
02cad6a319
2 changed files with 83 additions and 7 deletions
25
app/api/counts/route.ts
Normal file
25
app/api/counts/route.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
import { getDatabase } from '../../../lib/database';
|
||||
import { TABLE_NAMES } from '../../../lib/constants';
|
||||
import { createSuccessResponse, createDatabaseError, withErrorHandling } from '../../../lib/apiHelpers';
|
||||
|
||||
async function getCountsHandler() {
|
||||
try {
|
||||
const db = await getDatabase();
|
||||
|
||||
// Get counts in parallel
|
||||
const [streamsResult, teamsResult] = await Promise.all([
|
||||
db.get(`SELECT COUNT(*) as count FROM ${TABLE_NAMES.STREAMS}`),
|
||||
db.get(`SELECT COUNT(*) as count FROM ${TABLE_NAMES.TEAMS}`)
|
||||
]);
|
||||
|
||||
return createSuccessResponse({
|
||||
streams: streamsResult.count,
|
||||
teams: teamsResult.count
|
||||
});
|
||||
} catch (error) {
|
||||
return createDatabaseError('fetch counts', error);
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = withErrorHandling(getCountsHandler);
|
|
@ -19,8 +19,14 @@ type OBSStatus = {
|
|||
error?: string;
|
||||
};
|
||||
|
||||
type Counts = {
|
||||
streams: number;
|
||||
teams: number;
|
||||
};
|
||||
|
||||
export default function Footer() {
|
||||
const [obsStatus, setObsStatus] = useState<OBSStatus | null>(null);
|
||||
const [counts, setCounts] = useState<Counts | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
// Smart polling with performance monitoring and visibility detection
|
||||
|
@ -40,10 +46,25 @@ export default function Footer() {
|
|||
}
|
||||
};
|
||||
|
||||
const fetchCounts = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/counts');
|
||||
const data = await response.json();
|
||||
// Handle both old and new API response formats
|
||||
const countsData = data.success ? data.data : data;
|
||||
setCounts(countsData);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch counts:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Use smart polling that respects page visibility and adapts interval based on connection status
|
||||
const pollingInterval = obsStatus?.connected ? 15000 : 30000; // Poll faster when connected
|
||||
useSmartPolling(fetchOBSStatus, pollingInterval, [obsStatus?.connected]);
|
||||
|
||||
// Poll counts less frequently (every 60 seconds) since they don't change as often
|
||||
useSmartPolling(fetchCounts, 60000, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<footer className="glass p-6 mt-8">
|
||||
|
@ -78,11 +99,27 @@ export default function Footer() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Live Status */}
|
||||
{obsStatus?.connected && (
|
||||
{/* Live Status or Database Stats */}
|
||||
<div>
|
||||
<h3 className="font-semibold mb-4">Live Status</h3>
|
||||
<h3 className="font-semibold mb-4">
|
||||
{obsStatus?.connected ? 'Live Status' : 'Database Stats'}
|
||||
</h3>
|
||||
|
||||
{/* Show counts when OBS is disconnected */}
|
||||
{!obsStatus?.connected && counts && (
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span>Teams:</span>
|
||||
<span className="font-medium">{counts.teams}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Streams:</span>
|
||||
<span className="font-medium">{counts.streams}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{obsStatus?.connected && (
|
||||
<div className="space-y-2 text-sm">
|
||||
{obsStatus.currentScene && (
|
||||
<div className="flex justify-between">
|
||||
|
@ -98,6 +135,20 @@ export default function Footer() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Database counts */}
|
||||
{counts && (
|
||||
<>
|
||||
<div className="flex justify-between">
|
||||
<span>Teams:</span>
|
||||
<span className="font-medium">{counts.teams}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Streams:</span>
|
||||
<span className="font-medium">{counts.streams}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="flex gap-4 mt-4">
|
||||
<div className={`flex items-center gap-2 ${obsStatus.streaming ? 'text-red-400' : 'opacity-60'}`}>
|
||||
<div className={`w-2 h-2 rounded-full ${obsStatus.streaming ? 'bg-red-500' : 'bg-gray-500'}`}></div>
|
||||
|
@ -110,9 +161,9 @@ export default function Footer() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Error Message */}
|
||||
{obsStatus?.error && (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue