@tscircuit/fake-snippets 0.0.47 → 0.0.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,25 +1,46 @@
1
1
  import Header from "@/components/Header"
2
2
  import Footer from "@/components/Footer"
3
+ import PageSearchComponent from "@/components/PageSearchComponent"
4
+ import { useState } from "react"
5
+ import { Search, Tag, Filter } from "lucide-react"
6
+ import { Badge } from "@/components/ui/badge"
7
+ import { PrefetchPageLink } from "@/components/PrefetchPageLink"
3
8
 
4
9
  export const SearchPage = () => {
10
+ const [searchResults, setSearchResults] = useState<any[]>([])
11
+
5
12
  return (
6
- <div>
13
+ <div className="min-h-screen flex flex-col">
7
14
  <Header />
8
- <div className="container mx-auto px-4 py-8">
9
- <h1 className="text-3xl font-bold mb-6">Search Snippets</h1>
10
- {/* Add search functionality and results display here */}
11
- <input
12
- type="text"
13
- placeholder="Search snippets..."
14
- className="w-full p-2 border rounded mb-4"
15
- />
16
- <div>
17
- {/* Search results will be displayed here */}
18
- <p className="text-gray-600">
19
- No results found. Try a different search term.
20
- </p>
15
+ <main className="flex-grow bg-gray-50 pb-12">
16
+ <div className="container mx-auto px-4 py-8">
17
+ <div className="max-w-8xl mx-auto">
18
+ <div className="mb-6">
19
+ <div className="flex items-center gap-2 mb-3">
20
+ <Search className="w-6 h-6 text-blue-500" />
21
+ <h1 className="text-3xl font-bold text-gray-900">
22
+ Search tscircuit Packages
23
+ </h1>
24
+ </div>
25
+ <div className="flex flex-wrap gap-3">
26
+ <PrefetchPageLink href="/trending">
27
+ <Badge
28
+ variant="secondary"
29
+ className="px-3 py-1 cursor-pointer hover:bg-gray-200"
30
+ >
31
+ <Tag className="w-3.5 h-3.5 mr-1" />
32
+ <span>Browse Packages</span>
33
+ </Badge>
34
+ </PrefetchPageLink>
35
+ </div>
36
+ </div>
37
+
38
+ <PageSearchComponent
39
+ onResultsFetched={(results) => setSearchResults(results)}
40
+ />
41
+ </div>
21
42
  </div>
22
- </div>
43
+ </main>
23
44
  <Footer />
24
45
  </div>
25
46
  )
@@ -4,7 +4,7 @@ import { useAxios } from "@/hooks/use-axios"
4
4
  import { Snippet } from "fake-snippets-api/lib/db/schema"
5
5
  import Header from "@/components/Header"
6
6
  import Footer from "@/components/Footer"
7
- import { Link } from "wouter"
7
+ import { Link, useLocation, useSearchParams } from "wouter"
8
8
  import { StarIcon, LockClosedIcon } from "@radix-ui/react-icons"
9
9
  import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
10
10
  import { OptimizedImage } from "@/components/OptimizedImage"
@@ -36,8 +36,27 @@ import { SnippetCard } from "@/components/SnippetCard"
36
36
  const TrendingPage: React.FC = () => {
37
37
  const axios = useAxios()
38
38
  const apiBaseUrl = useSnippetsBaseApiUrl()
39
- const [searchQuery, setSearchQuery] = useState("")
40
- const [category, setCategory] = useState("all")
39
+ const [searchParams, setSearchParams] = useSearchParams()
40
+
41
+ // Initialize state from URL params or defaults
42
+ const [searchQuery, setSearchQuery] = useState(searchParams.get("q") || "")
43
+ const [category, setCategory] = useState(
44
+ searchParams.get("category") || "all",
45
+ )
46
+ const [time_period, setTimePeriod] = useState(
47
+ searchParams.get("time_period") || "all",
48
+ )
49
+ const [sortBy, setSortBy] = useState(searchParams.get("sort") || "stars")
50
+
51
+ // Update URL params when filters change
52
+ useEffect(() => {
53
+ const params = new URLSearchParams()
54
+ if (searchQuery) params.set("q", searchQuery)
55
+ if (category !== "all") params.set("category", category)
56
+ if (time_period !== "all") params.set("time_period", time_period)
57
+ if (sortBy !== "stars") params.set("sort", sortBy)
58
+ setSearchParams(params)
59
+ }, [searchQuery, category, time_period, sortBy, setSearchParams])
41
60
 
42
61
  const {
43
62
  data: snippets,
@@ -45,10 +64,15 @@ const TrendingPage: React.FC = () => {
45
64
  error,
46
65
  refetch,
47
66
  } = useQuery<Snippet[]>(
48
- ["trendingSnippets", category],
67
+ ["trendingSnippets", category, time_period],
49
68
  async () => {
50
- const params = category !== "all" ? { tag: category } : {}
51
- const response = await axios.get("/snippets/list_trending", { params })
69
+ const params = new URLSearchParams()
70
+ if (category !== "all") params.append("tag", category)
71
+ params.append("time_period", time_period)
72
+
73
+ const response = await axios.get(
74
+ `/snippets/list_trending?${params.toString()}`,
75
+ )
52
76
  return response.data.snippets
53
77
  },
54
78
  {
@@ -56,48 +80,65 @@ const TrendingPage: React.FC = () => {
56
80
  },
57
81
  )
58
82
 
59
- const filteredSnippets = snippets?.filter((snippet) => {
60
- if (!searchQuery) return true
83
+ const filteredSnippets = snippets
84
+ ?.filter((snippet) => {
85
+ if (!searchQuery) return true
61
86
 
62
- const query = searchQuery.toLowerCase().trim()
87
+ const query = searchQuery.toLowerCase().trim()
63
88
 
64
- const searchableFields = [
65
- snippet.unscoped_name.toLowerCase(),
66
- snippet.owner_name.toLowerCase(),
67
- (snippet.description || "").toLowerCase(),
68
- ]
89
+ const searchableFields = [
90
+ snippet.unscoped_name.toLowerCase(),
91
+ snippet.owner_name.toLowerCase(),
92
+ (snippet.description || "").toLowerCase(),
93
+ ]
69
94
 
70
- return searchableFields.some((field) => {
71
- const queryWords = query.split(/\s+/).filter((word) => word.length > 0)
72
- return queryWords.every((word) => field.includes(word))
95
+ return searchableFields.some((field) => {
96
+ const queryWords = query.split(/\s+/).filter((word) => word.length > 0)
97
+ return queryWords.every((word) => field.includes(word))
98
+ })
99
+ })
100
+ ?.sort((a, b) => {
101
+ if (sortBy === "stars") {
102
+ return (b.star_count || 0) - (a.star_count || 0)
103
+ }
104
+ return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
73
105
  })
74
- })
75
106
 
76
107
  return (
77
108
  <div className="min-h-screen flex flex-col bg-gray-50">
78
109
  <Header />
79
110
  <main className="flex-grow container mx-auto px-4 py-8">
80
111
  <div className="mb-8 max-w-3xl">
81
- <div className="flex items-center gap-2 mb-3">
82
- <Zap className="w-6 h-6 text-amber-500" />
83
- <h1 className="text-4xl font-bold text-gray-900">
84
- Trending Snippets
85
- </h1>
86
- </div>
112
+ <h1 className="text-4xl font-bold text-gray-900 mb-3">
113
+ Trending Snippets
114
+ </h1>
87
115
  <p className="text-lg text-gray-600 mb-4">
88
- Discover the most popular and innovative snippets from the community
89
- over the last 7 days. These trending designs showcase the best in
90
- circuit creativity and technical excellence.
116
+ Check out some of the top circuit designs from our community.
91
117
  </p>
92
- <div className="flex flex-wrap gap-3">
93
- <Badge variant="secondary" className="px-3 py-1">
94
- <Tag className="w-3.5 h-3.5 mr-1" />
95
- <span>Most Starred</span>
96
- </Badge>
97
- <Badge variant="secondary" className="px-3 py-1">
98
- <Calendar className="w-3.5 h-3.5 mr-1" />
99
- <span>Last 7 Days</span>
100
- </Badge>
118
+ <div className="flex flex-wrap gap-4">
119
+ <Select value={sortBy} onValueChange={setSortBy}>
120
+ <SelectTrigger className="w-[140px]">
121
+ <SelectValue placeholder="Sort By" />
122
+ </SelectTrigger>
123
+ <SelectContent>
124
+ <SelectItem value="stars">Most Starred</SelectItem>
125
+ <SelectItem value="recent">Most Recent</SelectItem>
126
+ </SelectContent>
127
+ </Select>
128
+ <Select
129
+ value={time_period}
130
+ onValueChange={setTimePeriod}
131
+ disabled={sortBy === "recent"}
132
+ >
133
+ <SelectTrigger className="w-[140px]">
134
+ <SelectValue placeholder="Time Period" />
135
+ </SelectTrigger>
136
+ <SelectContent>
137
+ <SelectItem value="7days">Last 7 Days</SelectItem>
138
+ <SelectItem value="30days">Last 30 Days</SelectItem>
139
+ <SelectItem value="all">All Time</SelectItem>
140
+ </SelectContent>
141
+ </Select>
101
142
  </div>
102
143
  </div>
103
144
 
@@ -201,16 +242,14 @@ const TrendingPage: React.FC = () => {
201
242
  </div>
202
243
  ) : (
203
244
  <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
204
- {filteredSnippets
205
- ?.sort((a, b) => b.updated_at.localeCompare(a.updated_at))
206
- ?.map((snippet) => (
207
- <SnippetCard
208
- key={snippet.snippet_id}
209
- snippet={snippet}
210
- baseUrl={apiBaseUrl}
211
- showOwner={true}
212
- />
213
- ))}
245
+ {filteredSnippets?.map((snippet) => (
246
+ <SnippetCard
247
+ key={snippet.snippet_id}
248
+ snippet={snippet}
249
+ baseUrl={apiBaseUrl}
250
+ showOwner={true}
251
+ />
252
+ ))}
214
253
  </div>
215
254
  )}
216
255
  </main>
@@ -22,6 +22,7 @@ import {
22
22
  SelectTrigger,
23
23
  SelectValue,
24
24
  } from "@/components/ui/select"
25
+ import { Star } from "lucide-react"
25
26
 
26
27
  export const UserProfilePage = () => {
27
28
  const { username } = useParams()
@@ -161,18 +162,29 @@ export const UserProfilePage = () => {
161
162
  </div>
162
163
  ) : (
163
164
  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
164
- {filteredSnippets?.map((snippet) => (
165
- <SnippetCard
166
- key={snippet.snippet_id}
167
- snippet={snippet}
168
- baseUrl={baseUrl}
169
- showOwner={activeTab === "starred"}
170
- isCurrentUserSnippet={
171
- isCurrentUserProfile && activeTab === "all"
172
- }
173
- onDeleteClick={handleDeleteClick}
174
- />
175
- ))}
165
+ {filteredSnippets?.length !== 0 ? (
166
+ filteredSnippets?.map((snippet) => (
167
+ <SnippetCard
168
+ key={snippet.snippet_id}
169
+ snippet={snippet}
170
+ baseUrl={baseUrl}
171
+ showOwner={activeTab === "starred"}
172
+ isCurrentUserSnippet={
173
+ isCurrentUserProfile && activeTab === "all"
174
+ }
175
+ onDeleteClick={handleDeleteClick}
176
+ />
177
+ ))
178
+ ) : (
179
+ <div className="col-span-full flex justify-center">
180
+ <div className="flex flex-col items-center py-12 text-gray-500">
181
+ <Star />
182
+ <span className="text-lg font-medium">
183
+ No starred packages
184
+ </span>
185
+ </div>
186
+ </div>
187
+ )}
176
188
  </div>
177
189
  )}
178
190
  </div>