diff --git a/app/api/counts/route.ts b/app/api/counts/route.ts new file mode 100644 index 0000000..86a638d --- /dev/null +++ b/app/api/counts/route.ts @@ -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); \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index 288b6ab..e1107df 100644 --- a/app/globals.css +++ b/app/globals.css @@ -40,6 +40,30 @@ body { line-height: 1.6; } +/* Status Indicator Dots */ +.status-dot { + width: 12px; + height: 12px; + border-radius: 50%; + display: inline-block; +} + +.status-dot.connected { + background-color: #859900; /* Solarized green */ +} + +.status-dot.disconnected { + background-color: #dc322f; /* Solarized red */ +} + +.status-dot.streaming { + background-color: #dc322f; /* Solarized red */ +} + +.status-dot.idle { + background-color: #586e75; /* Solarized base01 */ +} + /* Glass Card Component */ .glass { background: rgba(7, 54, 66, 0.4); diff --git a/components/Footer.tsx b/components/Footer.tsx index c43a131..3d4328c 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -19,8 +19,14 @@ type OBSStatus = { error?: string; }; +type Counts = { + streams: number; + teams: number; +}; + export default function Footer() { const [obsStatus, setObsStatus] = useState(null); + const [counts, setCounts] = useState(null); const [isLoading, setIsLoading] = useState(true); // Smart polling with performance monitoring and visibility detection @@ -40,9 +46,24 @@ 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 ( @@ -60,29 +81,58 @@ export default function Footer() {
{/* Connection Status */}
-
-
-
-

OBS Studio

-

- {obsStatus?.connected ? 'Connected' : 'Disconnected'} -

-
+

OBS Studio

+
+
+ + {obsStatus?.connected ? 'Connected' : 'Disconnected'} +
{obsStatus && (
{obsStatus.host}:{obsStatus.port}
{obsStatus.hasPassword &&
🔒 Authenticated
} + + {/* Streaming/Recording Status */} + {obsStatus.connected && ( +
+
+
+ {obsStatus.streaming ? 'LIVE' : 'OFFLINE'} +
+ +
+
+ {obsStatus.recording ? 'REC' : 'IDLE'} +
+
+ )}
)}
- {/* Live Status */} - {obsStatus?.connected && ( -
-

Live Status

- + {/* Live Status or Database Stats */} +
+

+ {obsStatus?.connected ? 'Live Status' : 'Database Stats'} +

+ + {/* Show counts when OBS is disconnected */} + {!obsStatus?.connected && counts && ( +
+
+ Teams: + {counts.teams} +
+
+ Streams: + {counts.streams} +
+
+ )} + + {obsStatus?.connected && (
{obsStatus.currentScene && (
@@ -98,20 +148,22 @@ export default function Footer() {
)} -
-
-
- {obsStatus.streaming ? 'LIVE' : 'OFFLINE'} -
- -
-
- {obsStatus.recording ? 'REC' : 'IDLE'} -
-
+ {/* Database counts */} + {counts && ( + <> +
+ Teams: + {counts.teams} +
+
+ Streams: + {counts.streams} +
+ + )}
-
- )} + )} +
{/* Error Message */}