Spaces:
Configuration error
Configuration error
File size: 6,231 Bytes
bcce530 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | "use client"
import { useState, useEffect, useCallback } from "react"
import { useSearchParams, useRouter } from "next/navigation"
import { PromptCard } from "@/components/explore/prompt-card"
import { SearchBar, SearchFilters } from "@/components/explore/search-bar"
import { Button } from "@/components/ui/button"
import { Loader2, Sparkles } from "lucide-react"
interface Prompt {
id: string
slug: string
title: string
description: string | null
category: string | null
totalRuns: number
starsCount: number
remixesCount: number
badges: string[]
creator: {
name: string | null
username: string | null
image: string | null
} | null
}
interface ExploreClientProps {
initialPrompts: Prompt[]
}
export function ExploreClient({ initialPrompts }: ExploreClientProps) {
const router = useRouter()
const searchParams = useSearchParams()
const [prompts, setPrompts] = useState<Prompt[]>(initialPrompts)
const [isLoading, setIsLoading] = useState(false)
const [hasMore, setHasMore] = useState(true)
const [offset, setOffset] = useState(initialPrompts.length)
// Fetch prompts with current filters
const fetchPrompts = useCallback(async (query: string, filters: SearchFilters, reset = false) => {
setIsLoading(true)
try {
const params = new URLSearchParams()
if (query) params.set("search", query)
if (filters.category) params.set("category", filters.category)
if (filters.sortBy) params.set("sort", filters.sortBy)
if (filters.dateRange && filters.dateRange !== 'all') params.set("dateRange", filters.dateRange)
params.set("limit", "12")
params.set("offset", reset ? "0" : offset.toString())
const res = await fetch(`/api/prompts?${params.toString()}`)
const data = await res.json()
if (reset) {
setPrompts(data)
setOffset(data.length)
} else {
setPrompts((prev) => {
const existingIds = new Set(prev.map(p => p.id))
const newPrompts = data.filter((p: Prompt) => !existingIds.has(p.id))
return [...prev, ...newPrompts]
})
setOffset((prev) => prev + data.length)
}
setHasMore(data.length === 12)
} catch (error) {
console.error("Failed to fetch prompts:", error)
} finally {
setIsLoading(false)
}
}, [offset])
const handleSearch = (query: string, filters: SearchFilters) => {
// Update URL with search params
const params = new URLSearchParams()
if (query) params.set("q", query)
if (filters.category) params.set("category", filters.category)
if (filters.sortBy && filters.sortBy !== 'trending') params.set("sort", filters.sortBy)
if (filters.dateRange && filters.dateRange !== 'all') params.set("date", filters.dateRange)
const queryString = params.toString()
router.push(queryString ? `/explore?${queryString}` : "/explore", { scroll: false })
// Fetch with new filters
fetchPrompts(query, filters, true)
}
return (
<div className="space-y-8">
{/* Advanced Search Bar */}
<SearchBar
initialQuery={searchParams.get("q") || ""}
onSearch={handleSearch}
/>
{/* Results Count */}
<div className="text-sm text-muted-foreground">
{prompts.length} prompt{prompts.length !== 1 ? "s" : ""} found
</div>
{/* Prompts Grid */}
{prompts.length > 0 ? (
<div className="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{prompts.map((prompt) => (
<PromptCard
key={prompt.id}
slug={prompt.slug}
title={prompt.title}
description={prompt.description}
category={prompt.category}
runs={prompt.totalRuns}
stars={prompt.starsCount}
remixes={prompt.remixesCount}
badges={prompt.badges}
creator={prompt.creator}
/>
))}
</div>
) : (
<div className="text-center py-16 text-muted-foreground">
<Sparkles className="h-12 w-12 mx-auto mb-4 opacity-20" />
<p className="text-lg font-medium mb-2">No prompts found</p>
<p className="text-sm">
Try adjusting your search or filters
</p>
</div>
)}
{/* Load More */}
{hasMore && prompts.length > 0 && (
<div className="flex justify-center pt-4">
<Button
variant="outline"
onClick={() => {
const query = searchParams.get("q") || ""
const filters: SearchFilters = {
category: searchParams.get("category") || undefined,
sortBy: (searchParams.get("sort") as any) || 'trending',
dateRange: (searchParams.get("date") as any) || 'all',
}
fetchPrompts(query, filters, false)
}}
disabled={isLoading}
className="gap-2"
>
{isLoading ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
Loading...
</>
) : (
"Load More"
)}
</Button>
</div>
)}
</div>
)
}
|