'use client'; import { useState, useEffect, useCallback } from 'react'; import { MinimizedApiKey } from '@/components/demo/MinimizedApiKey'; import { ImageZoomModal } from '@/components/demo/ImageZoomModal'; import { ImageGrid } from '@/components/demo/gallery/ImageGrid'; import { EmptyGalleryState } from '@/components/demo/gallery/EmptyGalleryState'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; const API_KEY_STORAGE_KEY = 'banatie_demo_api_key'; const IMAGES_PER_PAGE = 30; type ImageItem = { name: string; url: string; size: number; contentType: string; lastModified: string; }; type ImagesResponse = { success: boolean; data?: { images: ImageItem[]; total: number; offset: number; limit: number; hasMore: boolean; }; error?: string; message?: string; }; type ApiKeyInfo = { organizationSlug?: string; projectSlug?: string; }; type DownloadTimeMap = { [imageId: string]: number; }; export default function GalleryPage() { const [apiKey, setApiKey] = useState(''); const [apiKeyVisible, setApiKeyVisible] = useState(false); const [apiKeyValidated, setApiKeyValidated] = useState(false); const [apiKeyInfo, setApiKeyInfo] = useState(null); const [apiKeyError, setApiKeyError] = useState(''); const [validatingKey, setValidatingKey] = useState(false); const [images, setImages] = useState([]); const [offset, setOffset] = useState(0); const [hasMore, setHasMore] = useState(false); const [loading, setLoading] = useState(false); const [loadingMore, setLoadingMore] = useState(false); const [error, setError] = useState(''); const [zoomedImageUrl, setZoomedImageUrl] = useState(null); const [downloadTimes, setDownloadTimes] = useState({}); useEffect(() => { const storedApiKey = localStorage.getItem(API_KEY_STORAGE_KEY); if (storedApiKey) { setApiKey(storedApiKey); validateStoredApiKey(storedApiKey); } }, []); const validateStoredApiKey = async (keyToValidate: string) => { setValidatingKey(true); setApiKeyError(''); try { const response = await fetch(`${API_BASE_URL}/api/info`, { headers: { 'X-API-Key': keyToValidate, }, }); if (response.ok) { const data = await response.json(); setApiKeyValidated(true); if (data.keyInfo) { setApiKeyInfo({ organizationSlug: data.keyInfo.organizationSlug || data.keyInfo.organizationId, projectSlug: data.keyInfo.projectSlug || data.keyInfo.projectId, }); } else { setApiKeyInfo({ organizationSlug: 'Unknown', projectSlug: 'Unknown', }); } await fetchImages(keyToValidate, 0); } else { localStorage.removeItem(API_KEY_STORAGE_KEY); setApiKeyError('Stored API key is invalid or expired'); setApiKeyValidated(false); } } catch (error) { setApiKeyError('Failed to validate stored API key'); setApiKeyValidated(false); } finally { setValidatingKey(false); } }; const validateApiKey = async () => { if (!apiKey.trim()) { setApiKeyError('Please enter an API key'); return; } setValidatingKey(true); setApiKeyError(''); try { const response = await fetch(`${API_BASE_URL}/api/info`, { headers: { 'X-API-Key': apiKey, }, }); if (response.ok) { const data = await response.json(); setApiKeyValidated(true); localStorage.setItem(API_KEY_STORAGE_KEY, apiKey); if (data.keyInfo) { setApiKeyInfo({ organizationSlug: data.keyInfo.organizationSlug || data.keyInfo.organizationId, projectSlug: data.keyInfo.projectSlug || data.keyInfo.projectId, }); } else { setApiKeyInfo({ organizationSlug: 'Unknown', projectSlug: 'Unknown', }); } await fetchImages(apiKey, 0); } else { const error = await response.json(); setApiKeyError(error.message || 'Invalid API key'); setApiKeyValidated(false); } } catch (error) { setApiKeyError('Failed to validate API key. Please check your connection.'); setApiKeyValidated(false); } finally { setValidatingKey(false); } }; const revokeApiKey = () => { localStorage.removeItem(API_KEY_STORAGE_KEY); setApiKey(''); setApiKeyValidated(false); setApiKeyInfo(null); setApiKeyError(''); setImages([]); setOffset(0); setHasMore(false); setError(''); }; const fetchImages = async (keyToUse: string, fetchOffset: number) => { if (fetchOffset === 0) { setLoading(true); } else { setLoadingMore(true); } setError(''); try { const response = await fetch( `${API_BASE_URL}/api/images/generated?limit=${IMAGES_PER_PAGE}&offset=${fetchOffset}`, { headers: { 'X-API-Key': keyToUse, }, } ); if (!response.ok) { const errorData: ImagesResponse = await response.json(); throw new Error(errorData.error || errorData.message || 'Failed to fetch images'); } const result: ImagesResponse = await response.json(); if (result.success && result.data) { const { images: newImages, offset: newOffset, hasMore: newHasMore } = result.data; if (fetchOffset === 0) { setImages(newImages); } else { setImages((prev) => [...prev, ...newImages]); } setOffset(newOffset); setHasMore(newHasMore); } else { throw new Error(result.error || 'Failed to fetch images'); } } catch (error) { setError(error instanceof Error ? error.message : 'Failed to load images'); } finally { setLoading(false); setLoadingMore(false); } }; const handleLoadMore = () => { const newOffset = offset + IMAGES_PER_PAGE; fetchImages(apiKey, newOffset); }; const handleDownloadMeasured = useCallback((imageId: string, downloadMs: number) => { setDownloadTimes((prev) => ({ ...prev, [imageId]: downloadMs, })); }, []); return (
{apiKeyValidated && apiKeyInfo && ( )}

Image Gallery

Browse your AI-generated images

{!apiKeyValidated && (

API Key

setApiKey(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); validateApiKey(); } }} placeholder="Enter your API key" className="w-full px-4 py-3 bg-slate-800 border border-slate-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent pr-12" aria-label="API key input" />
{apiKeyError && (

{apiKeyError}

{apiKeyError.includes('Invalid') ? 'Please check your API key and try again. You can create a new key in the admin dashboard.' : 'Please check your internet connection and try again.'}

)}
)} {apiKeyValidated && (
{loading ? (

Loading images...

) : error ? (

{error}

{error.includes('fetch') || error.includes('load') ? 'Unable to load images. Please check your connection and try refreshing the page.' : 'An error occurred while fetching your images. Please try again later.'}

) : images.length === 0 ? ( ) : ( <> {hasMore && (
)} )}
)} setZoomedImageUrl(null)} />
); }