@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.
- package/bun.lock +3 -3
- package/dist/bundle.js +120 -55
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -3
- package/dist/schema.d.ts +16 -0
- package/dist/schema.js +4 -2
- package/fake-snippets-api/lib/db/db-client.ts +1 -0
- package/fake-snippets-api/lib/db/schema.ts +2 -0
- package/fake-snippets-api/routes/api/_fake/received_quotes.ts +97 -47
- package/fake-snippets-api/routes/api/snippets/list_trending.ts +22 -5
- package/package.json +2 -2
- package/src/components/PageSearchComponent.tsx +159 -0
- package/src/components/SearchComponent.tsx +1 -1
- package/src/components/ViewPackagePage/components/package-header.tsx +3 -4
- package/src/pages/search.tsx +36 -15
- package/src/pages/trending.tsx +85 -46
- package/src/pages/user-profile.tsx +24 -12
package/src/pages/search.tsx
CHANGED
|
@@ -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
|
-
<
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
</
|
|
43
|
+
</main>
|
|
23
44
|
<Footer />
|
|
24
45
|
</div>
|
|
25
46
|
)
|
package/src/pages/trending.tsx
CHANGED
|
@@ -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 [
|
|
40
|
-
|
|
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 =
|
|
51
|
-
|
|
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
|
|
60
|
-
|
|
83
|
+
const filteredSnippets = snippets
|
|
84
|
+
?.filter((snippet) => {
|
|
85
|
+
if (!searchQuery) return true
|
|
61
86
|
|
|
62
|
-
|
|
87
|
+
const query = searchQuery.toLowerCase().trim()
|
|
63
88
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
89
|
+
const searchableFields = [
|
|
90
|
+
snippet.unscoped_name.toLowerCase(),
|
|
91
|
+
snippet.owner_name.toLowerCase(),
|
|
92
|
+
(snippet.description || "").toLowerCase(),
|
|
93
|
+
]
|
|
69
94
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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-
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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?.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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>
|