mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { type BuildAppResult, getRecentApps } from '~/lib/persistence/apps';
|
|
import styles from './ExampleLibraryApps.module.scss';
|
|
import { importChat } from '~/lib/persistence/useChatHistory';
|
|
|
|
export const ExampleLibraryApps = () => {
|
|
const [numApps, setNumApps] = useState<number>(6);
|
|
const [apps, setApps] = useState<BuildAppResult[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
async function fetchRecentApps() {
|
|
try {
|
|
setLoading(true);
|
|
const recentApps = await getRecentApps(numApps);
|
|
setApps(recentApps);
|
|
setError(null);
|
|
} catch (err) {
|
|
console.error('Failed to fetch recent apps:', err);
|
|
setError('Failed to load recent apps');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
if (apps.length < numApps) {
|
|
fetchRecentApps();
|
|
}
|
|
}, [numApps]);
|
|
|
|
if (error) {
|
|
return <div className={styles.error}>{error}</div>;
|
|
}
|
|
|
|
if (apps.length === 0) {
|
|
if (loading) {
|
|
return <div className={styles.loading}>Loading recent apps...</div>;
|
|
}
|
|
return <div className={styles.empty}>No recent apps found</div>;
|
|
}
|
|
|
|
const displayApps = apps.slice(0, numApps);
|
|
|
|
return (
|
|
<div className={styles.container}>
|
|
<div className={styles.grid}>
|
|
{displayApps.map((app) => (
|
|
<div
|
|
key={app.appId}
|
|
className={`${styles.appItem} ${app.outcome !== 'success' ? styles.appItemError : ''}`}
|
|
onClick={() => {
|
|
importChat(
|
|
app.title ?? 'Untitled App',
|
|
app.messages.filter((msg) => {
|
|
// Workaround an issue where the messages in the database include images
|
|
// (used to generate the screenshots).
|
|
if (msg.role == 'assistant' && msg.type == 'image') {
|
|
return false;
|
|
}
|
|
return true;
|
|
}),
|
|
);
|
|
}}
|
|
>
|
|
{app.imageDataURL ? (
|
|
<img src={app.imageDataURL} alt={app.title || 'App preview'} className={styles.previewImage} />
|
|
) : (
|
|
<div className={styles.placeholderImage}>{app.title || 'No preview'}</div>
|
|
)}
|
|
<div className={styles.appTitle}>{app.title || 'Untitled App'}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
{loading && <div className={styles.loading}>Loading recent apps...</div>}
|
|
{!loading && (
|
|
<div className={styles.buttonContainer}>
|
|
<button
|
|
className={styles.loadMoreButton}
|
|
onClick={() => {
|
|
setNumApps((prev) => prev + 12);
|
|
}}
|
|
>
|
|
Load More
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|