diff --git a/frontend/src/api/activity.ts b/frontend/src/api/activity.ts new file mode 100644 index 000000000..bed383561 --- /dev/null +++ b/frontend/src/api/activity.ts @@ -0,0 +1,20 @@ +import { apiClient } from '../services/apiClient'; + +export interface ActivityEvent { + id: string; + type: 'completed' | 'submitted' | 'posted' | 'review'; + username: string; + avatar_url?: string | null; + detail: string; + timestamp: string; +} + +interface ActivityResponse { + items: ActivityEvent[]; +} + +export async function listActivity(limit = 4): Promise { + return apiClient('/api/activity', { + params: { limit }, + }); +} diff --git a/frontend/src/components/home/ActivityFeed.tsx b/frontend/src/components/home/ActivityFeed.tsx index 8b6b4b904..9c986191b 100644 --- a/frontend/src/components/home/ActivityFeed.tsx +++ b/frontend/src/components/home/ActivityFeed.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { slideInRight } from '../../lib/animations'; import { timeAgo } from '../../lib/utils'; +import { useActivity } from '../../hooks/useActivity'; interface ActivityEvent { id: string; @@ -76,7 +77,9 @@ function EventItem({ event }: { event: ActivityEvent }) { } export function ActivityFeed({ events }: { events?: ActivityEvent[] }) { - const displayEvents = events?.length ? events.slice(0, 4) : MOCK_EVENTS; + const { data, isError } = useActivity(4); + const remoteEvents = data?.items ?? []; + const displayEvents = events?.length ? events.slice(0, 4) : remoteEvents.length ? remoteEvents : MOCK_EVENTS; const [visibleEvents, setVisibleEvents] = useState(displayEvents.slice(0, 4)); useEffect(() => { @@ -91,6 +94,9 @@ export function ActivityFeed({ events }: { events?: ActivityEvent[] }) { Recent Activity
+ {isError && ( +

Using fallback activity feed while API is unavailable.

+ )} {visibleEvents.map((event) => ( listActivity(limit), + refetchInterval: 30_000, + staleTime: 15_000, + }); +}