'use client'; import { useEffect, useState } from 'react'; import { type BuildAppResult, type BuildAppSummary, getAppById, getRecentApps } from '~/lib/persistence/apps'; import styles from './ExampleLibraryApps.module.scss'; import { getMessagesRepositoryId } from '~/lib/persistence/message'; import { classNames } from '~/utils/classNames'; import { APP_SUMMARY_CATEGORY } from '~/lib/persistence/messageAppSummary'; import { parseAppSummaryMessage } from '~/lib/persistence/messageAppSummary'; const formatDate = (date: Date) => { return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', }).format(date); }; interface ExampleLibraryAppsProps { filterText: string; } export const ExampleLibraryApps = ({ filterText }: ExampleLibraryAppsProps) => { const [numApps, setNumApps] = useState(6); const [apps, setApps] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedAppId, setSelectedAppId] = useState(null); const [selectedAppContents, setSelectedAppContents] = useState(null); const [gridColumns, setGridColumns] = useState(1); const computeGridColumns = () => { const width = window.innerWidth; if (width <= 480) { return 1; } if (width <= 768) { return 2; } return 3; }; useEffect(() => { setGridColumns(computeGridColumns()); const handleResize = () => setGridColumns(computeGridColumns()); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); useEffect(() => { (async () => { if (selectedAppId) { const app = await getAppById(selectedAppId); setSelectedAppContents(app); } })(); }, [selectedAppId]); useEffect(() => { setApps([]); setNumApps(6); }, [filterText]); useEffect(() => { async function fetchRecentApps() { try { setLoading(true); const recentApps = await getRecentApps(numApps, filterText); setApps(recentApps); setError(null); } catch (err) { console.error('Failed to fetch recent apps:', err); setError('Failed to load recent apps'); } finally { setLoading(false); } } fetchRecentApps(); }, [numApps, filterText]); if (error) { return
{error}
; } if (apps.length === 0) { if (loading) { return
Loading recent apps...
; } return
No recent apps found
; } const displayApps = apps.slice(0, numApps); let beforeApps = displayApps; let afterApps: BuildAppSummary[] = []; if (selectedAppId) { let selectedIndex = displayApps.findIndex((app) => app.id === selectedAppId); if (selectedIndex >= 0) { while ((selectedIndex + 1) % gridColumns != 0) { selectedIndex++; } beforeApps = displayApps.slice(0, selectedIndex + 1); afterApps = displayApps.slice(selectedIndex + 1); } } const renderApp = (app: BuildAppSummary) => { return (
{ setSelectedAppId(app.id == selectedAppId ? null : app.id); setSelectedAppContents(null); }} className={`${styles.appItem} ${!app.outcome.testsPassed ? styles.appItemError : ''}`} > {app.imageDataURL ? ( {app.title ) : (
{app.title || 'No preview'}
)}
{app.title || 'Untitled App'}
Created at {formatDate(new Date(app.createdAt))} in {Math.round(app.elapsedMinutes)} minutes
{app.totalPeanuts} peanuts{app.outcome.hasDatabase ? ' (has database)' : ''}
{!app.outcome.testsPassed &&
⚠️ Not all tests are passing
}
); }; const getAppSummary = (appContents: BuildAppResult) => { const message = appContents.messages.findLast((message) => message.category === APP_SUMMARY_CATEGORY); return message ? parseAppSummaryMessage(message) : null; }; const renderAppDetails = (appId: string, appContents: BuildAppResult | null) => { const app = apps.find((app) => app.id === appId); if (!app) { return null; } const appSummary = appContents ? getAppSummary(appContents) : null; return (

{app.title}

Created: {new Date(app.createdAt).toLocaleString()}
Time: {app.elapsedMinutes} minutes
Peanuts: {app.totalPeanuts}
Database: {app.outcome.hasDatabase ? 'Present' : 'None'}
Test Results
{appSummary?.tests.length && (
{appSummary.tests.map((test) => (
{test.recordingId ? ( {test.title} ) : (
{test.title}
)}
))}
)} {!appSummary &&
Loading...
}
); }; return (
{beforeApps.map(renderApp)}
{selectedAppId && renderAppDetails(selectedAppId, selectedAppContents)}
{afterApps.map(renderApp)}
{loading &&
Loading recent apps...
} {!loading && (
)}
); };